Terrain optimizations
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-11-11 10:23:49 -05:00
parent 4d6db78059
commit 1b5d6c1795
23 changed files with 847 additions and 228 deletions

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Fri Nov 08 17:55:08 EST 2024 #Sun Nov 10 17:05:45 EST 2024
buildNumber=377 buildNumber=378

View File

@ -977,6 +977,17 @@ Memory debugging work + update memory flags in launch file
(11/10/2024) (11/10/2024)
Attempts at optimizing ClientDrawCellManager Attempts at optimizing ClientDrawCellManager
Server-driven homogenous tracking to accelerate client draw cell manager
Face data fill bounds check optimization
Data fill skipping optimization
Homogenous node skipping optimization
Remove evaluation by distance
Squared distance optimization
Distance calculation caching optimization
Two layer destruction optimization
Non-reallocating list iteration for children in draw cell manager optimization
Split leaf/nonleaf tracks for node evaluation optimization
# TODO # TODO

View File

@ -90,6 +90,11 @@
"type" : "BYTE_ARRAY" "type" : "BYTE_ARRAY"
}, },
{
"name" : "homogenousValue",
"type" : "FIXED_INT"
},
{ {
"name" : "chunkResolution", "name" : "chunkResolution",
"type" : "FIXED_INT" "type" : "FIXED_INT"
@ -209,6 +214,7 @@
"worldY", "worldY",
"worldZ", "worldZ",
"chunkResolution", "chunkResolution",
"homogenousValue",
"chunkData" "chunkData"
] ]
}, },

View File

