Terrain optimizations
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
This commit is contained in:
parent
4d6db78059
commit
1b5d6c1795
@ -1,3 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Fri Nov 08 17:55:08 EST 2024
|
||||
buildNumber=377
|
||||
#Sun Nov 10 17:05:45 EST 2024
|
||||
buildNumber=378
|
||||
|
||||
@ -977,6 +977,17 @@ Memory debugging work + update memory flags in launch file
|
||||
|
||||
(11/10/2024)
|
||||
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
|
||||
|
||||
@ -90,6 +90,11 @@
|
||||
"type" : "BYTE_ARRAY"
|
||||
},
|
||||
|
||||
{
|
||||
"name" : "homogenousValue",
|
||||
"type" : "FIXED_INT"
|
||||
},
|
||||
|
||||
{
|
||||
"name" : "chunkResolution",
|
||||
"type" : "FIXED_INT"
|
||||
@ -209,6 +214,7 @@
|
||||
"worldY",
|
||||
"worldZ",
|
||||
"chunkResolution",
|
||||
"homogenousValue",
|
||||
"chunkData"
|
||||
]
|
||||
},
|
||||
|
||||
@ -13,7 +13,6 @@ import electrosphere.collision.PhysicsEntityUtils;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.util.ds.octree.WorldOctTree;
|
||||
import electrosphere.util.ds.octree.WorldOctTree.FloatingChunkTreeNode;
|
||||
import electrosphere.util.math.GeomUtils;
|
||||
@ -36,27 +35,27 @@ public class ClientDrawCellManager {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
@ -99,9 +98,9 @@ public class ClientDrawCellManager {
|
||||
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
|
||||
@ -166,6 +165,9 @@ public class ClientDrawCellManager {
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.update");
|
||||
if(shouldUpdate && Globals.playerEntity != null){
|
||||
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
|
||||
updatedLastFrame = true;
|
||||
validCellCount = 0;
|
||||
@ -173,31 +175,31 @@ public class ClientDrawCellManager {
|
||||
//update all full res cells
|
||||
FloatingChunkTreeNode<DrawCell> rootNode = this.chunkTree.getRoot();
|
||||
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();
|
||||
if(!updatedLastFrame && !this.initialized){
|
||||
this.initialized = true;
|
||||
}
|
||||
if(!updatedLastFrame){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
|
||||
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, HALF_RES_LOD);
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
if(!updatedLastFrame){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
|
||||
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, QUARTER_RES_LOD);
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
if(!updatedLastFrame){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells");
|
||||
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, EIGHTH_RES_LOD);
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
if(!updatedLastFrame){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells");
|
||||
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, SIXTEENTH_RES_LOD);
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
// if(!updatedLastFrame){
|
||||
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
|
||||
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, HALF_RES_LOD, distCache);
|
||||
// Globals.profiler.endCpuSample();
|
||||
// }
|
||||
// if(!updatedLastFrame){
|
||||
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
|
||||
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, QUARTER_RES_LOD, distCache);
|
||||
// Globals.profiler.endCpuSample();
|
||||
// }
|
||||
// if(!updatedLastFrame){
|
||||
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells");
|
||||
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, EIGHTH_RES_LOD, distCache);
|
||||
// Globals.profiler.endCpuSample();
|
||||
// }
|
||||
// if(!updatedLastFrame){
|
||||
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells");
|
||||
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, SIXTEENTH_RES_LOD, distCache);
|
||||
// Globals.profiler.endCpuSample();
|
||||
// }
|
||||
// if(!updatedLastFrame){
|
||||
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - all res cells");
|
||||
// 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
|
||||
* @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){
|
||||
Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
private boolean recursivelyUpdateCells(FloatingChunkTreeNode<DrawCell> node, Vector3i playerPos, Map<FloatingChunkTreeNode<DrawCell>,Boolean> evaluationMap, int minLeafLod, int distCache){
|
||||
boolean updated = false;
|
||||
if(evaluationMap.containsKey(node)){
|
||||
return false;
|
||||
}
|
||||
if(this.shouldSplit(playerPos, node)){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.split");
|
||||
//perform op
|
||||
FloatingChunkTreeNode<DrawCell> container = chunkTree.split(node);
|
||||
|
||||
//do deletions
|
||||
this.recursivelyDestroy(node);
|
||||
|
||||
//do creations
|
||||
container.getChildren().forEach(child -> {
|
||||
Vector3i cellWorldPos = new Vector3i(
|
||||
child.getMinBound().x,
|
||||
child.getMinBound().y,
|
||||
child.getMinBound().z
|
||||
);
|
||||
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos);
|
||||
activeCells.add(drawCell);
|
||||
child.convertToLeaf(drawCell);
|
||||
evaluationMap.put(child,true);
|
||||
});
|
||||
|
||||
//update neighbors
|
||||
this.conditionalUpdateAdjacentNodes(container, container.getChildren().get(0).getLevel());
|
||||
|
||||
Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(this.shouldJoin(playerPos, node)) {
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.join");
|
||||
//perform op
|
||||
FloatingChunkTreeNode<DrawCell> newLeaf = chunkTree.join(node);
|
||||
|
||||
//do deletions
|
||||
this.recursivelyDestroy(node);
|
||||
|
||||
//do creations
|
||||
DrawCell drawCell = DrawCell.generateTerrainCell(node.getMinBound());
|
||||
activeCells.add(drawCell);
|
||||
newLeaf.convertToLeaf(drawCell);
|
||||
|
||||
//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);
|
||||
if(node.getData() != null && node.getData().hasGenerated() && node.getData().isHomogenous()){
|
||||
return false;
|
||||
}
|
||||
if(node.isLeaf()){
|
||||
if(this.shouldSplit(playerPos, node, distCache)){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.split");
|
||||
//perform op
|
||||
FloatingChunkTreeNode<DrawCell> container = chunkTree.split(node);
|
||||
|
||||
//do deletions
|
||||
this.twoLayerDestroy(node);
|
||||
node.convertToLeaf(null);
|
||||
|
||||
|
||||
//do creations
|
||||
container.getChildren().forEach(child -> {
|
||||
Vector3i cellWorldPos = new Vector3i(
|
||||
child.getMinBound().x,
|
||||
child.getMinBound().y,
|
||||
child.getMinBound().z
|
||||
);
|
||||
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos,this.chunkTree.getMaxLevel() - child.getLevel());
|
||||
child.convertToLeaf(drawCell);
|
||||
evaluationMap.put(child,true);
|
||||
});
|
||||
|
||||
//update neighbors
|
||||
this.conditionalUpdateAdjacentNodes(container, container.getChildren().get(0).getLevel());
|
||||
|
||||
Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(shouldRequest(playerPos, node, minLeafLod, distCache)){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
|
||||
|
||||
//calculate what to request
|
||||
DrawCell cell = node.getData();
|
||||
List<DrawCellFace> highResFaces = null;
|
||||
if(shouldSolveFaces(node,playerPos, distCache)){
|
||||
highResFaces = this.solveHighResFace(node);
|
||||
}
|
||||
} else if(node.getData() != null){
|
||||
node.getData().setFailedGenerationAttempts(node.getData().getFailedGenerationAttempts() + 1);
|
||||
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
|
||||
node.getData().setHasRequested(false);
|
||||
|
||||
//actually send requests
|
||||
if(this.requestChunks(node, highResFaces)){
|
||||
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);
|
||||
|
||||
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;
|
||||
@ -322,10 +339,47 @@ public class ClientDrawCellManager {
|
||||
* @param node the node
|
||||
* @return the distance
|
||||
*/
|
||||
public double getMinDistance(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
|
||||
Vector3i min = node.getMinBound();
|
||||
Vector3i max = node.getMaxBound();
|
||||
return GeomUtils.getMinDistanceAABB(pos, Globals.clientWorldData.convertWorldToRealSpace(min), Globals.clientWorldData.convertWorldToRealSpace(max));
|
||||
public double getMinDistance(Vector3i worldPos, FloatingChunkTreeNode<DrawCell> node, int distCache){
|
||||
if(node.getData() == null){
|
||||
return GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound());
|
||||
} 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
|
||||
* @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
|
||||
//to combine fullres nodes into larger nodes to conserve on draw calls
|
||||
return
|
||||
node.isLeaf() &&
|
||||
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 &&
|
||||
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST
|
||||
this.getMinDistance(pos, node, distCache) <= SIXTEENTH_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
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 &&
|
||||
this.getMinDistance(pos, node) <= QUARTER_RES_DIST
|
||||
this.getMinDistance(pos, node, distCache) <= QUARTER_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
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() &&
|
||||
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
|
||||
* @param pos The position of the cell
|
||||
* @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
|
||||
*/
|
||||
private int getLODLevel(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
|
||||
private int getLODLevel(FloatingChunkTreeNode<DrawCell> node){
|
||||
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
|
||||
* @param node The node for the chunk
|
||||
@ -389,6 +477,9 @@ public class ClientDrawCellManager {
|
||||
if(node.getLevel() == this.chunkTree.getMaxLevel()){
|
||||
return null;
|
||||
}
|
||||
if(this.chunkTree.getMaxLevel() - node.getLevel() > SIXTEENTH_RES_LOD){
|
||||
return null;
|
||||
}
|
||||
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
|
||||
int spacing = (int)Math.pow(2,lodMultiplitier);
|
||||
List<DrawCellFace> faces = new LinkedList<DrawCellFace>();
|
||||
@ -488,35 +579,35 @@ public class ClientDrawCellManager {
|
||||
* @param node The node
|
||||
* @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
|
||||
//to combine fullres nodes into larger nodes to conserve on draw calls
|
||||
return
|
||||
node.getLevel() > 0 &&
|
||||
!node.isLeaf() &&
|
||||
(node.getLevel() != this.chunkTree.getMaxLevel()) &&
|
||||
(
|
||||
(
|
||||
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 &&
|
||||
this.getMinDistance(pos, node) > HALF_RES_DIST
|
||||
this.getMinDistance(pos, node, distCache) > HALF_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
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 &&
|
||||
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
|
||||
* @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
|
||||
node.isLeaf() &&
|
||||
node.getData() != null &&
|
||||
!node.getData().hasRequested() &&
|
||||
(this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod &&
|
||||
@ -545,25 +635,25 @@ public class ClientDrawCellManager {
|
||||
(
|
||||
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
|
||||
&&
|
||||
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST
|
||||
this.getMinDistance(pos, node, distCache) <= EIGHTH_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
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
|
||||
&&
|
||||
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
|
||||
* @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
|
||||
node.isLeaf() &&
|
||||
node.getData() != null &&
|
||||
!node.getData().hasGenerated() &&
|
||||
(this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod &&
|
||||
@ -592,25 +681,25 @@ public class ClientDrawCellManager {
|
||||
(
|
||||
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
|
||||
&&
|
||||
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST
|
||||
this.getMinDistance(pos, node, distCache) <= EIGHTH_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
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
|
||||
&&
|
||||
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));
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.types.terrain.TerrainChunk;
|
||||
import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
|
||||
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
|
||||
@ -34,6 +36,12 @@ public class DrawCell {
|
||||
|
||||
//the position of the draw cell in world coordinates
|
||||
Vector3i worldPos;
|
||||
|
||||
|
||||
/**
|
||||
* The LOD of the draw cell
|
||||
*/
|
||||
int lod;
|
||||
|
||||
//the main entity for the cell
|
||||
Entity modelEntity;
|
||||
@ -63,10 +71,30 @@ public class DrawCell {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public static DrawCell generateTerrainCell(
|
||||
Vector3i worldPos
|
||||
Vector3i worldPos,
|
||||
int lod
|
||||
){
|
||||
DrawCell rVal = new DrawCell();
|
||||
rVal.lod = lod;
|
||||
rVal.worldPos = worldPos;
|
||||
return rVal;
|
||||
}
|
||||
@ -116,6 +146,8 @@ public class DrawCell {
|
||||
}
|
||||
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas, this.hasPolygons());
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos(), new Quaterniond());
|
||||
// this.weights = null;
|
||||
// this.types = null;
|
||||
this.setHasGenerated(true);
|
||||
}
|
||||
|
||||
@ -164,10 +196,26 @@ public class DrawCell {
|
||||
* @return true if the data was successfully filled, false otherwise
|
||||
*/
|
||||
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);
|
||||
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 z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
|
||||
if(i < validLookups){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||
worldPos.x + (x / ChunkData.CHUNK_SIZE) * spacingFactor,
|
||||
worldPos.y + (y / ChunkData.CHUNK_SIZE) * spacingFactor,
|
||||
@ -201,8 +249,14 @@ public class DrawCell {
|
||||
y % 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
|
||||
if(!this.multiVoxelType){
|
||||
if(this.initialVoxelType == -1){
|
||||
@ -253,6 +307,9 @@ public class DrawCell {
|
||||
if(currentChunk == null){
|
||||
return false;
|
||||
}
|
||||
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
|
||||
this.homogenous = false;
|
||||
}
|
||||
faceWeights[x][y] = currentChunk.getWeight(
|
||||
0,
|
||||
localCoord1,
|
||||
@ -276,6 +333,9 @@ public class DrawCell {
|
||||
if(currentChunk == null){
|
||||
return false;
|
||||
}
|
||||
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
|
||||
this.homogenous = false;
|
||||
}
|
||||
faceWeights[x][y] = currentChunk.getWeight(
|
||||
0,
|
||||
localCoord1,
|
||||
@ -299,6 +359,9 @@ public class DrawCell {
|
||||
if(currentChunk == null){
|
||||
return false;
|
||||
}
|
||||
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
|
||||
this.homogenous = false;
|
||||
}
|
||||
faceWeights[x][y] = currentChunk.getWeight(
|
||||
localCoord1,
|
||||
0,
|
||||
@ -322,6 +385,9 @@ public class DrawCell {
|
||||
if(currentChunk == null){
|
||||
return false;
|
||||
}
|
||||
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
|
||||
this.homogenous = false;
|
||||
}
|
||||
faceWeights[x][y] = currentChunk.getWeight(
|
||||
localCoord1,
|
||||
0,
|
||||
@ -345,6 +411,9 @@ public class DrawCell {
|
||||
if(currentChunk == null){
|
||||
return false;
|
||||
}
|
||||
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
|
||||
this.homogenous = false;
|
||||
}
|
||||
faceWeights[x][y] = currentChunk.getWeight(
|
||||
localCoord1,
|
||||
localCoord2,
|
||||
@ -368,6 +437,9 @@ public class DrawCell {
|
||||
if(currentChunk == null){
|
||||
return false;
|
||||
}
|
||||
if(currentChunk.getHomogenousValue() == ChunkData.NOT_HOMOGENOUS){
|
||||
this.homogenous = false;
|
||||
}
|
||||
faceWeights[x][y] = currentChunk.getWeight(
|
||||
localCoord1,
|
||||
localCoord2,
|
||||
@ -483,6 +555,29 @@ public class DrawCell {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -233,7 +233,8 @@ public class DrawCellManager {
|
||||
|
||||
//build the cell
|
||||
DrawCell cell = DrawCell.generateTerrainCell(
|
||||
worldPos
|
||||
worldPos,
|
||||
0
|
||||
);
|
||||
cells.add(cell);
|
||||
keyCellMap.put(targetKey,cell);
|
||||
|
||||
@ -55,7 +55,7 @@ public class ClientTerrainManager {
|
||||
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
|
||||
|
||||
//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
|
||||
|
||||
@ -56,7 +56,7 @@ public class ImGuiChunkMonitor {
|
||||
ImGui.text("Chunk Monitor");
|
||||
|
||||
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());
|
||||
|
||||
|
||||
|
||||
@ -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_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;
|
||||
|
||||
String animationStartUp = Animation.ANIMATION_MOVEMENT_STARTUP;
|
||||
|
||||
@ -50,6 +50,7 @@ public class TerrainChunk {
|
||||
TerrainChunkData data;
|
||||
try {
|
||||
data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
|
||||
data.constructBuffers();
|
||||
if(Globals.clientScene.containsEntity(rVal)){
|
||||
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas);
|
||||
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
package electrosphere.entity.types.terrain;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
/**
|
||||
* The data required to generate a texture
|
||||
*/
|
||||
@ -20,6 +24,17 @@ public class TerrainChunkData {
|
||||
//texture ratio vector
|
||||
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
|
||||
*/
|
||||
@ -44,6 +59,49 @@ public class TerrainChunkData {
|
||||
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
|
||||
* @return the vertex data
|
||||
@ -100,4 +158,29 @@ public class TerrainChunkData {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ public class TerrainMessage extends NetworkMessage {
|
||||
double realLocationY;
|
||||
double realLocationZ;
|
||||
byte[] chunkData;
|
||||
int homogenousValue;
|
||||
int chunkResolution;
|
||||
float terrainWeight;
|
||||
int terrainValue;
|
||||
@ -317,6 +318,20 @@ public class TerrainMessage extends NetworkMessage {
|
||||
this.chunkData = chunkData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets homogenousValue
|
||||
*/
|
||||
public int gethomogenousValue() {
|
||||
return homogenousValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets homogenousValue
|
||||
*/
|
||||
public void sethomogenousValue(int homogenousValue) {
|
||||
this.homogenousValue = homogenousValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets chunkResolution
|
||||
*/
|
||||
@ -738,17 +753,20 @@ public class TerrainMessage extends NetworkMessage {
|
||||
if(currentStreamLength < 18){
|
||||
return false;
|
||||
}
|
||||
int chunkDataSize = 0;
|
||||
if(currentStreamLength < 22){
|
||||
return false;
|
||||
}
|
||||
int chunkDataSize = 0;
|
||||
if(currentStreamLength < 26){
|
||||
return false;
|
||||
} else {
|
||||
temporaryByteQueue.add(byteBuffer.peek(18 + 0));
|
||||
temporaryByteQueue.add(byteBuffer.peek(18 + 1));
|
||||
temporaryByteQueue.add(byteBuffer.peek(18 + 2));
|
||||
temporaryByteQueue.add(byteBuffer.peek(18 + 3));
|
||||
temporaryByteQueue.add(byteBuffer.peek(22 + 0));
|
||||
temporaryByteQueue.add(byteBuffer.peek(22 + 1));
|
||||
temporaryByteQueue.add(byteBuffer.peek(22 + 2));
|
||||
temporaryByteQueue.add(byteBuffer.peek(22 + 3));
|
||||
chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
|
||||
}
|
||||
if(currentStreamLength < 22 + chunkDataSize){
|
||||
if(currentStreamLength < 26 + chunkDataSize){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -764,6 +782,7 @@ public class TerrainMessage extends NetworkMessage {
|
||||
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.sethomogenousValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
|
||||
return rVal;
|
||||
}
|
||||
@ -771,12 +790,13 @@ public class TerrainMessage extends NetworkMessage {
|
||||
/**
|
||||
* 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);
|
||||
rVal.setworldX(worldX);
|
||||
rVal.setworldY(worldY);
|
||||
rVal.setworldZ(worldZ);
|
||||
rVal.setchunkResolution(chunkResolution);
|
||||
rVal.sethomogenousValue(homogenousValue);
|
||||
rVal.setchunkData(chunkData);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
@ -1158,7 +1178,7 @@ public class TerrainMessage extends NetworkMessage {
|
||||
}
|
||||
break;
|
||||
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
|
||||
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
|
||||
//entity messaage header
|
||||
@ -1179,12 +1199,16 @@ public class TerrainMessage extends NetworkMessage {
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[14+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(homogenousValue);
|
||||
for(int i = 0; i < 4; 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++){
|
||||
rawBytes[22+i] = chunkData[i];
|
||||
rawBytes[26+i] = chunkData[i];
|
||||
}
|
||||
break;
|
||||
case REQUESTFLUIDDATA:
|
||||
|
||||
@ -246,7 +246,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
||||
|
||||
// System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
|
||||
LoggerInterface.loggerNetworking.DEBUG("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
|
||||
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructSendReducedChunkDataMessage(worldX, worldY, worldZ, stride, buffer.array()));
|
||||
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructSendReducedChunkDataMessage(worldX, worldY, worldZ, stride, chunk.getHomogenousValue(), buffer.array()));
|
||||
};
|
||||
|
||||
//request chunk
|
||||
|
||||
@ -9,7 +9,6 @@ import java.util.Map;
|
||||
|
||||
|
||||
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();
|
||||
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
|
||||
if(vertexArrayBufferData.position() > 0){
|
||||
@ -858,11 +835,6 @@ public class TerrainChunkModelGeneration {
|
||||
// SAMPLER INDICES
|
||||
//
|
||||
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){
|
||||
samplerBuffer.flip();
|
||||
mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_INDEX_ATTRIB_LOC);
|
||||
@ -875,14 +847,9 @@ public class TerrainChunkModelGeneration {
|
||||
// SAMPLER RATIO DATA
|
||||
//
|
||||
try {
|
||||
int vertexCount = data.getTextureRatioVectors().size() / 3;
|
||||
FloatBuffer samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3);
|
||||
for(float samplerVec : data.getTextureRatioVectors()){
|
||||
samplerBuffer.put(samplerVec);
|
||||
}
|
||||
if(samplerBuffer.position() > 0){
|
||||
samplerBuffer.flip();
|
||||
mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_RATIO_ATTRIB_LOC);
|
||||
if(ratioBuffer.position() > 0){
|
||||
ratioBuffer.flip();
|
||||
mesh.bufferCustomFloatAttribArray(ratioBuffer, 3, SAMPLER_RATIO_ATTRIB_LOC);
|
||||
}
|
||||
} catch (NullPointerException ex){
|
||||
ex.printStackTrace();
|
||||
@ -918,7 +885,7 @@ public class TerrainChunkModelGeneration {
|
||||
*/
|
||||
public static Model generateTerrainModel(TerrainChunkData data, VoxelTextureAtlas atlas){
|
||||
Model rVal = new Model();
|
||||
Mesh m = generateTerrainMesh(data);
|
||||
Mesh m = TerrainChunkModelGeneration.generateTerrainMesh(data);
|
||||
|
||||
//construct the material for the chunk
|
||||
Material groundMat = new Material();
|
||||
|
||||
@ -19,6 +19,7 @@ import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.ServerEntityUtils;
|
||||
import electrosphere.entity.state.server.ServerPlayerViewDirTree;
|
||||
import electrosphere.entity.types.creature.CreatureUtils;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.EntityMessage;
|
||||
@ -401,12 +402,16 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
//clear all entities in cell
|
||||
for(Entity entity : cell.getScene().getEntityList()){
|
||||
if(ServerPlayerViewDirTree.hasTree(entity)){
|
||||
int playerId = CreatureUtils.getControllerPlayerId(entity);
|
||||
Player player = Globals.playerManager.getPlayerFromId(playerId);
|
||||
throw new Error(
|
||||
"Trying to unload a player's entity! " +
|
||||
entity + "\n" +
|
||||
EntityUtils.getPosition(entity) + "\n" +
|
||||
serverWorldData.convertRealToWorldSpace(EntityUtils.getPosition(entity)) + "\n" +
|
||||
worldPos
|
||||
"entity: " + entity + "\n" +
|
||||
"entity pos (real): " + EntityUtils.getPosition(entity) + "\n" +
|
||||
"entity pos (world): " + serverWorldData.convertRealToWorldSpace(EntityUtils.getPosition(entity)) + "\n" +
|
||||
"chunk pos (world): " + worldPos + "\n" +
|
||||
"player pos (world): " + player.getWorldPos() + "\n" +
|
||||
"Number of players in cell: " + cell.getPlayers().size()
|
||||
);
|
||||
}
|
||||
ServerEntityUtils.destroyEntity(entity);
|
||||
|
||||
@ -11,6 +11,7 @@ import java.util.concurrent.Semaphore;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterOutputStream;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
@ -153,14 +154,21 @@ public class ChunkDiskMap {
|
||||
}
|
||||
IntBuffer intView = buffer.asIntBuffer();
|
||||
intView.position(DIM * DIM * DIM);
|
||||
int firstType = -1;
|
||||
boolean homogenous = true;
|
||||
for(int x = 0; x < DIM; x++){
|
||||
for(int y = 0; y < DIM; y++){
|
||||
for(int z = 0; z < DIM; z++){
|
||||
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();
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.server.terrain.generation;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.entity.scene.RealmDescriptor;
|
||||
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ package electrosphere.server.terrain.generation;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.game.terrain.processing.TerrainInterpolator;
|
||||
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ package electrosphere.server.terrain.generation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
|
||||
// import org.graalvm.polyglot.Value;
|
||||
|
||||
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];;
|
||||
int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||
|
||||
int firstType = -1;
|
||||
boolean homogenous = true;
|
||||
try {
|
||||
//actual generation algo
|
||||
|
||||
@ -176,6 +180,11 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
weights[x][y][z] = voxel.weight;
|
||||
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();
|
||||
@ -184,7 +193,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
} catch(Exception ex){
|
||||
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();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -52,15 +52,21 @@ public class ServerTerrainChunk {
|
||||
*/
|
||||
int[][][] values;
|
||||
|
||||
/**
|
||||
* The homogenous value of this data, or ChunkData.NOT_HOMOGENOUS if it is not homogenous
|
||||
*/
|
||||
int homogenousValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param worldX The world position x coordinate
|
||||
* @param worldY The world position y 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 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.worldY = worldY;
|
||||
this.worldZ = worldZ;
|
||||
@ -182,5 +188,12 @@ public class ServerTerrainChunk {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ public class WorldOctTree <T> {
|
||||
FloatingChunkTreeNode<T> newContainer = new FloatingChunkTreeNode<>(this, currentLevel, min, max);
|
||||
|
||||
//replace existing node
|
||||
replaceNode(existing,newContainer);
|
||||
this.replaceNode(existing,newContainer);
|
||||
|
||||
//update tracking
|
||||
this.nodes.remove(existing);
|
||||
@ -156,8 +156,9 @@ public class WorldOctTree <T> {
|
||||
this.root = newNode;
|
||||
} else {
|
||||
FloatingChunkTreeNode<T> parent = existing.getParent();
|
||||
int index = parent.children.indexOf(existing);
|
||||
parent.removeChild(existing);
|
||||
parent.addChild(newNode);
|
||||
parent.addChild(index, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,6 +423,16 @@ public class WorldOctTree <T> {
|
||||
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
|
||||
* @param child the child
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package electrosphere.util.math;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package electrosphere.client.terrain.cells;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
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);
|
||||
ClientDrawCellManager manager = new ClientDrawCellManager(null, 64);
|
||||
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));
|
||||
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
|
||||
Main.shutdown();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user