scaled foliage cell generation

This commit is contained in:
austin 2024-09-11 20:54:04 -04:00
parent 8430439509
commit 5e06dbe786
8 changed files with 343 additions and 153 deletions

1
.gitignore vendored
View File

@ -14,6 +14,7 @@
/NetArranger-*.jar /NetArranger-*.jar
/lwjglx-debug-*.jar /lwjglx-debug-*.jar
/hs_err_pid* /hs_err_pid*
/replay_pid*
/trace.log /trace.log
.classpath .classpath

View File

@ -128,7 +128,10 @@ public class ClientFoliageManager {
* @return true if all are ready, false otherwise * @return true if all are ready, false otherwise
*/ */
public boolean dependenciesAreReady(){ public boolean dependenciesAreReady(){
return Globals.clientWorldData != null; return
Globals.clientWorldData != null &&
Globals.drawCellManager != null
;
} }
/** /**

View File

@ -122,22 +122,30 @@ public class FoliageCell {
*/ */
int scale; int scale;
//position of the foliage cell in world coordinates /**
* Position of the foliage cell in world coordinates
*/
protected Vector3i worldPosition; protected Vector3i worldPosition;
//position of the foliage cell in local coordinates
/**
* Position of the foliage cell in local coordinates
*/
protected Vector3i voxelPosition; protected Vector3i voxelPosition;
//the real position of this cell, stored so we don't constantly have to recalculate
/**
* The real position of this cell, stored so we don't constantly have to recalculate
*/
protected Vector3d realPosition; protected Vector3d realPosition;
//constituent entities
/**
* Constituent entities
*/
protected Set<Entity> containedEntities; protected Set<Entity> containedEntities;
//template bounding shere used for checking frustum for this cell
static Sphered boundingSphere = new Sphered(0.5,0.5,0.5,2);
/** /**
* The density of the cell * Template bounding shere used for checking frustum for this cell
*/ */
protected float density; static Sphered boundingSphere = new Sphered(0.5,0.5,0.5,2);
/** /**
* Inits the foliage cell data * Inits the foliage cell data
@ -157,12 +165,11 @@ public class FoliageCell {
* @param worldPos The position of the foliage cell in world coordinates * @param worldPos The position of the foliage cell in world coordinates
* @param voxelPos The position of the foliage cell in voxel coordinates * @param voxelPos The position of the foliage cell in voxel coordinates
*/ */
protected FoliageCell(Vector3i worldPos, Vector3i voxelPos, Vector3d realPos, int scale, float density){ protected FoliageCell(Vector3i worldPos, Vector3i voxelPos, Vector3d realPos, int scale){
this.worldPosition = worldPos; this.worldPosition = worldPos;
this.voxelPosition = voxelPos; this.voxelPosition = voxelPos;
this.realPosition = realPos; this.realPosition = realPos;
this.scale = scale; this.scale = scale;
this.density = density;
this.containedEntities = new HashSet<Entity>(); this.containedEntities = new HashSet<Entity>();
} }
@ -198,34 +205,94 @@ public class FoliageCell {
boolean shouldGenerate = false; boolean shouldGenerate = false;
//get foliage types supported //get foliage types supported
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition); ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition);
List<String> foliageTypesSupported = null; if(data == null){
if(data != null && voxelPosition.y + 1 < ServerTerrainChunk.CHUNK_DIMENSION){ return;
foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPosition)).getAmbientFoliage(); }
boolean airAbove = data.getType(voxelPosition.x,voxelPosition.y+1,voxelPosition.z) == 0; if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){
if(foliageTypesSupported != null && airAbove && scale < 2){ return;
shouldGenerate = true; }
} if(!Globals.drawCellManager.generatedPhysics(worldPosition)){
return;
}
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPosition)).getAmbientFoliage();
boolean airAbove = data.getType(voxelPosition.x,voxelPosition.y+1,voxelPosition.z) == 0;
if(foliageTypesSupported != null && airAbove && scale < 3){
shouldGenerate = true;
} }
if(shouldGenerate){ if(shouldGenerate){
//get type //get type
String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size()); String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size());
FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName); FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName);
//create cell and buffer //create cell and buffer
int FINAL_SPACING = (int)(TARGET_FOLIAGE_SPACING * density); ByteBuffer buffer = BufferUtils.createByteBuffer(TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
ByteBuffer buffer = BufferUtils.createByteBuffer(FINAL_SPACING * FINAL_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
FloatBuffer floatBufferView = buffer.asFloatBuffer(); FloatBuffer floatBufferView = buffer.asFloatBuffer();
int drawCount = 0;
for(int x = 0; x < scale; x++){
for(int y = 0; y < scale; y++){
for(int z = 0; z < scale; z++){
drawCount = drawCount + insertBlades(x, y, z, floatBufferView, data);
}
}
}
// drawCount = drawCount + this.insertBlades(0, 0, 0, floatBufferView, data);
if(drawCount > 0){
buffer.position(0);
buffer.limit(TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
//construct data texture
Texture dataTexture = new Texture(Globals.renderingEngine.getOpenGLState(),buffer,SINGLE_FOLIAGE_DATA_SIZE_BYTES / 4,TARGET_FOLIAGE_PER_CELL);
//create entity
Entity grassEntity = EntityCreationUtils.createClientSpatialEntity();
TextureInstancedActor.attachTextureInstancedActor(grassEntity, foliageType.getModelPath(), vertexPath, fragmentPath, dataTexture, drawCount);
EntityUtils.getPosition(grassEntity).set(realPosition);
EntityUtils.getRotation(grassEntity).set(0,0,0,1);
EntityUtils.getScale(grassEntity).set(1,1,1);
//add ambient foliage behavior tree
AmbientFoliage.attachAmbientFoliageTree(grassEntity, 1.0f, foliageType.getGrowthModel().getGrowthRate());
this.addEntity(grassEntity);
}
}
}
/**
* Insert blades of grass into the entity
* @param vX the x offset of the voxel
* @param vY the y offset of the voxel
* @param vZ the z offset of the voxel
* @param floatBufferView the gpu data buffer
* @param chunkData the chunk data
* @return the number of blades of grass added
*/
protected int insertBlades(int vX, int vY, int vZ, FloatBuffer floatBufferView, ChunkData chunkData){
int rVal = 0;
//get positions offset
Vector3d voxelRealPos = new Vector3d(realPosition).add(vX,vY,vZ);
Vector3i currVoxelPos = new Vector3i(voxelPosition).add(vY,vY,vZ);
//check that the current voxel even supports foliage
boolean shouldGenerate = false;
List<String> foliageTypesSupported = null;
if(chunkData != null && currVoxelPos.y + 1 < ServerTerrainChunk.CHUNK_DIMENSION){
foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(chunkData.getType(currVoxelPos)).getAmbientFoliage();
boolean airAbove = chunkData.getType(currVoxelPos.x,currVoxelPos.y+1,currVoxelPos.z) == 0;
if(foliageTypesSupported != null && airAbove){
shouldGenerate = true;
}
}
if(shouldGenerate){
//construct simple grid to place foliage on //construct simple grid to place foliage on
Vector3d sample_00 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add(-0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_00 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_01 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add(-0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_01 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_02 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add(-0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_02 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_10 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add( 0,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_10 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_11 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add( 0,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_11 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_12 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add( 0,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_12 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_20 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add( 0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_20 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_21 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add( 0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_21 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT, 0), new Vector3d(0,-1,0), RAY_LENGTH);
Vector3d sample_22 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).add( 0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH); Vector3d sample_22 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT, 0.5), new Vector3d(0,-1,0), RAY_LENGTH);
//get the heights of each sample //get the heights of each sample
float height_11 = (float)(sample_11 != null ? sample_11.y : 0); float height_11 = (float)(sample_11 != null ? sample_11.y : 0);
float height_00 = (float)(sample_00 != null ? sample_00.y : height_11); float height_00 = (float)(sample_00 != null ? sample_00.y : height_11);
@ -244,14 +311,13 @@ public class FoliageCell {
//if we don't find data for the center sample, can't place grass so don't create entity //if we don't find data for the center sample, can't place grass so don't create entity
if(sample_11 != null){ if(sample_11 != null){
//generate positions to place //generate positions to place
int drawCount = 0; for(int x = 0; x < TARGET_FOLIAGE_SPACING; x=x+scale){
for(int x = 0; x < FINAL_SPACING; x++){ for(int z = 0; z < TARGET_FOLIAGE_SPACING; z=z+scale){
for(int z = 0; z < FINAL_SPACING; z++){
//get position to place //get position to place
double rand1 = placementRandomizer.nextDouble(); double rand1 = placementRandomizer.nextDouble();
double rand2 = placementRandomizer.nextDouble(); double rand2 = placementRandomizer.nextDouble();
double relativePositionOnGridX = x / (1.0 * FINAL_SPACING) + rand1 / FINAL_SPACING; double relativePositionOnGridX = x / (1.0 * TARGET_FOLIAGE_SPACING) + rand1 / TARGET_FOLIAGE_SPACING;
double relativePositionOnGridZ = z / (1.0 * FINAL_SPACING) + rand2 / FINAL_SPACING; double relativePositionOnGridZ = z / (1.0 * TARGET_FOLIAGE_SPACING) + rand2 / TARGET_FOLIAGE_SPACING;
double offsetX = relativePositionOnGridX - 0.5; double offsetX = relativePositionOnGridX - 0.5;
double offsetZ = relativePositionOnGridZ - 0.5; double offsetZ = relativePositionOnGridZ - 0.5;
//determine quadrant we're placing in //determine quadrant we're placing in
@ -320,32 +386,18 @@ public class FoliageCell {
offsetY = offsetY - realPosition.y; offsetY = offsetY - realPosition.y;
double rotVar = placementRandomizer.nextDouble() * Math.PI * 2; double rotVar = placementRandomizer.nextDouble() * Math.PI * 2;
double rotVar2 = placementRandomizer.nextDouble(); double rotVar2 = placementRandomizer.nextDouble();
floatBufferView.put((float)offsetX); floatBufferView.put((float)offsetX + vX);
floatBufferView.put((float)offsetY); floatBufferView.put((float)offsetY + vY);
floatBufferView.put((float)offsetZ); floatBufferView.put((float)offsetZ + vZ);
floatBufferView.put((float)rotVar); floatBufferView.put((float)rotVar);
floatBufferView.put((float)rotVar2); floatBufferView.put((float)rotVar2);
drawCount++; rVal++;
} }
} }
} }
buffer.position(0);
buffer.limit(FINAL_SPACING * FINAL_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
//construct data texture
Texture dataTexture = new Texture(Globals.renderingEngine.getOpenGLState(),buffer,SINGLE_FOLIAGE_DATA_SIZE_BYTES / 4,FINAL_SPACING * FINAL_SPACING);
//create entity
Entity grassEntity = EntityCreationUtils.createClientSpatialEntity();
TextureInstancedActor.attachTextureInstancedActor(grassEntity, foliageType.getModelPath(), vertexPath, fragmentPath, dataTexture, drawCount);
EntityUtils.getPosition(grassEntity).set(realPosition);
EntityUtils.getRotation(grassEntity).set(0,0,0,1);
EntityUtils.getScale(grassEntity).set(1,1,1);
//add ambient foliage behavior tree
AmbientFoliage.attachAmbientFoliageTree(grassEntity, 1.0f, foliageType.getGrowthModel().getGrowthRate());
this.addEntity(grassEntity);
} }
} }
return rVal;
} }
/** /**
@ -396,7 +448,6 @@ public class FoliageCell {
modelMatrix = modelMatrix.identity(); modelMatrix = modelMatrix.identity();
// cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
modelMatrix.translate(cameraModifiedPosition); modelMatrix.translate(cameraModifiedPosition);
modelMatrix.rotate(new Quaterniond(grassRotation)); modelMatrix.rotate(new Quaterniond(grassRotation));
modelMatrix.scale(new Vector3d(EntityUtils.getScale(entity))); modelMatrix.scale(new Vector3d(EntityUtils.getScale(entity)));
@ -500,16 +551,4 @@ public class FoliageCell {
// } // }
// } // }
// } // }
/**
* Gets whether the voxel type supports foliage or not
* @param type
* @return
*/
private boolean typeSupportsFoliage(int type){
if(Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type) != null){
return Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type).getAmbientFoliage() != null;
}
return false;
}
} }