@ -13,7 +13,6 @@ import electrosphere.collision.PhysicsEntityUtils;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.ds.octree.WorldOctTree; import electrosphere.util.ds.octree.WorldOctTree;
import electrosphere.util.ds.octree.WorldOctTree.FloatingChunkTreeNode; import electrosphere.util.ds.octree.WorldOctTree.FloatingChunkTreeNode;
import electrosphere.util.math.GeomUtils; import electrosphere.util.math.GeomUtils;
@ -36,27 +35,27 @@ public class ClientDrawCellManager {
/** /**
* The distance to draw at full resolution * The distance to draw at full resolution
*/ */
public static final double FULL_RES_DIST = 8 * ServerTerrainChunk.CHUNK_DIMENSION; public static final double FULL_RES_DIST = 8 * 8;
/** /**
* The distance for half resolution * The distance for half resolution
*/ */
public static final double HALF_RES_DIST = 16 * ServerTerrainChunk.CHUNK_DIMENSION; public static final double HALF_RES_DIST = 16 * 16;
/** /**
* The distance for quarter resolution * The distance for quarter resolution
*/ */
public static final double QUARTER_RES_DIST = 20 * ServerTerrainChunk.CHUNK_DIMENSION; public static final double QUARTER_RES_DIST = 20 * 20;
/** /**
* The distance for eighth resolution * The distance for eighth resolution
*/ */
public static final double EIGHTH_RES_DIST = 32 * ServerTerrainChunk.CHUNK_DIMENSION; public static final double EIGHTH_RES_DIST = 32 * 32;
/** /**
* The distance for sixteenth resolution * The distance for sixteenth resolution
*/ */
public static final double SIXTEENTH_RES_DIST = 128 * ServerTerrainChunk.CHUNK_DIMENSION; public static final double SIXTEENTH_RES_DIST = 128 * 128;
/** /**
* Lod value for a full res chunk * Lod value for a full res chunk
@ -99,9 +98,9 @@ public class ClientDrawCellManager {
Map<FloatingChunkTreeNode<DrawCell>,Boolean> evaluationMap = new HashMap<FloatingChunkTreeNode<DrawCell>,Boolean>(); Map<FloatingChunkTreeNode<DrawCell>,Boolean> evaluationMap = new HashMap<FloatingChunkTreeNode<DrawCell>,Boolean>();
/** /**
* All draw cells currently tracked * The last recorded player world position
*/ */
List<DrawCell> activeCells = new LinkedList<DrawCell>(); Vector3i lastPlayerPos = new Vector3i();
/** /**
* Tracks whether the cell manager updated last frame or not * Tracks whether the cell manager updated last frame or not
@ -166,6 +165,9 @@ public class ClientDrawCellManager {
Globals.profiler.beginCpuSample("ClientDrawCellManager.update"); Globals.profiler.beginCpuSample("ClientDrawCellManager.update");
if(shouldUpdate && Globals.playerEntity != null){ if(shouldUpdate && Globals.playerEntity != null){
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
Vector3i playerWorldPos = Globals.clientWorldData.convertRealToWorldSpace(playerPos);
int distCache = this.getDistCache(this.lastPlayerPos, playerWorldPos);
this.lastPlayerPos.set(playerWorldPos);
//the sets to iterate through //the sets to iterate through
updatedLastFrame = true; updatedLastFrame = true;
validCellCount = 0; validCellCount = 0;
@ -173,31 +175,31 @@ public class ClientDrawCellManager {
//update all full res cells //update all full res cells
FloatingChunkTreeNode<DrawCell> rootNode = this.chunkTree.getRoot(); FloatingChunkTreeNode<DrawCell> rootNode = this.chunkTree.getRoot();
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - full res cells"); Globals.profiler.beginCpuSample("ClientDrawCellManager.update - full res cells");
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, FULL_RES_LOD); updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, SIXTEENTH_RES_LOD, distCache);
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
if(!updatedLastFrame && !this.initialized){ if(!updatedLastFrame && !this.initialized){
this.initialized = true; this.initialized = true;
} }
if(!updatedLastFrame){ // if(!updatedLastFrame){
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells"); // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, HALF_RES_LOD); // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, HALF_RES_LOD, distCache);
Globals.profiler.endCpuSample(); // Globals.profiler.endCpuSample();
} // }
if(!updatedLastFrame){ // if(!updatedLastFrame){
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells"); // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, QUARTER_RES_LOD); // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, QUARTER_RES_LOD, distCache);
Globals.profiler.endCpuSample(); // Globals.profiler.endCpuSample();
} // }
if(!updatedLastFrame){ // if(!updatedLastFrame){
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells"); // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells");
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, EIGHTH_RES_LOD); // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, EIGHTH_RES_LOD, distCache);
Globals.profiler.endCpuSample(); // Globals.profiler.endCpuSample();
} // }
if(!updatedLastFrame){ // if(!updatedLastFrame){
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells"); // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells");
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, SIXTEENTH_RES_LOD); // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, SIXTEENTH_RES_LOD, distCache);
Globals.profiler.endCpuSample(); // Globals.profiler.endCpuSample();
} // }
// if(!updatedLastFrame){ // if(!updatedLastFrame){
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - all res cells"); // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - all res cells");
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, ALL_RES_LOD); // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, ALL_RES_LOD);
@ -215,102 +217,117 @@ public class ClientDrawCellManager {
* @param evaluationMap Map of leaf nodes that have been evaluated this frame * @param evaluationMap Map of leaf nodes that have been evaluated this frame
* @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, Map<FloatingChunkTreeNode<DrawCell>,Boolean> evaluationMap, int minLeafLod){ private boolean recursivelyUpdateCells(FloatingChunkTreeNode<DrawCell> node, Vector3i playerPos, Map<FloatingChunkTreeNode<DrawCell>,Boolean> evaluationMap, int minLeafLod, int distCache){
Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity);
boolean updated = false; boolean updated = false;
if(evaluationMap.containsKey(node)){ if(evaluationMap.containsKey(node)){
return false; return false;
} }
if(this.shouldSplit(playerPos, node)){ if(node.getData() != null && node.getData().hasGenerated() && node.getData().isHomogenous()){
Globals.profiler.beginCpuSample("ClientDrawCellManager.split"); return false;
//perform op }
FloatingChunkTreeNode<DrawCell> container = chunkTree.split(node); if(node.isLeaf()){
if(this.shouldSplit(playerPos, node, distCache)){
//do deletions Globals.profiler.beginCpuSample("ClientDrawCellManager.split");
this.recursivelyDestroy(node); //perform op
FloatingChunkTreeNode<DrawCell> container = chunkTree.split(node);
//do creations
container.getChildren().forEach(child -> { //do deletions
Vector3i cellWorldPos = new Vector3i( this.twoLayerDestroy(node);
child.getMinBound().x, node.convertToLeaf(null);
child.getMinBound().y,
child.getMinBound().z
); //do creations
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos); container.getChildren().forEach(child -> {
activeCells.add(drawCell); Vector3i cellWorldPos = new Vector3i(
child.convertToLeaf(drawCell); child.getMinBound().x,
evaluationMap.put(child,true); child.getMinBound().y,
}); child.getMinBound().z
);
//update neighbors DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos,this.chunkTree.getMaxLevel() - child.getLevel());
this.conditionalUpdateAdjacentNodes(container, container.getChildren().get(0).getLevel()); child.convertToLeaf(drawCell);
evaluationMap.put(child,true);
Globals.profiler.endCpuSample(); });
updated = true;
} else if(this.shouldJoin(playerPos, node)) { //update neighbors
Globals.profiler.beginCpuSample("ClientDrawCellManager.join"); this.conditionalUpdateAdjacentNodes(container, container.getChildren().get(0).getLevel());
//perform op
FloatingChunkTreeNode<DrawCell> newLeaf = chunkTree.join(node); Globals.profiler.endCpuSample();
updated = true;
//do deletions } else if(shouldRequest(playerPos, node, minLeafLod, distCache)){
this.recursivelyDestroy(node); Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
//do creations //calculate what to request
DrawCell drawCell = DrawCell.generateTerrainCell(node.getMinBound()); DrawCell cell = node.getData();
activeCells.add(drawCell); List<DrawCellFace> highResFaces = null;
newLeaf.convertToLeaf(drawCell); if(shouldSolveFaces(node,playerPos, distCache)){
highResFaces = this.solveHighResFace(node);
//update neighbors
this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel());
evaluationMap.put(newLeaf,true);
Globals.profiler.endCpuSample();
updated = true;
} else if(shouldRequest(playerPos, node, minLeafLod)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
//calculate what to request
DrawCell cell = node.getData();
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
//actually send requests
if(this.requestChunks(node, highResFaces)){
cell.setHasRequested(true);
}
evaluationMap.put(node,true);
Globals.profiler.endCpuSample();
updated = true;
} else if(shouldGenerate(playerPos, node, minLeafLod)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
int lodLevel = this.getLODLevel(playerRealPos, node);
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
if(containsDataToGenerate(node,highResFaces)){
node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces);
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
node.getData().setHasRequested(false);
} }
} else if(node.getData() != null){
node.getData().setFailedGenerationAttempts(node.getData().getFailedGenerationAttempts() + 1); //actually send requests
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){ if(this.requestChunks(node, highResFaces)){
node.getData().setHasRequested(false); cell.setHasRequested(true);
} }
}
evaluationMap.put(node,true);
Globals.profiler.endCpuSample();
updated = true;
} else if(!node.isLeaf()){
this.validCellCount++;
List<FloatingChunkTreeNode<DrawCell>> children = node.getChildren();
for(int i = 0; i < 8; i++){
FloatingChunkTreeNode<DrawCell> child = children.get(i);
boolean childUpdate = recursivelyUpdateCells(child, playerPos, evaluationMap, minLeafLod);
if(childUpdate == true){
updated = true;
}
}
if((this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod){
evaluationMap.put(node,true); evaluationMap.put(node,true);
Globals.profiler.endCpuSample();
updated = true;
} else if(shouldGenerate(playerPos, node, minLeafLod, distCache)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
int lodLevel = this.getLODLevel(node);
//high res faces
List<DrawCellFace> highResFaces = null;
if(shouldSolveFaces(node,playerPos, distCache)){
highResFaces = this.solveHighResFace(node);
}
if(containsDataToGenerate(node,highResFaces)){
node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces);
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
node.getData().setHasRequested(false);
}
} else if(node.getData() != null){
node.getData().setFailedGenerationAttempts(node.getData().getFailedGenerationAttempts() + 1);
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
node.getData().setHasRequested(false);
}
}
evaluationMap.put(node,true);
Globals.profiler.endCpuSample();
updated = true;
}
} else {
if(this.shouldJoin(playerPos, node, distCache)) {
Globals.profiler.beginCpuSample("ClientDrawCellManager.join");
//perform op
FloatingChunkTreeNode<DrawCell> newLeaf = chunkTree.join(node);
//do deletions
this.twoLayerDestroy(node);
//do creations
DrawCell drawCell = DrawCell.generateTerrainCell(node.getMinBound(),this.chunkTree.getMaxLevel() - newLeaf.getLevel());
newLeaf.convertToLeaf(drawCell);
//update neighbors
this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel());
evaluationMap.put(newLeaf,true);
Globals.profiler.endCpuSample();
updated = true;
} else {
this.validCellCount++;
List<FloatingChunkTreeNode<DrawCell>> children = node.getChildren();
for(int i = 0; i < 8; i++){
FloatingChunkTreeNode<DrawCell> child = children.get(i);
boolean childUpdate = recursivelyUpdateCells(child, playerPos, evaluationMap, minLeafLod, distCache);
if(childUpdate == true){
updated = true;
}
}
if((this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod){
evaluationMap.put(node,true);
}
} }
} }
return updated; return updated;
@ -322,10 +339,47 @@ public class ClientDrawCellManager {
* @param node the node * @param node the node
* @return the distance * @return the distance
*/ */
public double getMinDistance(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){ public double getMinDistance(Vector3i worldPos, FloatingChunkTreeNode<DrawCell> node, int distCache){
Vector3i min = node.getMinBound(); if(node.getData() == null){
Vector3i max = node.getMaxBound(); return GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound());
return GeomUtils.getMinDistanceAABB(pos, Globals.clientWorldData.convertWorldToRealSpace(min), Globals.clientWorldData.convertWorldToRealSpace(max)); } else {
return node.getData().getMinDistance(worldPos, node, distCache);
}
}
/**
* Gets the distance cache value
* @param lastPlayerPos The last player world position
* @param currentPlayerPos The current player world position
* @return The distance cache value
*/
private int getDistCache(Vector3i lastPlayerPos, Vector3i currentPlayerPos){
if(
lastPlayerPos.x / 16 != currentPlayerPos.x / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16
){
return SIXTEENTH_RES_LOD;
}
if(
lastPlayerPos.x / 8 != currentPlayerPos.x / 8 || lastPlayerPos.z / 8 != currentPlayerPos.z / 8 || lastPlayerPos.z / 8 != currentPlayerPos.z / 8
){
return EIGHTH_RES_LOD;
}
if(
lastPlayerPos.x / 4 != currentPlayerPos.x / 4 || lastPlayerPos.z / 4 != currentPlayerPos.z / 4 || lastPlayerPos.z / 4 != currentPlayerPos.z / 4
){
return SIXTEENTH_RES_LOD;
}
if(
lastPlayerPos.x / 2 != currentPlayerPos.x / 2 || lastPlayerPos.z / 2 != currentPlayerPos.z / 2 || lastPlayerPos.z / 2 != currentPlayerPos.z / 2
){
return EIGHTH_RES_LOD;
}
if(
lastPlayerPos.x != currentPlayerPos.x || lastPlayerPos.z != currentPlayerPos.z || lastPlayerPos.z != currentPlayerPos.z
){
return QUARTER_RES_LOD;
}
return -1;
} }
/** /**
@ -334,36 +388,38 @@ public class ClientDrawCellManager {
* @param node The node * @param node The node
* @return true if should split, false otherwise * @return true if should split, false otherwise
*/ */
public boolean shouldSplit(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){ public boolean shouldSplit(Vector3i pos, FloatingChunkTreeNode<DrawCell> node, int distCache){
//breaking out into dedicated function so can add case handling ie if we want //breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls //to combine fullres nodes into larger nodes to conserve on draw calls
return return
node.isLeaf() &&
node.canSplit() && node.canSplit() &&
(node.getLevel() != this.chunkTree.getMaxLevel()) &&
(node.getData() == null || !node.getData().hasGenerated() || !node.getData().isHomogenous()) &&
(node.getParent() != null || node == this.chunkTree.getRoot()) &&
( (
( (
node.getLevel() < this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD && node.getLevel() < this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD &&
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST this.getMinDistance(pos, node, distCache) <= SIXTEENTH_RES_DIST
) )
|| ||
( (
node.getLevel() < this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && node.getLevel() < this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD &&
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST this.getMinDistance(pos, node, distCache) <= EIGHTH_RES_DIST
) )
|| ||
( (
node.getLevel() < this.chunkTree.getMaxLevel() - QUARTER_RES_LOD && node.getLevel() < this.chunkTree.getMaxLevel() - QUARTER_RES_LOD &&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST this.getMinDistance(pos, node, distCache) <= QUARTER_RES_DIST
) )
|| ||
( (
node.getLevel() < this.chunkTree.getMaxLevel() - HALF_RES_LOD && node.getLevel() < this.chunkTree.getMaxLevel() - HALF_RES_LOD &&
this.getMinDistance(pos, node) <= HALF_RES_DIST this.getMinDistance(pos, node, distCache) <= HALF_RES_DIST
) )
|| ||
( (
node.getLevel() < this.chunkTree.getMaxLevel() && node.getLevel() < this.chunkTree.getMaxLevel() &&
this.getMinDistance(pos, node) <= FULL_RES_DIST this.getMinDistance(pos, node, distCache) <= FULL_RES_DIST
) )
) )
; ;
@ -371,14 +427,46 @@ public class ClientDrawCellManager {
/** /**
* Gets the LOD level of the draw cell * Gets the LOD level of the draw cell
* @param pos The position of the cell
* @param node The node to consider * @param node The node to consider
* @return -1 if outside of render range, -1 if the node is not a valid draw cell leaf, otherwise returns the LOD level * @return -1 if outside of render range, -1 if the node is not a valid draw cell leaf, otherwise returns the LOD level
*/ */
private int getLODLevel(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){ private int getLODLevel(FloatingChunkTreeNode<DrawCell> node){
return this.chunkTree.getMaxLevel() - node.getLevel(); return this.chunkTree.getMaxLevel() - node.getLevel();
} }
/**
* Tracks whether the high res faces should be solved for or not
* @param node The node
* @param playerWorldPos The player's world position
* @return true if should solve for high res faces, false otherwise
*/
private boolean shouldSolveFaces(FloatingChunkTreeNode<DrawCell> node, Vector3i playerWorldPos, int distCache){
if(node.getLevel() == this.chunkTree.getMaxLevel()){
return false;
}
int lod = this.chunkTree.getMaxLevel() - node.getLevel();
if(lod > SIXTEENTH_RES_LOD){
return false;
}
switch(lod){
case HALF_RES_LOD: {
return this.getMinDistance(playerWorldPos, node, distCache) > HALF_RES_DIST;
}
case QUARTER_RES_LOD: {
return this.getMinDistance(playerWorldPos, node, distCache) > QUARTER_RES_DIST;
}
case EIGHTH_RES_LOD: {
return this.getMinDistance(playerWorldPos, node, distCache) > EIGHTH_RES_DIST;
}
case SIXTEENTH_RES_LOD: {
return this.getMinDistance(playerWorldPos, node, distCache) > SIXTEENTH_RES_DIST;
}
default: {
throw new Error("Unsupported lod: " + lod);
}
}
}
/** /**
* Solves which face (if any) is the high res face for a LOD chunk * Solves which face (if any) is the high res face for a LOD chunk
* @param node The node for the chunk * @param node The node for the chunk
@ -389,6 +477,9 @@ public class ClientDrawCellManager {
if(node.getLevel() == this.chunkTree.getMaxLevel()){ if(node.getLevel() == this.chunkTree.getMaxLevel()){
return null; return null;
} }
if(this.chunkTree.getMaxLevel() - node.getLevel() > SIXTEENTH_RES_LOD){
return null;
}
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1; int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
int spacing = (int)Math.pow(2,lodMultiplitier); int spacing = (int)Math.pow(2,lodMultiplitier);
List<DrawCellFace> faces = new LinkedList<DrawCellFace>(); List<DrawCellFace> faces = new LinkedList<DrawCellFace>();
@ -488,35 +579,35 @@ public class ClientDrawCellManager {
* @param node The node * @param node The node
* @return true if should be joined, false otherwise * @return true if should be joined, false otherwise
*/ */
public boolean shouldJoin(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){ public boolean shouldJoin(Vector3i pos, FloatingChunkTreeNode<DrawCell> node, int distCache){
//breaking out into dedicated function so can add case handling ie if we want //breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls //to combine fullres nodes into larger nodes to conserve on draw calls
return return
node.getLevel() > 0 && node.getLevel() > 0 &&
!node.isLeaf() && (node.getLevel() != this.chunkTree.getMaxLevel()) &&
( (
( (
node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD && node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD &&
this.getMinDistance(pos, node) > FULL_RES_DIST this.getMinDistance(pos, node, distCache) > FULL_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD && node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD &&
this.getMinDistance(pos, node) > HALF_RES_DIST this.getMinDistance(pos, node, distCache) > HALF_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD &&
this.getMinDistance(pos, node) > QUARTER_RES_DIST this.getMinDistance(pos, node, distCache) > QUARTER_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD && node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD &&
this.getMinDistance(pos, node) > EIGHTH_RES_DIST this.getMinDistance(pos, node, distCache) > EIGHTH_RES_DIST
) )
|| ||
( (
this.getMinDistance(pos, node) > SIXTEENTH_RES_DIST this.getMinDistance(pos, node, distCache) > SIXTEENTH_RES_DIST
) )
) )
; ;
@ -529,9 +620,8 @@ public class ClientDrawCellManager {
* @param minLeafLod The minimum LOD required to evaluate a leaf * @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, int minLeafLod){ public boolean shouldRequest(Vector3i pos, FloatingChunkTreeNode<DrawCell> node, int minLeafLod, int distCache){
return return
node.isLeaf() &&
node.getData() != null && node.getData() != null &&
!node.getData().hasRequested() && !node.getData().hasRequested() &&
(this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod && (this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod &&
@ -545,25 +635,25 @@ public class ClientDrawCellManager {
( (
node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD
&& &&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST this.getMinDistance(pos, node, distCache) <= QUARTER_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD
&& &&
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST this.getMinDistance(pos, node, distCache) <= EIGHTH_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD
&& &&
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST this.getMinDistance(pos, node, distCache) <= SIXTEENTH_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD
&& &&
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST this.getMinDistance(pos, node, distCache) <= SIXTEENTH_RES_DIST
) )
) )
; ;
@ -576,9 +666,8 @@ public class ClientDrawCellManager {
* @param minLeafLod The minimum LOD required to evaluate a leaf * @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, int minLeafLod){ public boolean shouldGenerate(Vector3i pos, FloatingChunkTreeNode<DrawCell> node, int minLeafLod, int distCache){
return return
node.isLeaf() &&
node.getData() != null && node.getData() != null &&
!node.getData().hasGenerated() && !node.getData().hasGenerated() &&
(this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod && (this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod &&
@ -592,25 +681,25 @@ public class ClientDrawCellManager {
( (
node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - HALF_RES_LOD
&& &&
this.getMinDistance(pos, node) <= QUARTER_RES_DIST this.getMinDistance(pos, node, distCache) <= QUARTER_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - QUARTER_RES_LOD
&& &&
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST this.getMinDistance(pos, node, distCache) <= EIGHTH_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD
&& &&
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST this.getMinDistance(pos, node, distCache) <= SIXTEENTH_RES_DIST
) )
|| ||
( (
node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD node.getLevel() == this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD
&& &&
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST this.getMinDistance(pos, node, distCache) <= SIXTEENTH_RES_DIST
) )
) )
; ;
@ -644,7 +733,22 @@ public class ClientDrawCellManager {
node.getChildren().forEach(child -> recursivelyDestroy(child)); node.getChildren().forEach(child -> recursivelyDestroy(child));
} }
if(node.getData() != null){ if(node.getData() != null){
activeCells.remove(node.getData()); node.getData().destroy();
}
}
/**
* Destroys two layers of nodes
* @param node The top node
*/
private void twoLayerDestroy(FloatingChunkTreeNode<DrawCell> node){
if(node.getData() == null){
for(FloatingChunkTreeNode<DrawCell> child : node.getChildren()){
if(child.getData() != null){
child.getData().destroy();
}
}
} else {
node.getData().destroy(); node.getData().destroy();
} }
} }

View File

@ -14,6 +14,8 @@ 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.util.ds.octree.WorldOctTree.FloatingChunkTreeNode;
import electrosphere.util.math.GeomUtils;
/** /**
* 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
@ -34,6 +36,12 @@ public class DrawCell {
//the position of the draw cell in world coordinates //the position of the draw cell in world coordinates
Vector3i worldPos; Vector3i worldPos;
/**
* The LOD of the draw cell
*/
int lod;
//the main entity for the cell //the main entity for the cell
Entity modelEntity; Entity modelEntity;
@ -63,10 +71,30 @@ public class DrawCell {
*/ */
boolean multiVoxelType = false; boolean multiVoxelType = false;
/**
* Tracks whether this draw cell is flagged as homogenous from the server or not
*/
boolean homogenous = true;
/** /**
* Number of failed generation attempts * Number of failed generation attempts
*/ */
int failedGenerationAttempts = 0; int failedGenerationAttempts = 0;
/**
* The number of valid fill lookups
*/
int validLookups = 0;
/**
* Labels an invalid distance cache
*/
static final int INVALID_DIST_CACHE = -1;
/**
* The cached minimum distance
*/
double cachedMinDistance = -1;
/** /**
@ -81,9 +109,11 @@ public class DrawCell {
* Constructs a drawcell object * Constructs a drawcell object
*/ */
public static DrawCell generateTerrainCell( public static DrawCell generateTerrainCell(
Vector3i worldPos Vector3i worldPos,
int lod
){ ){
DrawCell rVal = new DrawCell(); DrawCell rVal = new DrawCell();
rVal.lod = lod;
rVal.worldPos = worldPos; rVal.worldPos = worldPos;
return rVal; return rVal;
} }
@ -116,6 +146,8 @@ public class DrawCell {
} }
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas, this.hasPolygons()); modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas, this.hasPolygons());
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos(), new Quaterniond()); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos(), new Quaterniond());
// this.weights = null;
// this.types = null;
this.setHasGenerated(true); this.setHasGenerated(true);
} }
@ -164,10 +196,26 @@ public class DrawCell {
* @return true if the data was successfully filled, false otherwise * @return true if the data was successfully filled, false otherwise
*/ */
private boolean fillInData(int lod){ private boolean fillInData(int lod){
// if(lod == ClientDrawCellManager.FULL_RES_LOD){
ChunkData homogenousLookupChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
worldPos.x,
worldPos.y,
worldPos.z,
lod
);
if(homogenousLookupChunk != null && homogenousLookupChunk.getHomogenousValue() != ChunkData.NOT_HOMOGENOUS){
return true;
}
// }
int spacingFactor = (int)Math.pow(2,lod); int spacingFactor = (int)Math.pow(2,lod);
for(int x = 0; x < ChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){ int i = 0;
for(int x = i / ChunkData.CHUNK_DATA_GENERATOR_SIZE / ChunkData.CHUNK_DATA_GENERATOR_SIZE; x < ChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){
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++){
if(i < validLookups){
i++;
continue;
}
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint( ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
worldPos.x + (x / ChunkData.CHUNK_SIZE) * spacingFactor, worldPos.x + (x / ChunkData.CHUNK_SIZE) * spacingFactor,
worldPos.y + (y / ChunkData.CHUNK_SIZE) * spacingFactor, worldPos.y + (y / ChunkData.CHUNK_SIZE) * spacingFactor,
@ -201,8 +249,14 @@ public class DrawCell {
y % ChunkData.CHUNK_SIZE, y % ChunkData.CHUNK_SIZE,
z % ChunkData.CHUNK_SIZE z % ChunkData.CHUNK_SIZE
); );
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
i++;
validLookups++;
} }
//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){
if(this.initialVoxelType == -1){ if(this.initialVoxelType == -1){
@ -253,6 +307,9 @@ public class DrawCell {
if(currentChunk == null){ if(currentChunk == null){
return false; return false;
} }
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
faceWeights[x][y] = currentChunk.getWeight( faceWeights[x][y] = currentChunk.getWeight(
0, 0,
localCoord1, localCoord1,
@ -276,6 +333,9 @@ public class DrawCell {
if(currentChunk == null){ if(currentChunk == null){
return false; return false;
} }
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
faceWeights[x][y] = currentChunk.getWeight( faceWeights[x][y] = currentChunk.getWeight(
0, 0,
localCoord1, localCoord1,
@ -299,6 +359,9 @@ public class DrawCell {
if(currentChunk == null){ if(currentChunk == null){
return false; return false;
} }
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
faceWeights[x][y] = currentChunk.getWeight( faceWeights[x][y] = currentChunk.getWeight(
localCoord1, localCoord1,
0, 0,
@ -322,6 +385,9 @@ public class DrawCell {
if(currentChunk == null){ if(currentChunk == null){
return false; return false;
} }
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
faceWeights[x][y] = currentChunk.getWeight( faceWeights[x][y] = currentChunk.getWeight(
localCoord1, localCoord1,
0, 0,
@ -345,6 +411,9 @@ public class DrawCell {
if(currentChunk == null){ if(currentChunk == null){
return false; return false;
} }
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
faceWeights[x][y] = currentChunk.getWeight( faceWeights[x][y] = currentChunk.getWeight(
localCoord1, localCoord1,
localCoord2, localCoord2,
@ -368,6 +437,9 @@ public class DrawCell {
if(currentChunk == null){ if(currentChunk == null){
return false; return false;
} }
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
this.homogenous = false;
}
faceWeights[x][y] = currentChunk.getWeight( faceWeights[x][y] = currentChunk.getWeight(
localCoord1, localCoord1,
localCoord2, localCoord2,
@ -483,6 +555,29 @@ public class DrawCell {
this.failedGenerationAttempts = this.failedGenerationAttempts + attempts; this.failedGenerationAttempts = this.failedGenerationAttempts + attempts;
} }
/**
* Gets whether this draw cell is homogenous or not
* @return true if it is homogenous, false otherwise
*/
public boolean isHomogenous(){
return homogenous;
}
/**
* Gets the minimum distance from a node to a point
* @param pos the position to check against
* @param node the node
* @param distCache the lod value under which distance caches are invalidated
* @return the distance
*/
public double getMinDistance(Vector3i worldPos, FloatingChunkTreeNode<DrawCell> node, int distCache){
if(cachedMinDistance != INVALID_DIST_CACHE && distCache < lod){
return cachedMinDistance;
} else {
this.cachedMinDistance = GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound());
return cachedMinDistance;
}
}
} }

View File

@ -233,7 +233,8 @@ public class DrawCellManager {
//build the cell //build the cell
DrawCell cell = DrawCell.generateTerrainCell( DrawCell cell = DrawCell.generateTerrainCell(
worldPos worldPos,
0
); );
cells.add(cell); cells.add(cell);
keyCellMap.put(targetKey,cell); keyCellMap.put(targetKey,cell);

View File

@ -55,7 +55,7 @@ public class ClientTerrainManager {
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
//caches chunks from server //caches chunks from server
static final int CACHE_SIZE = 2500 + (int)(ClientDrawCellManager.FULL_RES_DIST) + (int)(ClientDrawCellManager.HALF_RES_DIST); static final int CACHE_SIZE = 5000 + (int)(ClientDrawCellManager.FULL_RES_DIST * 10) + (int)(ClientDrawCellManager.HALF_RES_DIST * 10);
/** /**
* Size of the cache in bytes * Size of the cache in bytes

View File

@ -56,7 +56,7 @@ public class ImGuiChunkMonitor {
ImGui.text("Chunk Monitor"); ImGui.text("Chunk Monitor");
Globals.clientDrawCellManager.updateStatus(); Globals.clientDrawCellManager.updateStatus();
// ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getFullResCacheSize()); ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getMaxResCount());
// ImGui.text("Garbage queue: " + Globals.clientDrawCellManager.getGarbageSize()); // ImGui.text("Garbage queue: " + Globals.clientDrawCellManager.getGarbageSize());

View File

@ -69,7 +69,7 @@ public class ClientEditorMovementTree implements BehaviorTree {
static final double STATE_DIFFERENCE_CREEP_MULTIPLIER = 0.001; //while the movement tree is idle, slowly creep the position of the entity towards the true server position by this amount static final double STATE_DIFFERENCE_CREEP_MULTIPLIER = 0.001; //while the movement tree is idle, slowly creep the position of the entity towards the true server position by this amount
static final double STATE_DIFFERENCE_CREEP_CUTOFF = 0.01; //the cutoff for creep when we say it's "close enough" static final double STATE_DIFFERENCE_CREEP_CUTOFF = 0.01; //the cutoff for creep when we say it's "close enough"
public static final float EDITOR_MAX_VELOCITY = 1.0f; public static final float EDITOR_MAX_VELOCITY = 0.5f;
public static final float EDITOR_ACCEL = 1.0f; public static final float EDITOR_ACCEL = 1.0f;
String animationStartUp = Animation.ANIMATION_MOVEMENT_STARTUP; String animationStartUp = Animation.ANIMATION_MOVEMENT_STARTUP;

View File

@ -50,6 +50,7 @@ public class TerrainChunk {
TerrainChunkData data; TerrainChunkData data;
try { try {
data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
data.constructBuffers();
if(Globals.clientScene.containsEntity(rVal)){ if(Globals.clientScene.containsEntity(rVal)){
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas); String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas);
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);

View File

@ -1,7 +1,11 @@
package electrosphere.entity.types.terrain; package electrosphere.entity.types.terrain;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.List; import java.util.List;
import org.lwjgl.BufferUtils;
/** /**
* The data required to generate a texture * The data required to generate a texture
*/ */
@ -20,6 +24,17 @@ 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 various buffers of data to send to the gpu
*/
FloatBuffer vertexArrayBufferData = null;
FloatBuffer normalArrayBufferData = null;
FloatBuffer textureArrayBufferData = null;
IntBuffer elementArrayBufferData = null;
FloatBuffer samplerBuffer = null;
FloatBuffer ratioBuffer = null;
/** /**
* The LOD of the model * The LOD of the model
*/ */
@ -44,6 +59,49 @@ public class TerrainChunkData {
this.lod = lod; this.lod = lod;
} }
/**
* Allocates and fills the buffers to send to the gpu
*/
public void constructBuffers(){
int elementCount = this.getFaceElements().size();
vertexArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3);
normalArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3);
textureArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 2);
elementArrayBufferData = BufferUtils.createIntBuffer(elementCount);
int incrementer = 0;
for(int element : this.getFaceElements()){
//for each element, need to push vert, normal, etc
//push current vertex
vertexArrayBufferData.put(this.getVertices().get(element*3+0));
vertexArrayBufferData.put(this.getVertices().get(element*3+1));
vertexArrayBufferData.put(this.getVertices().get(element*3+2));
//push current normals
normalArrayBufferData.put(this.getNormals().get(element*3+0));
normalArrayBufferData.put(this.getNormals().get(element*3+1));
normalArrayBufferData.put(this.getNormals().get(element*3+2));
//push current uvs
textureArrayBufferData.put(this.getUVs().get(element*2+0));
textureArrayBufferData.put(this.getUVs().get(element*2+1));
//push faces -- instead of pushing the element directly, instead use the incrementer because we want to draw a new vertex
//there should be no vertex-sharing between triangles. This keeps the meshes from blurring texture/lighting
elementArrayBufferData.put(incrementer);
incrementer++;
}
int vertexCount = this.getTextureSamplers().size() / 3;
samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3);
for(float samplerVec : this.getTextureSamplers()){
samplerBuffer.put(samplerVec);
}
ratioBuffer = BufferUtils.createFloatBuffer(vertexCount * 3);
for(float ratioVec : this.getTextureRatioVectors()){
ratioBuffer.put(ratioVec);
}
}
/** /**
* Gets the vertex data * Gets the vertex data
* @return the vertex data * @return the vertex data
@ -100,4 +158,29 @@ public class TerrainChunkData {
return lod; return lod;
} }
public FloatBuffer getVertexArrayBufferData() {
return vertexArrayBufferData;
}
public FloatBuffer getNormalArrayBufferData() {
return normalArrayBufferData;
}
public FloatBuffer getTextureArrayBufferData() {
return textureArrayBufferData;
}
public IntBuffer getElementArrayBufferData() {
return elementArrayBufferData;
}
public FloatBuffer getSamplerBuffer() {
return samplerBuffer;
}
public FloatBuffer getRatioBuffer() {
return ratioBuffer;
}
} }

View File

@ -48,6 +48,7 @@ public class TerrainMessage extends NetworkMessage {
double realLocationY; double realLocationY;
double realLocationZ; double realLocationZ;
byte[] chunkData; byte[] chunkData;
int homogenousValue;
int chunkResolution; int chunkResolution;
float terrainWeight; float terrainWeight;
int terrainValue; int terrainValue;
@ -317,6 +318,20 @@ public class TerrainMessage extends NetworkMessage {
this.chunkData = chunkData; this.chunkData = chunkData;
} }
/**
* Gets homogenousValue
*/
public int gethomogenousValue() {
return homogenousValue;
}
/**
* Sets homogenousValue
*/
public void sethomogenousValue(int homogenousValue) {
this.homogenousValue = homogenousValue;
}
/** /**
* Gets chunkResolution * Gets chunkResolution
*/ */
@ -738,17 +753,20 @@ public class TerrainMessage extends NetworkMessage {
if(currentStreamLength < 18){ if(currentStreamLength < 18){
return false; return false;
} }
int chunkDataSize = 0;
if(currentStreamLength < 22){ if(currentStreamLength < 22){
return false; return false;
}
int chunkDataSize = 0;
if(currentStreamLength < 26){
return false;
} else { } else {
temporaryByteQueue.add(byteBuffer.peek(18 + 0)); temporaryByteQueue.add(byteBuffer.peek(22 + 0));
temporaryByteQueue.add(byteBuffer.peek(18 + 1)); temporaryByteQueue.add(byteBuffer.peek(22 + 1));
temporaryByteQueue.add(byteBuffer.peek(18 + 2)); temporaryByteQueue.add(byteBuffer.peek(22 + 2));
temporaryByteQueue.add(byteBuffer.peek(18 + 3)); temporaryByteQueue.add(byteBuffer.peek(22 + 3));
chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
} }
if(currentStreamLength < 22 + chunkDataSize){ if(currentStreamLength < 26 + chunkDataSize){
return false; return false;
} }
return true; return true;
@ -764,6 +782,7 @@ public class TerrainMessage extends NetworkMessage {
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.sethomogenousValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer)); rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
return rVal; return rVal;
} }
@ -771,12 +790,13 @@ public class TerrainMessage extends NetworkMessage {
/** /**
* Constructs a message of type SendReducedChunkData * Constructs a message of type SendReducedChunkData
*/ */
public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,byte[] chunkData){ public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,int homogenousValue,byte[] chunkData){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
rVal.setworldY(worldY); rVal.setworldY(worldY);
rVal.setworldZ(worldZ); rVal.setworldZ(worldZ);
rVal.setchunkResolution(chunkResolution); rVal.setchunkResolution(chunkResolution);
rVal.sethomogenousValue(homogenousValue);
rVal.setchunkData(chunkData); rVal.setchunkData(chunkData);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
@ -1158,7 +1178,7 @@ public class TerrainMessage extends NetworkMessage {
} }
break; break;
case SENDREDUCEDCHUNKDATA: case SENDREDUCEDCHUNKDATA:
rawBytes = new byte[2+4+4+4+4+4+chunkData.length]; rawBytes = new byte[2+4+4+4+4+4+4+chunkData.length];
//message header //message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN; rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
//entity messaage header //entity messaage header
@ -1179,12 +1199,16 @@ public class TerrainMessage extends NetworkMessage {
for(int i = 0; i < 4; i++){ for(int i = 0; i < 4; i++){
rawBytes[14+i] = intValues[i]; rawBytes[14+i] = intValues[i];
} }
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length); intValues = ByteStreamUtils.serializeIntToBytes(homogenousValue);
for(int i = 0; i < 4; i++){ for(int i = 0; i < 4; i++){
rawBytes[18+i] = intValues[i]; rawBytes[18+i] = intValues[i];
} }
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
for(int i = 0; i < 4; i++){
rawBytes[22+i] = intValues[i];
}
for(int i = 0; i < chunkData.length; i++){ for(int i = 0; i < chunkData.length; i++){
rawBytes[22+i] = chunkData[i]; rawBytes[26+i] = chunkData[i];
} }
break; break;
case REQUESTFLUIDDATA: case REQUESTFLUIDDATA:

View File

@ -246,7 +246,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
// System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ); // System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
LoggerInterface.loggerNetworking.DEBUG("(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())); connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructSendReducedChunkDataMessage(worldX, worldY, worldZ, stride, chunk.getHomogenousValue(), buffer.array()));
}; };
//request chunk //request chunk

View File

@ -9,7 +9,6 @@ import java.util.Map;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
@ -785,7 +784,12 @@ public class TerrainChunkModelGeneration {
FloatBuffer vertexArrayBufferData = data.getVertexArrayBufferData();
FloatBuffer normalArrayBufferData = data.getNormalArrayBufferData();
FloatBuffer textureArrayBufferData = data.getTextureArrayBufferData();
IntBuffer elementArrayBufferData = data.getElementArrayBufferData();
FloatBuffer samplerBuffer = data.getSamplerBuffer();
FloatBuffer ratioBuffer = data.getRatioBuffer();
@ -795,33 +799,6 @@ public class TerrainChunkModelGeneration {
// //
int elementCount = data.getFaceElements().size(); int elementCount = data.getFaceElements().size();
try { try {
FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3);
FloatBuffer normalArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3);
FloatBuffer textureArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 2);
IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(elementCount);
int incrementer = 0;
for(int element : data.getFaceElements()){
//for each element, need to push vert, normal, etc
//push current vertex
vertexArrayBufferData.put(data.getVertices().get(element*3+0));
vertexArrayBufferData.put(data.getVertices().get(element*3+1));
vertexArrayBufferData.put(data.getVertices().get(element*3+2));
//push current normals
normalArrayBufferData.put(data.getNormals().get(element*3+0));
normalArrayBufferData.put(data.getNormals().get(element*3+1));
normalArrayBufferData.put(data.getNormals().get(element*3+2));
//push current uvs
textureArrayBufferData.put(data.getUVs().get(element*2+0));
textureArrayBufferData.put(data.getUVs().get(element*2+1));
//push faces -- instead of pushing the element directly, instead use the incrementer because we want to draw a new vertex
//there should be no vertex-sharing between triangles. This keeps the meshes from blurring texture/lighting
elementArrayBufferData.put(incrementer);
incrementer++;
}
//actually buffer vertices //actually buffer vertices
if(vertexArrayBufferData.position() > 0){ if(vertexArrayBufferData.position() > 0){
@ -858,11 +835,6 @@ public class TerrainChunkModelGeneration {
// SAMPLER INDICES // SAMPLER INDICES
// //
try { try {
int vertexCount = data.getTextureSamplers().size() / 3;
FloatBuffer samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3);
for(float samplerVec : data.getTextureSamplers()){
samplerBuffer.put(samplerVec);
}
if(samplerBuffer.position() > 0){ if(samplerBuffer.position() > 0){
samplerBuffer.flip(); samplerBuffer.flip();
mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_INDEX_ATTRIB_LOC); mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_INDEX_ATTRIB_LOC);
@ -875,14 +847,9 @@ public class TerrainChunkModelGeneration {
// SAMPLER RATIO DATA // SAMPLER RATIO DATA
// //
try { try {
int vertexCount = data.getTextureRatioVectors().size() / 3; if(ratioBuffer.position() > 0){
FloatBuffer samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3); ratioBuffer.flip();
for(float samplerVec : data.getTextureRatioVectors()){ mesh.bufferCustomFloatAttribArray(ratioBuffer, 3, SAMPLER_RATIO_ATTRIB_LOC);
samplerBuffer.put(samplerVec);
}
if(samplerBuffer.position() > 0){
samplerBuffer.flip();
mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_RATIO_ATTRIB_LOC);
} }
} catch (NullPointerException ex){ } catch (NullPointerException ex){
ex.printStackTrace(); ex.printStackTrace();
@ -918,7 +885,7 @@ public class TerrainChunkModelGeneration {
*/ */
public static Model generateTerrainModel(TerrainChunkData data, VoxelTextureAtlas atlas){ public static Model generateTerrainModel(TerrainChunkData data, VoxelTextureAtlas atlas){
Model rVal = new Model(); Model rVal = new Model();
Mesh m = generateTerrainMesh(data); Mesh m = TerrainChunkModelGeneration.generateTerrainMesh(data);
//construct the material for the chunk //construct the material for the chunk
Material groundMat = new Material(); Material groundMat = new Material();

View File

@ -19,6 +19,7 @@ import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.server.ServerPlayerViewDirTree; import electrosphere.entity.state.server.ServerPlayerViewDirTree;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.server.world.ServerWorldData; import electrosphere.game.server.world.ServerWorldData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.EntityMessage;
@ -401,12 +402,16 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//clear all entities in cell //clear all entities in cell
for(Entity entity : cell.getScene().getEntityList()){ for(Entity entity : cell.getScene().getEntityList()){
if(ServerPlayerViewDirTree.hasTree(entity)){ if(ServerPlayerViewDirTree.hasTree(entity)){
int playerId = CreatureUtils.getControllerPlayerId(entity);
Player player = Globals.playerManager.getPlayerFromId(playerId);
throw new Error( throw new Error(
"Trying to unload a player's entity! " + "Trying to unload a player's entity! " +
entity + "\n" + "entity: " + entity + "\n" +
EntityUtils.getPosition(entity) + "\n" + "entity pos (real): " + EntityUtils.getPosition(entity) + "\n" +
serverWorldData.convertRealToWorldSpace(EntityUtils.getPosition(entity)) + "\n" + "entity pos (world): " + serverWorldData.convertRealToWorldSpace(EntityUtils.getPosition(entity)) + "\n" +
worldPos "chunk pos (world): " + worldPos + "\n" +
"player pos (world): " + player.getWorldPos() + "\n" +
"Number of players in cell: " + cell.getPlayers().size()
); );
} }
ServerEntityUtils.destroyEntity(entity); ServerEntityUtils.destroyEntity(entity);

View File

@ -11,6 +11,7 @@ import java.util.concurrent.Semaphore;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterOutputStream; import java.util.zip.InflaterOutputStream;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
@ -153,14 +154,21 @@ public class ChunkDiskMap {
} }
IntBuffer intView = buffer.asIntBuffer(); IntBuffer intView = buffer.asIntBuffer();
intView.position(DIM * DIM * DIM); intView.position(DIM * DIM * DIM);
int firstType = -1;
boolean homogenous = true;
for(int x = 0; x < DIM; x++){ for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){ for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){ for(int z = 0; z < DIM; z++){
values[x][y][z] = intView.get(); values[x][y][z] = intView.get();
if(firstType == -1){
firstType = values[x][y][z];
} else if(homogenous && firstType == values[x][y][z]){
homogenous = false;
}
} }
} }
} }
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); rVal = new ServerTerrainChunk(worldX, worldY, worldZ, homogenous ? firstType : ChunkData.NOT_HOMOGENOUS, weights, values);
} }
} }
lock.release(); lock.release();

View File

@ -1,5 +1,6 @@
package electrosphere.server.terrain.generation; package electrosphere.server.terrain.generation;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.entity.scene.RealmDescriptor; import electrosphere.entity.scene.RealmDescriptor;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
@ -43,7 +44,7 @@ public class DefaultChunkGenerator implements ChunkGenerator {
} }
} }
} }
ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ, ChunkData.NOT_HOMOGENOUS, weights, values);
return rVal; return rVal;
} }

View File

@ -3,6 +3,7 @@ package electrosphere.server.terrain.generation;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.game.terrain.processing.TerrainInterpolator; import electrosphere.game.terrain.processing.TerrainInterpolator;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
@ -52,7 +53,7 @@ public class OverworldChunkGenerator implements ChunkGenerator {
} }
} }
} }
returnedChunk = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); returnedChunk = new ServerTerrainChunk(worldX, worldY, worldZ, ChunkData.NOT_HOMOGENOUS, weights, values);
return returnedChunk; return returnedChunk;
} }

