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
/lwjglx-debug-*.jar
/hs_err_pid*
/replay_pid*
/trace.log
.classpath

View File

@ -128,7 +128,10 @@ public class ClientFoliageManager {
* @return true if all are ready, false otherwise
*/
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;
//position of the foliage cell in world coordinates
/**
* Position of the foliage cell in world coordinates
*/
protected Vector3i worldPosition;
//position of the foliage cell in local coordinates
/**
* Position of the foliage cell in local coordinates
*/
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;
//constituent entities
/**
* Constituent entities
*/
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
@ -157,12 +165,11 @@ public class FoliageCell {
* @param worldPos The position of the foliage cell in world 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.voxelPosition = voxelPos;
this.realPosition = realPos;
this.scale = scale;
this.density = density;
this.containedEntities = new HashSet<Entity>();
}
@ -198,34 +205,94 @@ public class FoliageCell {
boolean shouldGenerate = false;
//get foliage types supported
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition);
List<String> foliageTypesSupported = null;
if(data != null && voxelPosition.y + 1 < ServerTerrainChunk.CHUNK_DIMENSION){
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 < 2){
shouldGenerate = true;
}
if(data == null){
return;
}
if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){
return;
}
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){
//get type
String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size());
FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName);
//create cell and buffer
int FINAL_SPACING = (int)(TARGET_FOLIAGE_SPACING * density);
ByteBuffer buffer = BufferUtils.createByteBuffer(FINAL_SPACING * FINAL_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
ByteBuffer buffer = BufferUtils.createByteBuffer(TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
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
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_01 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).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_10 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).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_12 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).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_21 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPosition).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_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(voxelRealPos).add(-0.5,SAMPLE_START_HEIGHT, 0), 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(voxelRealPos).add( 0,SAMPLE_START_HEIGHT,-0.5), 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(voxelRealPos).add( 0,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(voxelRealPos).add( 0.5,SAMPLE_START_HEIGHT, 0), 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
float height_11 = (float)(sample_11 != null ? sample_11.y : 0);
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(sample_11 != null){
//generate positions to place
int drawCount = 0;
for(int x = 0; x < FINAL_SPACING; x++){
for(int z = 0; z < FINAL_SPACING; z++){
for(int x = 0; x < TARGET_FOLIAGE_SPACING; x=x+scale){
for(int z = 0; z < TARGET_FOLIAGE_SPACING; z=z+scale){
//get position to place
double rand1 = placementRandomizer.nextDouble();
double rand2 = placementRandomizer.nextDouble();
double relativePositionOnGridX = x / (1.0 * FINAL_SPACING) + rand1 / FINAL_SPACING;
double relativePositionOnGridZ = z / (1.0 * FINAL_SPACING) + rand2 / FINAL_SPACING;
double relativePositionOnGridX = x / (1.0 * TARGET_FOLIAGE_SPACING) + rand1 / TARGET_FOLIAGE_SPACING;
double relativePositionOnGridZ = z / (1.0 * TARGET_FOLIAGE_SPACING) + rand2 / TARGET_FOLIAGE_SPACING;
double offsetX = relativePositionOnGridX - 0.5;
double offsetZ = relativePositionOnGridZ - 0.5;
//determine quadrant we're placing in
@ -320,32 +386,18 @@ public class FoliageCell {
offsetY = offsetY - realPosition.y;
double rotVar = placementRandomizer.nextDouble() * Math.PI * 2;
double rotVar2 = placementRandomizer.nextDouble();
floatBufferView.put((float)offsetX);
floatBufferView.put((float)offsetY);
floatBufferView.put((float)offsetZ);
floatBufferView.put((float)offsetX + vX);
floatBufferView.put((float)offsetY + vY);
floatBufferView.put((float)offsetZ + vZ);
floatBufferView.put((float)rotVar);
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();
// 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)));
@ -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;
import java.util.List;
import java.util.LinkedList;
import org.joml.Vector3d;
@ -10,6 +11,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils;
import electrosphere.util.ds.octree.ChunkTree;
import electrosphere.util.ds.octree.ChunkTree.ChunkTreeNode;
import electrosphere.util.math.GeomUtils;
/**
* A whole chunk of foliage
@ -25,7 +27,12 @@ public class FoliageChunk {
/**
* 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
@ -68,6 +75,7 @@ public class FoliageChunk {
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));
this.updateCells();
Globals.profiler.endCpuSample();
}
@ -78,47 +86,13 @@ public class FoliageChunk {
Globals.profiler.beginCpuSample("FoliageChunk.updateCells");
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
//the sets to iterate through
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
this.recursivelyUpdateCells(rootNode, playerPos);
//split into nodes
// 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());
// }
// }
// 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();
boolean updated = true;
int attempts = 0;
while(updated && attempts < 3){
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
updated = this.recursivelyUpdateCells(rootNode, playerPos);
attempts++;
}
Globals.profiler.endCpuSample();
}
@ -127,7 +101,8 @@ public class FoliageChunk {
* @param node The root node
* @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)){
//perform op
ChunkTreeNode<FoliageCell> container = chunkTree.split(node);
@ -142,8 +117,9 @@ public class FoliageChunk {
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));
child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel()));
});
updated = true;
} else if(this.shouldJoin(playerPos, node)) {
//perform op
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node);
@ -152,12 +128,19 @@ public class FoliageChunk {
this.recursivelyDestroy(node);
//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)){
node.getData().generate();
updated = true;
} 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){
Vector3i min = node.getMinBound();
Vector3i max = node.getMaxBound();
double minX = min.x + realPos.x;
double minY = min.y + realPos.y;
double minZ = min.z + realPos.z;
double maxX = max.x + realPos.x;
double maxY = max.y + realPos.y;
double maxZ = max.z + realPos.z;
if(Math.abs(pos.x - minX) < Math.abs(pos.x - maxX)){
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);
}
}
}
return GeomUtils.getMinDistanceAABB(pos, new Vector3d(minX,minY,minZ), new Vector3d(maxX,maxY,maxZ));
}
/**
@ -217,9 +174,18 @@ public class FoliageChunk {
//to combine fullres nodes into larger nodes to conserve on draw calls
return
node.isLeaf() &&
node.getLevel() < ChunkTree.MAX_LEVEL &&
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
node.getLevel() > 0 &&
!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){
return
node.getLevel() == ChunkTree.MAX_LEVEL &&
node.isLeaf() &&
node.getData() != null &&
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){
return
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.Vector3d;
import org.joml.Vector3i;
import org.ode4j.ode.DBody;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.collision.CollisionEngine;
@ -36,9 +35,6 @@ public class DrawCell {
//the main entity for the cell
Entity modelEntity;
//the physics mesh
DBody physicsObject;
//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];

View File

@ -490,6 +490,20 @@ public class DrawCellManager {
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

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)));
}
}