View File

@ -1,5 +1,6 @@
package electrosphere.client.foliagemanager; package electrosphere.client.foliagemanager;
import java.util.List;
import java.util.LinkedList; import java.util.LinkedList;
import org.joml.Vector3d; import org.joml.Vector3d;
@ -10,6 +11,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.util.ds.octree.ChunkTree; import electrosphere.util.ds.octree.ChunkTree;
import electrosphere.util.ds.octree.ChunkTree.ChunkTreeNode; import electrosphere.util.ds.octree.ChunkTree.ChunkTreeNode;
import electrosphere.util.math.GeomUtils;
/** /**
* A whole chunk of foliage * A whole chunk of foliage
@ -25,7 +27,12 @@ public class FoliageChunk {
/** /**
* The distance to draw at full resolution * The distance to draw at full resolution
*/ */
static final double FULL_RES_DIST = 15; static final double FULL_RES_DIST = 20;
/**
* The distance for half resolution
*/
static final double HALF_RES_DIST = 40;
/** /**
* The octree holding all the chunks to evaluate * The octree holding all the chunks to evaluate
@ -68,6 +75,7 @@ public class FoliageChunk {
this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
// //evaluate top cells if chunk above this one exists // //evaluate top cells if chunk above this one exists
this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0)); this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0));
this.updateCells();
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }
@ -78,47 +86,13 @@ public class FoliageChunk {
Globals.profiler.beginCpuSample("FoliageChunk.updateCells"); Globals.profiler.beginCpuSample("FoliageChunk.updateCells");
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
//the sets to iterate through //the sets to iterate through
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot(); boolean updated = true;
this.recursivelyUpdateCells(rootNode, playerPos); int attempts = 0;
//split into nodes while(updated && attempts < 3){
// Globals.profiler.beginCpuSample("FoliageChunk.updateCells - evaluate split/join"); ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
// while(openSet.size() > 0){ updated = this.recursivelyUpdateCells(rootNode, playerPos);
// ChunkTreeNode<FoliageCell> current = openSet.remove(0); attempts++;
}
// 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(); Globals.profiler.endCpuSample();
} }
@ -127,7 +101,8 @@ public class FoliageChunk {
* @param node The root node * @param node The root node
* @param playerPos The player's position * @param playerPos The player's position
*/ */
private void recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){ private boolean recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){
boolean updated = false;
if(this.shouldSplit(playerPos, node)){ if(this.shouldSplit(playerPos, node)){
//perform op //perform op
ChunkTreeNode<FoliageCell> container = chunkTree.split(node); ChunkTreeNode<FoliageCell> container = chunkTree.split(node);
@ -142,8 +117,9 @@ public class FoliageChunk {
worldPos.y * ChunkData.CHUNK_SIZE + child.getMinBound().y, worldPos.y * ChunkData.CHUNK_SIZE + child.getMinBound().y,
worldPos.z * ChunkData.CHUNK_SIZE + child.getMinBound().z worldPos.z * ChunkData.CHUNK_SIZE + child.getMinBound().z
); );
child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel(), 1.0f)); child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel()));
}); });
updated = true;
} else if(this.shouldJoin(playerPos, node)) { } else if(this.shouldJoin(playerPos, node)) {
//perform op //perform op
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node); ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node);
@ -152,12 +128,19 @@ public class FoliageChunk {
this.recursivelyDestroy(node); this.recursivelyDestroy(node);
//do creations //do creations
newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel(), 1.0f)); newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel()));
updated = true;
} else if(shouldGenerate(playerPos, node)){ } else if(shouldGenerate(playerPos, node)){
node.getData().generate(); node.getData().generate();
updated = true;
} else if(!node.isLeaf()){ } else if(!node.isLeaf()){
new LinkedList<>(node.getChildren()).forEach(child -> recursivelyUpdateCells(child, playerPos)); List<ChunkTreeNode<FoliageCell>> children = new LinkedList<ChunkTreeNode<FoliageCell>>(node.getChildren());
for(ChunkTreeNode<FoliageCell> child : children){
boolean childUpdate = recursivelyUpdateCells(child, playerPos);
updated = childUpdate || updated;
}
} }
return updated;
} }
/** /**
@ -169,41 +152,15 @@ public class FoliageChunk {
public double getMinDistance(Vector3d pos, ChunkTreeNode<FoliageCell> node){ public double getMinDistance(Vector3d pos, ChunkTreeNode<FoliageCell> node){
Vector3i min = node.getMinBound(); Vector3i min = node.getMinBound();
Vector3i max = node.getMaxBound(); Vector3i max = node.getMaxBound();
double minX = min.x + realPos.x; double minX = min.x + realPos.x;
double minY = min.y + realPos.y; double minY = min.y + realPos.y;
double minZ = min.z + realPos.z; double minZ = min.z + realPos.z;
double maxX = max.x + realPos.x; double maxX = max.x + realPos.x;
double maxY = max.y + realPos.y; double maxY = max.y + realPos.y;
double maxZ = max.z + realPos.z; double maxZ = max.z + realPos.z;
if(Math.abs(pos.x - minX) < Math.abs(pos.x - maxX)){ return GeomUtils.getMinDistanceAABB(pos, new Vector3d(minX,minY,minZ), new Vector3d(maxX,maxY,maxZ));
if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(minX,minY,minZ);
} else {
return pos.distance(minX,minY,maxZ);
}
} else {
if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(minX,maxY,minZ);
} else {
return pos.distance(minX,maxY,maxZ);
}
}
} else {
if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){
if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(maxX,minY,minZ);
} else {
return pos.distance(maxX,minY,maxZ);
}
} else {
if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(maxX,maxY,minZ);
} else {
return pos.distance(maxX,maxY,maxZ);
}
}
}
} }
/** /**
@ -217,9 +174,18 @@ public class FoliageChunk {
//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.isLeaf() &&
node.getLevel() < ChunkTree.MAX_LEVEL &&
node.canSplit() && node.canSplit() &&
this.getMinDistance(pos, node) <= FULL_RES_DIST (
(
node.getLevel() < ChunkTree.MAX_LEVEL - 1 &&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
||
(
node.getLevel() < ChunkTree.MAX_LEVEL &&
this.getMinDistance(pos, node) <= FULL_RES_DIST
)
)
; ;
} }
@ -235,7 +201,16 @@ public class FoliageChunk {
return return
node.getLevel() > 0 && node.getLevel() > 0 &&
!node.isLeaf() && !node.isLeaf() &&
this.getMinDistance(pos, node) > FULL_RES_DIST (
(
node.getLevel() == ChunkTree.MAX_LEVEL &&
this.getMinDistance(pos, node) > FULL_RES_DIST
)
||
(
this.getMinDistance(pos, node) > HALF_RES_DIST
)
)
; ;
} }
@ -247,11 +222,23 @@ public class FoliageChunk {
*/ */
public boolean shouldGenerate(Vector3d pos, ChunkTreeNode<FoliageCell> node){ public boolean shouldGenerate(Vector3d pos, ChunkTreeNode<FoliageCell> node){
return return
node.getLevel() == ChunkTree.MAX_LEVEL &&
node.isLeaf() && node.isLeaf() &&
node.getData() != null && node.getData() != null &&
node.getData().containedEntities.size() < 1 && node.getData().containedEntities.size() < 1 &&
this.getMinDistance(pos, node) <= FULL_RES_DIST; (
(
node.getLevel() == ChunkTree.MAX_LEVEL
// &&
// this.getMinDistance(pos, node) <= FULL_RES_DIST
)
||
(
node.getLevel() == ChunkTree.MAX_LEVEL - 1
&&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
)
;
} }
/** /**
@ -262,7 +249,8 @@ public class FoliageChunk {
public boolean shouldDestroy(ChunkTreeNode<FoliageCell> node){ public boolean shouldDestroy(ChunkTreeNode<FoliageCell> node){
return return
node.getData() != null && node.getData() != null &&
node.getData().containedEntities.size() > 0; node.getData().containedEntities.size() > 0
;
} }
/** /**

View File

@ -3,7 +3,6 @@ package electrosphere.client.terrain.cells;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3i; import org.joml.Vector3i;
import org.ode4j.ode.DBody;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.collision.CollisionEngine; import electrosphere.collision.CollisionEngine;
@ -36,9 +35,6 @@ public class DrawCell {
//the main entity for the cell //the main entity for the cell
Entity modelEntity; Entity modelEntity;
//the physics mesh
DBody physicsObject;
//Allocated once instead of continuously, used to generate the visual/physics models //Allocated once instead of continuously, used to generate the visual/physics models
float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE];

View File

@ -490,6 +490,20 @@ public class DrawCellManager {
atlas = voxelTextureAtlas; atlas = voxelTextureAtlas;
} }
/**
* Checks if the draw cell at the given world position has generated physics
* @param worldPos The world position
* @return true if has generated physics, false otherwise
*/
public boolean generatedPhysics(Vector3i worldPos){
String key = this.getCellKey(worldPos.x, worldPos.y, worldPos.z);
if(!keyCellMap.containsKey(key)){
return false;
}
DrawCell cell = this.keyCellMap.get(key);
return cell.modelEntity != null;
}
/** /**
transvoxel algorithm spacing management transvoxel algorithm spacing management

View File

@ -0,0 +1,105 @@
package electrosphere.util.math;
import org.joml.Vector3d;
/**
* Utilities for dealing with geometry
*/
public class GeomUtils {
/**
* Gets the minimum 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 getMinDistanceAABB(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.distance(pos.x,pos.y,minZ);
} else {
return pos.distance(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.distance(pos.x,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(pos.x,minY,minZ);
} else {
return pos.distance(pos.x,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distance(pos.x,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(pos.x,maxY,minZ);
} else {
return pos.distance(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.distance(minX,pos.y,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(minX,pos.y,minZ);
} else {
return pos.distance(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.distance(minX,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(minX,minY,minZ);
} else {
return pos.distance(minX,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distance(minX,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(minX,maxY,minZ);
} else {
return pos.distance(minX,maxY,maxZ);
}
}
} else {
if(pos.y > minY && pos.y < maxY){
if(pos.z > minZ && pos.z < maxZ){
return pos.distance(maxX,pos.y,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(maxX,pos.y,minZ);
} else {
return pos.distance(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.distance(maxX,minY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(maxX,minY,minZ);
} else {
return pos.distance(maxX,minY,maxZ);
}
} else {
if(pos.z > minZ && pos.z < maxZ){
return pos.distance(maxX,maxY,pos.z);
} else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){
return pos.distance(maxX,maxY,minZ);
} else {
return pos.distance(maxX,maxY,maxZ);
}
}
}
}
}

View File

@ -0,0 +1,44 @@
package electrosphere.util.math;
import static org.junit.jupiter.api.Assertions.*;
import org.joml.Vector3d;
import electrosphere.test.annotations.UnitTest;
/**
* Tests for geometry utils
*/
public class GeomUtilsTests {
@UnitTest
public void test_getMinDistanceAABB_center_0(){
assertEquals(0, GeomUtils.getMinDistanceAABB(new Vector3d(1,1,1), new Vector3d(0,0,0), new Vector3d(2,2,2)));
}
@UnitTest
public void test_getMinDistanceAABB_xSide_1(){
assertEquals(1, GeomUtils.getMinDistanceAABB(new Vector3d(3,0,0), new Vector3d(0,0,0), new Vector3d(2,2,2)));
}
@UnitTest
public void test_getMinDistanceAABB_ySide_1(){
assertEquals(1, GeomUtils.getMinDistanceAABB(new Vector3d(0,3,0), new Vector3d(0,0,0), new Vector3d(2,2,2)));
}
@UnitTest
public void test_getMinDistanceAABB_zSide_1(){
assertEquals(1, GeomUtils.getMinDistanceAABB(new Vector3d(0,0,3), new Vector3d(0,0,0), new Vector3d(2,2,2)));
}
@UnitTest
public void test_getMinDistanceAABB_angle_1(){
assertEquals(Math.sqrt(2), GeomUtils.getMinDistanceAABB(new Vector3d(3,3,1), new Vector3d(0,0,0), new Vector3d(2,2,2)));
}
@UnitTest
public void test_getMinDistanceAABB_angle_2(){
assertEquals(Math.sqrt(3), GeomUtils.getMinDistanceAABB(new Vector3d(3,3,3), new Vector3d(0,0,0), new Vector3d(2,2,2)));
}
}