View File

@ -3,6 +3,8 @@ package electrosphere.server.terrain.generation;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import electrosphere.client.terrain.cache.ChunkData;
// import org.graalvm.polyglot.Value; // import org.graalvm.polyglot.Value;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
@ -94,6 +96,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
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];;
int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
int firstType = -1;
boolean homogenous = true;
try { try {
//actual generation algo //actual generation algo
@ -176,6 +180,11 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
weights[x][y][z] = voxel.weight; weights[x][y][z] = voxel.weight;
values[x][y][z] = voxel.type; values[x][y][z] = voxel.type;
} }
if(firstType == -1){
firstType = values[x][y][z];
} else if(homogenous && firstType == values[x][y][z]){
homogenous = false;
}
} }
} }
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
@ -184,7 +193,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
} catch(Exception ex){ } catch(Exception ex){
ex.printStackTrace(); ex.printStackTrace();
} }
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); rVal = new ServerTerrainChunk(worldX, worldY, worldZ, homogenous ? firstType : ChunkData.NOT_HOMOGENOUS, weights, values);
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
return rVal; return rVal;
} }

View File

@ -52,15 +52,21 @@ public class ServerTerrainChunk {
*/ */
int[][][] values; int[][][] values;
/**
* The homogenous value of this data, or ChunkData.NOT_HOMOGENOUS if it is not homogenous
*/
int homogenousValue;
/** /**
* Constructor * Constructor
* @param worldX The world position x coordinate * @param worldX The world position x coordinate
* @param worldY The world position y coordinate * @param worldY The world position y coordinate
* @param worldZ The world position z coordinate * @param worldZ The world position z coordinate
* @param homogenousValue The homogenous value of the terrain chunk
* @param weights The weights of the chunk * @param weights The weights of the chunk
* @param values The values of the chunk * @param values The values of the chunk
*/ */
public ServerTerrainChunk(int worldX, int worldY, int worldZ, float[][][] weights, int[][][] values) { public ServerTerrainChunk(int worldX, int worldY, int worldZ, int homogenousValue, float[][][] weights, int[][][] values) {
this.worldX = worldX; this.worldX = worldX;
this.worldY = worldY; this.worldY = worldY;
this.worldZ = worldZ; this.worldZ = worldZ;
@ -182,5 +188,12 @@ public class ServerTerrainChunk {
return values[x][y][z]; return values[x][y][z];
} }
/**
* Gets the homogenous value of the chunk
* @return The homogenous value of this data, or ChunkData.NOT_HOMOGENOUS if it is not homogenous
*/
public int getHomogenousValue(){
return homogenousValue;
}
} }

