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
|
#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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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());
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user