foliage manager fixes
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-09-11 19:02:53 -04:00
parent 742c606540
commit 8430439509
9 changed files with 188 additions and 92 deletions

View File

@ -61,15 +61,19 @@ public class ClientFoliageManager {
for(int x = -chunkRadius; x < chunkRadius+1; x++){
for(int y = -chunkRadius; y < chunkRadius+1; y++){
for(int z = -chunkRadius; z < chunkRadius+1; z++){
Vector3i worldPos = Globals.clientWorldData.convertRealToWorldSpace(EntityUtils.getPosition(Globals.playerEntity));
updatePosition(worldPos);
Vector3i worldPos = Globals.clientWorldData.convertRealToWorldSpace(EntityUtils.getPosition(Globals.playerEntity)).add(x,y,z);
if(Globals.clientWorldData.worldPosInBounds(worldPos)){
this.updatePosition(worldPos);
}
}
}
}
Globals.profiler.beginCpuSample("ClientFoliageManager.update - destroy chunks");
for(FoliageChunk chunk : this.chunkUpdateCache){
chunk.destroy();
}
this.chunkUpdateCache.clear();
Globals.profiler.endCpuSample();
}
Globals.profiler.endCpuSample();
}

View File

@ -22,8 +22,6 @@ import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.foliage.AmbientFoliage;
import electrosphere.entity.types.camera.CameraEntityUtils;
@ -344,7 +342,7 @@ public class FoliageCell {
EntityUtils.getRotation(grassEntity).set(0,0,0,1);
EntityUtils.getScale(grassEntity).set(1,1,1);
//add ambient foliage behavior tree
AmbientFoliage.attachAmbientFoliageTree(grassEntity, 0.0f, foliageType.getGrowthModel().getGrowthRate());
AmbientFoliage.attachAmbientFoliageTree(grassEntity, 1.0f, foliageType.getGrowthModel().getGrowthRate());
this.addEntity(grassEntity);
}
}
@ -384,9 +382,9 @@ public class FoliageCell {
RenderPipelineState renderPipelineState = Globals.renderingEngine.getRenderPipelineState();
OpenGLState openGLState = Globals.renderingEngine.getOpenGLState();
Vector3f cameraModifiedPosition = new Vector3f((float)realPosition.x,(float)realPosition.y,(float)realPosition.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Vector3f cameraModifiedPosition = new Vector3f((float)realPosition.x,(float)realPosition.y,(float)realPosition.z).sub(cameraCenter);
//frustum check entire cell
boolean shouldRender = true;//renderPipelineState.getFrustumIntersection().testSphere((float)(cameraModifiedPosition.x + boundingSphere.x), (float)(cameraModifiedPosition.y + boundingSphere.y), (float)(cameraModifiedPosition.z + boundingSphere.z), (float)(boundingSphere.r));
boolean shouldRender = renderPipelineState.getFrustumIntersection().testSphere((float)(cameraModifiedPosition.x + boundingSphere.x), (float)(cameraModifiedPosition.y + boundingSphere.y), (float)(cameraModifiedPosition.z + boundingSphere.z), (float)(boundingSphere.r));
if(shouldRender){
//disable frustum check and instead perform at cell level
boolean currentFrustumCheckState = renderPipelineState.shouldFrustumCheck();
@ -398,7 +396,7 @@ public class FoliageCell {
modelMatrix = modelMatrix.identity();
cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
// cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
modelMatrix.translate(cameraModifiedPosition);
modelMatrix.rotate(new Quaterniond(grassRotation));
modelMatrix.scale(new Vector3d(EntityUtils.getScale(entity)));

View File

@ -1,7 +1,6 @@
package electrosphere.client.foliagemanager;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3d;
import org.joml.Vector3i;
@ -65,80 +64,100 @@ public class FoliageChunk {
* Initializes all cells in the chunk
*/
public void initCells(){
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
Globals.profiler.beginCpuSample("FoliageChunk.initCells");
this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
// //evaluate top cells if chunk above this one exists
this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0));
//the sets to iterate through
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
List<ChunkTreeNode<FoliageCell>> openSet = new LinkedList<ChunkTreeNode<FoliageCell>>();
List<ChunkTreeNode<FoliageCell>> toInit = new LinkedList<ChunkTreeNode<FoliageCell>>();
openSet.add(rootNode);
//split into nodes
while(openSet.size() > 0){
ChunkTreeNode<FoliageCell> current = openSet.remove(0);
if(this.shouldSplit(playerPos, current)){
//add children for this one
ChunkTreeNode<FoliageCell> container = chunkTree.split(current);
openSet.addAll(container.getChildren());
} else {
//do nothing
toInit.add(current);
}
}
//init end-nodes as leaves
for(ChunkTreeNode<FoliageCell> current : toInit){
Vector3d realPos = Globals.clientWorldData.convertWorldToRealSpace(worldPos).add(new Vector3d(current.getMinBound()));
current.convertToLeaf(new FoliageCell(worldPos, current.getMinBound(), realPos, 4 - current.getLevel(), 1.0f));
current.getData().generate();
}
Globals.profiler.endCpuSample();
}
/**
* Updates all cells in the chunk
*/
public void updateCells(){
Globals.profiler.beginCpuSample("FoliageChunk.updateCells");
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
//the sets to iterate through
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
List<ChunkTreeNode<FoliageCell>> openSet = new LinkedList<ChunkTreeNode<FoliageCell>>();
List<ChunkTreeNode<FoliageCell>> toInit = new LinkedList<ChunkTreeNode<FoliageCell>>();
List<ChunkTreeNode<FoliageCell>> toCleanup = new LinkedList<ChunkTreeNode<FoliageCell>>();
openSet.add(rootNode);
this.recursivelyUpdateCells(rootNode, playerPos);
//split into nodes
while(openSet.size() > 0){
ChunkTreeNode<FoliageCell> current = openSet.remove(0);
// Globals.profiler.beginCpuSample("FoliageChunk.updateCells - evaluate split/join");
// while(openSet.size() > 0){
// ChunkTreeNode<FoliageCell> current = openSet.remove(0);
if(this.shouldSplit(playerPos, current)){
//add children for this one
ChunkTreeNode<FoliageCell> container = chunkTree.split(current);
toInit.addAll(container.getChildren());
toCleanup.add(current);
} else if(this.shouldJoin(playerPos, current)) {
//add children for this one
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(current);
toCleanup.addAll(current.getChildren());
toCleanup.add(current);
toInit.add(newLeaf);
} else if(!current.isLeaf()){
openSet.addAll(current.getChildren());
}
}
//init end-nodes as leaves
for(ChunkTreeNode<FoliageCell> current : toInit){
Vector3d realPos = Globals.clientWorldData.convertWorldToRealSpace(worldPos).add(new Vector3d(current.getMinBound()));
current.convertToLeaf(new FoliageCell(worldPos, current.getMinBound(), realPos, 5 - current.getLevel(), 1.0f));
current.getData().generate();
}
//destroy orphan nodes
for(ChunkTreeNode<FoliageCell> current : toCleanup){
if(current.getData() != null){
current.getData().destroy();
}
// if(this.shouldSplit(playerPos, current)){
// //add children for this one
// ChunkTreeNode<FoliageCell> container = chunkTree.split(current);
// toInit.addAll(container.getChildren());
// toCleanup.add(current);
// } else if(this.shouldJoin(playerPos, current)) {
// //add children for this one
// ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(current);
// toCleanup.addAll(current.getChildren());
// toCleanup.add(current);
// toInit.add(newLeaf);
// } else if(!current.isLeaf()){
// openSet.addAll(current.getChildren());
// }
// }
// Globals.profiler.endCpuSample();
// Globals.profiler.beginCpuSample("FoliageChunk.updateCells - generate");
// //init end-nodes as leaves
// for(ChunkTreeNode<FoliageCell> current : toInit){
// Vector3d realPos = Globals.clientWorldData.convertWorldToRealSpace(worldPos).add(new Vector3d(current.getMinBound()));
// current.convertToLeaf(new FoliageCell(worldPos, current.getMinBound(), realPos, 5 - current.getLevel(), 1.0f));
// if(this.shouldGenerate(playerPos,current)){
// current.getData().generate();
// }
// }
// Globals.profiler.endCpuSample();
// Globals.profiler.beginCpuSample("FoliageChunk.updateCells - destroy");
// //destroy orphan nodes
// for(ChunkTreeNode<FoliageCell> current : toCleanup){
// if(current.getData() != null){
// current.getData().destroy();
// }
// }
// Globals.profiler.endCpuSample();
Globals.profiler.endCpuSample();
}
/**
* Recursively update child nodes
* @param node The root node
* @param playerPos The player's position
*/
private void recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){
if(this.shouldSplit(playerPos, node)){
//perform op
ChunkTreeNode<FoliageCell> container = chunkTree.split(node);
//do deletions
this.recursivelyDestroy(node);
//do creations
container.getChildren().forEach(child -> {
Vector3d realPos = new Vector3d(
worldPos.x * ChunkData.CHUNK_SIZE + child.getMinBound().x,
worldPos.y * ChunkData.CHUNK_SIZE + child.getMinBound().y,
worldPos.z * ChunkData.CHUNK_SIZE + child.getMinBound().z
);
child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel(), 1.0f));
});
} else if(this.shouldJoin(playerPos, node)) {
//perform op
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node);
//do deletions
this.recursivelyDestroy(node);
//do creations
newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel(), 1.0f));
} else if(shouldGenerate(playerPos, node)){
node.getData().generate();
} else if(!node.isLeaf()){
new LinkedList<>(node.getChildren()).forEach(child -> recursivelyUpdateCells(child, playerPos));
}
}
/**
@ -196,9 +215,11 @@ public class FoliageChunk {
public boolean shouldSplit(Vector3d pos, ChunkTreeNode<FoliageCell> node){
//breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls
return this.getMinDistance(pos, node) <= FULL_RES_DIST &&
return
node.isLeaf() &&
node.canSplit()
node.getLevel() < ChunkTree.MAX_LEVEL &&
node.canSplit() &&
this.getMinDistance(pos, node) <= FULL_RES_DIST
;
}
@ -211,17 +232,56 @@ public class FoliageChunk {
public boolean shouldJoin(Vector3d pos, ChunkTreeNode<FoliageCell> node){
//breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls
return this.getMinDistance(pos, node) > FULL_RES_DIST &&
!node.isLeaf()
return
node.getLevel() > 0 &&
!node.isLeaf() &&
this.getMinDistance(pos, node) > FULL_RES_DIST
;
}
/**
* Checks if this cell should generate
* @param pos the player's position
* @param node the node
* @return true if should generate, false otherwise
*/
public boolean shouldGenerate(Vector3d pos, ChunkTreeNode<FoliageCell> node){
return
node.getLevel() == ChunkTree.MAX_LEVEL &&
node.isLeaf() &&
node.getData() != null &&
node.getData().containedEntities.size() < 1 &&
this.getMinDistance(pos, node) <= FULL_RES_DIST;
}
/**
* Checks if the node should have destroy called on it
* @param node The node
* @return true if should destroy, false otherwise
*/
public boolean shouldDestroy(ChunkTreeNode<FoliageCell> node){
return
node.getData() != null &&
node.getData().containedEntities.size() > 0;
}
/**
* Destroys the foliage chunk
*/
protected void destroy(){
for(ChunkTreeNode<FoliageCell> leaf : this.chunkTree.getLeaves()){
leaf.getData().destroy();
this.recursivelyDestroy(this.chunkTree.getRoot());
}
/**
* Recursively destroy a tree
* @param node The root of the tree
*/
private void recursivelyDestroy(ChunkTreeNode<FoliageCell> node){
if(node.getChildren().size() > 0){
node.getChildren().forEach(child -> recursivelyDestroy(child));
}
if(node.getData() != null){
node.getData().destroy();
}
}
@ -229,8 +289,19 @@ public class FoliageChunk {
* Draws all cells in the chunk
*/
protected void draw(){
for(ChunkTreeNode<FoliageCell> leaf : this.chunkTree.getLeaves()){
leaf.getData().draw();
recursivelyDraw(this.chunkTree.getRoot());
}
/**
* Recursively draws all nodes
* @param node The root node
*/
private void recursivelyDraw(ChunkTreeNode<FoliageCell> node){
if(node.getChildren().size() > 0){
node.getChildren().forEach(child -> recursivelyDraw(child));
}
if(node.getData() != null){
node.getData().draw();
}
}

View File

@ -93,6 +93,21 @@ public class ClientWorldData {
return convertChunkToRealSpace(world);
}
/**
* Checks if a world position is in bounds or not
* @param worldPos The world position
* @return true if is in bounds, false otherwise
*/
public boolean worldPosInBounds(Vector3i worldPos){
return worldPos.x >= convertRealToWorld(worldMinPoint.x) &&
worldPos.x < convertRealToWorld(worldMaxPoint.x) &&
worldPos.y >= convertRealToWorld(worldMinPoint.y) &&
worldPos.y < convertRealToWorld(worldMaxPoint.y) &&
worldPos.z >= convertRealToWorld(worldMinPoint.z) &&
worldPos.z < convertRealToWorld(worldMaxPoint.z)
;
}
/**
* Converts a real space position to its world space equivalent
* @param position The real space position

View File

@ -447,6 +447,10 @@ public class Main {
if(Globals.netMonitor != null){
Globals.netMonitor.close();
}
//shutdown profiler
if(Globals.profiler != null){
Globals.profiler.destroy();
}
//shutdown ode
if(initOde){
OdeHelper.closeODE();

View File

@ -69,4 +69,13 @@ public class Profiler {
}
}
/**
* Destroys the profiler
*/
public void destroy(){
if(PROFILE){
Remotery.rmt_DestroyGlobalInstance(pointer);
}
}
}

View File

@ -29,7 +29,7 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
Globals.clientWorldData = new ClientWorldData(
//Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize
new Vector3f(message.getworldMinX(),0,message.getworldMinY()),
new Vector3f(message.getworldMaxX(),3,message.getworldMaxY()),
new Vector3f(message.getworldMaxX(),32,message.getworldMaxY()),
ChunkData.CHUNK_SIZE,
message.getrandomDampener(),
message.getworldSizeDiscrete()

View File

@ -126,21 +126,6 @@ public class ChunkTree<T> {
return this.root;
}
/**
* Gets the number of leaves in the tree
*/
public int getNumLeaves() {
return this.getLeaves().size();
}
/**
* Gets all leaf nodes
* @return All leaf nodes
*/
public List<ChunkTreeNode<T>> getLeaves(){
return this.nodes.stream().filter(node -> node.isLeaf()).collect(Collectors.toList());
}
/**
* A node in a chunk tree

View File

@ -0,0 +1,10 @@
package electrosphere.util.ds.octree;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit testing for the chunk octree implementation
*/
public class ChunkTreeTests {
}