View File

@ -136,7 +136,7 @@ public class WorldOctTree <T> {
FloatingChunkTreeNode<T> newContainer = new FloatingChunkTreeNode<>(this, currentLevel, min, max); FloatingChunkTreeNode<T> newContainer = new FloatingChunkTreeNode<>(this, currentLevel, min, max);
//replace existing node //replace existing node
replaceNode(existing,newContainer); this.replaceNode(existing,newContainer);
//update tracking //update tracking
this.nodes.remove(existing); this.nodes.remove(existing);
@ -156,8 +156,9 @@ public class WorldOctTree <T> {
this.root = newNode; this.root = newNode;
} else { } else {
FloatingChunkTreeNode<T> parent = existing.getParent(); FloatingChunkTreeNode<T> parent = existing.getParent();
int index = parent.children.indexOf(existing);
parent.removeChild(existing); parent.removeChild(existing);
parent.addChild(newNode); parent.addChild(index, newNode);
} }
} }
@ -422,6 +423,16 @@ public class WorldOctTree <T> {
child.parent = this; child.parent = this;
} }
/**
* Adds a child to this node
* @param index The index of the child
* @param child The child
*/
private void addChild(int index, FloatingChunkTreeNode<T> child){
this.children.add(index, child);
child.parent = this;
}
/** /**
* Removes a child node * Removes a child node
* @param child the child * @param child the child

View File

@ -1,6 +1,7 @@
package electrosphere.util.math; package electrosphere.util.math;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3i;
/** /**
* Utilities for dealing with geometry * Utilities for dealing with geometry
@ -101,5 +102,284 @@ public class GeomUtils {
} }
} }
} }
/**
* Gets the minimum squared distance from a point to an axis aligned cube
* @param pos the position to check against
* @param cubeMin The min position of the cube
* @param cubeMax The max position of the cube
* @return the distance
*/
public static double getMinSquaredDistanceAABB(Vector3d pos, Vector3d cubeMin, Vector3d cubeMax){
double minX = cubeMin.x;
double minY = cubeMin.y;
double minZ = cubeMin.z;
double maxX = cubeMax.x;
double maxY = cubeMax.y;
double maxZ = cubeMax.z;
if(pos.x > minX && pos.x < maxX){
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return 0;
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(pos.x,pos.y,minZ);
} else {
return pos.distanceSquared(pos.x,pos.y,maxZ);
}
} else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(pos.x,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(pos.x,minY,minZ);
} else {
return pos.distanceSquared(pos.x,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(pos.x,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(pos.x,maxY,minZ);
} else {
return pos.distanceSquared(pos.x,maxY,maxZ);
}
}
} else if(Math.abs(pos.x - minX) < Math.abs(pos.x - maxX)){
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(minX,pos.y,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(minX,pos.y,minZ);
} else {
return pos.distanceSquared(minX,pos.y,maxZ);
}
} else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(minX,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(minX,minY,minZ);
} else {
return pos.distanceSquared(minX,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(minX,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(minX,maxY,minZ);
} else {
return pos.distanceSquared(minX,maxY,maxZ);
}
}
} else {
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(maxX,pos.y,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(maxX,pos.y,minZ);
} else {
return pos.distanceSquared(maxX,pos.y,maxZ);
}
} else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(maxX,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(maxX,minY,minZ);
} else {
return pos.distanceSquared(maxX,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(maxX,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(maxX,maxY,minZ);
} else {
return pos.distanceSquared(maxX,maxY,maxZ);
}
}
}
}
/**
* Gets the minimum squared distance from a point to an axis aligned cube
* @param pos the position to check against
* @param cubeMin The min position of the cube
* @param cubeMax The max position of the cube
* @return the distance
*/
public static double getMinSquaredDistanceAABB(Vector3i pos, Vector3i cubeMin, Vector3i cubeMax){
int minX = cubeMin.x;
int minY = cubeMin.y;
int minZ = cubeMin.z;
int maxX = cubeMax.x;
int maxY = cubeMax.y;
int maxZ = cubeMax.z;
if(pos.x > minX && pos.x < maxX){
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return 0;
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(pos.x,pos.y,minZ);
} else {
return pos.distanceSquared(pos.x,pos.y,maxZ);
}
} else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(pos.x,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(pos.x,minY,minZ);
} else {
return pos.distanceSquared(pos.x,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(pos.x,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(pos.x,maxY,minZ);
} else {
return pos.distanceSquared(pos.x,maxY,maxZ);
}
}
} else if(Math.abs(pos.x - minX) < Math.abs(pos.x - maxX)){
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(minX,pos.y,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(minX,pos.y,minZ);
} else {
return pos.distanceSquared(minX,pos.y,maxZ);
}
} else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(minX,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(minX,minY,minZ);
} else {
return pos.distanceSquared(minX,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(minX,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(minX,maxY,minZ);
} else {
return pos.distanceSquared(minX,maxY,maxZ);
}
}
} else {
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(maxX,pos.y,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(maxX,pos.y,minZ);
} else {
return pos.distanceSquared(maxX,pos.y,maxZ);
}
} else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(maxX,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(maxX,minY,minZ);
} else {
return pos.distanceSquared(maxX,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distanceSquared(maxX,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distanceSquared(maxX,maxY,minZ);
} else {
return pos.distanceSquared(maxX,maxY,maxZ);
}
}
}
}
/**
* Gets the minimum squared distance from a point to an axis aligned cube
* @param pos the position to check against
* @param cubeMin The min position of the cube
* @param cubeMax The max position of the cube
* @return the distance
*/
public static double getMinSquaredDistanceAABBUnrolled(int posX, int posY, int posZ, int minX, int minY, int minZ, int maxX, int maxY, int maxZ){
if(posX > minX && posX < maxX){
if(posY > minY && posY < maxY){
if(posZ > minZ && posZ < maxZ){
return 0;
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posZ - minZ) * (posZ - minZ);// pos.distanceSquared(posX,posY,minZ);
} else {
return (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(posX,posY,maxZ);
}
} else if(Math.abs(posY - minY) < Math.abs(posY - maxY)){
if(posZ > minZ && posZ < maxZ){
return (posY - minY) * (posY - minY);//pos.distanceSquared(posX,minY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posY - minY) * (posY - minY) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(posX,minY,minZ);
} else {
return (posY - minY) * (posY - minY) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(posX,minY,maxZ);
}
} else {
if(posZ > minZ && posZ < maxZ){
return (posY - maxY) * (posY - maxY);//pos.distanceSquared(posX,maxY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posY - maxY) * (posY - maxY) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(posX,maxY,minZ);
} else {
return (posY - maxY) * (posY - maxY) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(posX,maxY,maxZ);
}
}
} else if(Math.abs(posX - minX) < Math.abs(posX - maxX)){
if(posY > minY && posY < maxY){
if(posZ > minZ && posZ < maxZ){
return (posX - minX) * (posX - minX);//pos.distanceSquared(minX,posY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posX - minX) * (posX - minX) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(minX,posY,minZ);
} else {
return (posX - minX) * (posX - minX) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(minX,posY,maxZ);
}
} else if(Math.abs(posY - minY) < Math.abs(posY - maxY)){
if(posZ > minZ && posZ < maxZ){
return (posX - minX) * (posX - minX);//pos.distanceSquared(minX,minY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posX - minX) * (posX - minX) + (posY - minY) * (posY - minY) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(minX,minY,minZ);
} else {
return (posX - minX) * (posX - minX) + (posY - minY) * (posY - minY) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(minX,minY,maxZ);
}
} else {
if(posZ > minZ && posZ < maxZ){
return (posX - minX) * (posX - minX) + (posY - maxY) * (posY - maxY);//pos.distanceSquared(minX,maxY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posX - minX) * (posX - minX) + (posY - maxY) * (posY - maxY) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(minX,maxY,minZ);
} else {
return (posX - minX) * (posX - minX) + (posY - maxY) * (posY - maxY) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(minX,maxY,maxZ);
}
}
} else {
if(posY > minY && posY < maxY){
if(posZ > minZ && posZ < maxZ){
return (posX - maxX) * (posX - maxX);//pos.distanceSquared(maxX,posY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posX - maxX) * (posX - maxX) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(maxX,posY,minZ);
} else {
return (posX - maxX) * (posX - maxX) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(maxX,posY,maxZ);
}
} else if(Math.abs(posY - minY) < Math.abs(posY - maxY)){
if(posZ > minZ && posZ < maxZ){
return (posX - maxX) * (posX - maxX);//pos.distanceSquared(maxX,minY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posX - maxX) * (posX - maxX) + (posY - minY) * (posY - minY) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(maxX,minY,minZ);
} else {
return (posX - maxX) * (posX - maxX) + (posY - minY) * (posY - minY) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(maxX,minY,maxZ);
}
} else {
if(posZ > minZ && posZ < maxZ){
return (posX - maxX) * (posX - maxX) + (posY - maxY) * (posY - maxY);//pos.distanceSquared(maxX,maxY,posZ);
} else if(Math.abs(posZ - minZ) < Math.abs(posZ - maxZ)){
return (posX - maxX) * (posX - maxX) + (posY - maxY) * (posY - maxY) + (posZ - minZ) * (posZ - minZ);//pos.distanceSquared(maxX,maxY,minZ);
} else {
return (posX - maxX) * (posX - maxX) + (posY - maxY) * (posY - maxY) + (posZ - maxZ) * (posZ - maxZ);//pos.distanceSquared(maxX,maxY,maxZ);
}
}
}
}
} }

