From 5e06dbe786f9241e0768960d799afb128f134911 Mon Sep 17 00:00:00 2001 From: austin Date: Wed, 11 Sep 2024 20:54:04 -0400 Subject: [PATCH] scaled foliage cell generation --- .gitignore | 1 + .../foliagemanager/ClientFoliageManager.java | 5 +- .../client/foliagemanager/FoliageCell.java | 173 +++++++++++------- .../client/foliagemanager/FoliageChunk.java | 150 +++++++-------- .../client/terrain/cells/DrawCell.java | 4 - .../client/terrain/cells/DrawCellManager.java | 14 ++ .../electrosphere/util/math/GeomUtils.java | 105 +++++++++++ .../util/math/GeomUtilsTests.java | 44 +++++ 8 files changed, 343 insertions(+), 153 deletions(-) create mode 100644 src/main/java/electrosphere/util/math/GeomUtils.java create mode 100644 src/test/java/electrosphere/util/math/GeomUtilsTests.java diff --git a/.gitignore b/.gitignore index 8a453a27..2dc2c924 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ /NetArranger-*.jar /lwjglx-debug-*.jar /hs_err_pid* +/replay_pid* /trace.log .classpath diff --git a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java index 55e61a59..c190419d 100644 --- a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java +++ b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java @@ -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 + ; } /** diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index 49a40a4f..2166d310 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -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 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(); } @@ -198,34 +205,94 @@ public class FoliageCell { boolean shouldGenerate = false; //get foliage types supported ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition); - List 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 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 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; - } } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java index 80e476e1..64ff4657 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java @@ -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 rootNode = this.chunkTree.getRoot(); - this.recursivelyUpdateCells(rootNode, playerPos); - //split into nodes - // Globals.profiler.beginCpuSample("FoliageChunk.updateCells - evaluate split/join"); - // while(openSet.size() > 0){ - // ChunkTreeNode current = openSet.remove(0); - - // if(this.shouldSplit(playerPos, current)){ - // //add children for this one - // ChunkTreeNode container = chunkTree.split(current); - // toInit.addAll(container.getChildren()); - // toCleanup.add(current); - // } else if(this.shouldJoin(playerPos, current)) { - // //add children for this one - // ChunkTreeNode 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 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 current : toCleanup){ - // if(current.getData() != null){ - // current.getData().destroy(); - // } - // } - // Globals.profiler.endCpuSample(); + boolean updated = true; + int attempts = 0; + while(updated && attempts < 3){ + ChunkTreeNode 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 node, Vector3d playerPos){ + private boolean recursivelyUpdateCells(ChunkTreeNode node, Vector3d playerPos){ + boolean updated = false; if(this.shouldSplit(playerPos, node)){ //perform op ChunkTreeNode 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 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> children = new LinkedList>(node.getChildren()); + for(ChunkTreeNode 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 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 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 node){ return node.getData() != null && - node.getData().containedEntities.size() > 0; + node.getData().containedEntities.size() > 0 + ; } /** diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 26a3ed6d..e9903dc1 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -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]; diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java index 5ee9b063..617b0bea 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java @@ -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 diff --git a/src/main/java/electrosphere/util/math/GeomUtils.java b/src/main/java/electrosphere/util/math/GeomUtils.java new file mode 100644 index 00000000..0e15e278 --- /dev/null +++ b/src/main/java/electrosphere/util/math/GeomUtils.java @@ -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); + } + } + } + } + +} diff --git a/src/test/java/electrosphere/util/math/GeomUtilsTests.java b/src/test/java/electrosphere/util/math/GeomUtilsTests.java new file mode 100644 index 00000000..0f4a9dd6 --- /dev/null +++ b/src/test/java/electrosphere/util/math/GeomUtilsTests.java @@ -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))); + } + +}