View File

@ -2,7 +2,6 @@ package electrosphere.client.terrain.cells;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector3i; import org.joml.Vector3i;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -39,11 +38,11 @@ public class ClientDrawCellManagerTests {
Globals.clientWorldData = new ClientWorldData(new Vector3f(0), new Vector3f(worldDiscreteSize * ServerTerrainChunk.CHUNK_DIMENSION), 0, worldDiscreteSize); Globals.clientWorldData = new ClientWorldData(new Vector3f(0), new Vector3f(worldDiscreteSize * ServerTerrainChunk.CHUNK_DIMENSION), 0, worldDiscreteSize);
ClientDrawCellManager manager = new ClientDrawCellManager(null, 64); ClientDrawCellManager manager = new ClientDrawCellManager(null, 64);
double precomputedMidDist = 0; double precomputedMidDist = 0;
Vector3d playerPos = new Vector3d(0,0,0); Vector3i playerPos = new Vector3i(0,0,0);
FloatingChunkTreeNode<DrawCell> node = FloatingChunkTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16)); FloatingChunkTreeNode<DrawCell> node = FloatingChunkTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16));
node.convertToLeaf(DrawCell.generateTerrainCell(new Vector3i(0,0,0))); node.convertToLeaf(DrawCell.generateTerrainCell(new Vector3i(0,0,0),3));
assertTrue(manager.shouldSplit(playerPos, node)); assertTrue(manager.shouldSplit(playerPos, node, 0));
//cleanup //cleanup
Main.shutdown(); Main.shutdown();