diff --git a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java index 961e75ba..417ccb2c 100644 --- a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java +++ b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java @@ -31,6 +31,8 @@ import electrosphere.renderer.actor.instance.TextureInstancedActor; import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; import electrosphere.renderer.texture.Texture; +import electrosphere.util.ds.Octree; +import electrosphere.util.ds.Octree.OctreeNode; /** * Manages ambient foliage (grass, small plants, etc) that should be shown, typically instanced @@ -79,8 +81,14 @@ public class ClientFoliageManager { //The number of frames that must pass before a cell can be reevaluated for foliage placement static final int EVALUATION_COOLDOWN = 100; + float targetDensity = 1.0f; + /** + * The octree holding all the chunks to evaluate + */ + Octree chunkTree; + //The map of all attributes for instanced foliage static final Map attributes = new HashMap(); @@ -126,6 +134,7 @@ public class ClientFoliageManager { Globals.assetManager.addShaderToQueue(vertexPath, fragmentPath); } } + this.chunkTree = new Octree(new Vector3d(0), new Vector3d(1000000000l)); ready = true; } @@ -134,13 +143,26 @@ public class ClientFoliageManager { */ public void update(){ Globals.profiler.beginCpuSample("ClientFoliageManager.update"); - if(ready){ + if(ready && this.dependenciesAreReady()){ Vector3d playerPosition = null; if(Globals.playerEntity != null){ playerPosition = EntityUtils.getPosition(Globals.playerEntity); } - Globals.profiler.beginCpuSample("ClientFoliageManager.update (talley invalid cells)"); + Vector3i worldPos = Globals.clientWorldData.convertRealToWorldSpace(playerPosition); + if(!chunkTree.containsLeaf(new Vector3d(worldPos))){ + this.createFoliageChunk(worldPos); + } + + // + //evaluate what cells should be updated + Globals.profiler.beginCpuSample("ClientFoliageManager.update (evaluate foliage chunks)"); + this.evaluateChunks(playerPosition, this.chunkTree.getRoot()); + Globals.profiler.endCpuSample(); + + + // //flip through all valid cells and see if you can invalidate any + Globals.profiler.beginCpuSample("ClientFoliageManager.update (talley invalid cells)"); List invalidationList = new LinkedList(); for(FoliageCell cell : activeCells){ if( @@ -151,6 +173,9 @@ public class ClientFoliageManager { } } Globals.profiler.endCpuSample(); + + // + //actually invalidate cells Globals.profiler.beginCpuSample("ClientFoliageManager.update (actually invalidate cells)"); for(FoliageCell cell : invalidationList){ invalidateCell(cell); @@ -164,7 +189,7 @@ public class ClientFoliageManager { cooldownTime--; if(cooldownTime <= 0){ String split[] = key.split("_"); - Vector3i worldPos = new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2])); + worldPos = new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2])); Vector3i voxelPos = new Vector3i(Integer.parseInt(split[3]),Integer.parseInt(split[4]),Integer.parseInt(split[5])); Vector3d realPos = Globals.clientWorldData.convertWorldToRealSpace(worldPos).add(voxelPos.x,voxelPos.y,voxelPos.z); ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); @@ -177,7 +202,7 @@ public class ClientFoliageManager { playerPosition.distance(realPos) < CELL_DISTANCE_MAX ){ //create foliage cell - createFoliageCell(worldPos,voxelPos,1); + createFoliageCell(worldPos,voxelPos,1,targetDensity); locationEvaluationCooldownMap.remove(key); } else { locationEvaluationCooldownMap.put(key, EVALUATION_COOLDOWN); @@ -282,7 +307,7 @@ public class ClientFoliageManager { activeCells.size() < CELL_COUNT_MAX ){ //create foliage cell - createFoliageCell(worldPos,currentPos,1); + createFoliageCell(worldPos,currentPos,1,targetDensity); } } } @@ -321,7 +346,7 @@ public class ClientFoliageManager { activeCells.size() < CELL_COUNT_MAX ){ //create foliage cell - createFoliageCell(worldPos,currentPos,1); + createFoliageCell(worldPos,currentPos,1,targetDensity); } } } @@ -340,7 +365,7 @@ public class ClientFoliageManager { * @param worldPos The world position * @param voxelPos The voxel position */ - private void createFoliageCell(Vector3i worldPos, Vector3i voxelPos, float initialGrowthLevel){ + private void createFoliageCell(Vector3i worldPos, Vector3i voxelPos, float initialGrowthLevel, float density){ //get foliage types supported ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); List foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPos)).getAmbientFoliage(); @@ -356,8 +381,9 @@ public class ClientFoliageManager { FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName); //create cell and buffer - FoliageCell cell = new FoliageCell(worldPos, voxelPos, realPos); - ByteBuffer buffer = BufferUtils.createByteBuffer(TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES); + FoliageCell cell = new FoliageCell(worldPos, voxelPos, realPos, density); + int FINAL_SPACING = (int)(TARGET_FOLIAGE_SPACING * density); + ByteBuffer buffer = BufferUtils.createByteBuffer(FINAL_SPACING * FINAL_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES); FloatBuffer floatBufferView = buffer.asFloatBuffer(); //construct simple grid to place foliage on Vector3d sample_00 = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(new Vector3d(realPos).add(-0.5,SAMPLE_START_HEIGHT,-0.5), new Vector3d(0,-1,0), RAY_LENGTH); @@ -388,13 +414,13 @@ public class ClientFoliageManager { if(sample_11 != null){ //generate positions to place int drawCount = 0; - for(int x = 0; x < TARGET_FOLIAGE_SPACING; x++){ - for(int z = 0; z < TARGET_FOLIAGE_SPACING; z++){ + for(int x = 0; x < FINAL_SPACING; x++){ + for(int z = 0; z < FINAL_SPACING; z++){ //get position to place double rand1 = placementRandomizer.nextDouble(); double rand2 = placementRandomizer.nextDouble(); - 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 relativePositionOnGridX = x / (1.0 * FINAL_SPACING) + rand1 / FINAL_SPACING; + double relativePositionOnGridZ = z / (1.0 * FINAL_SPACING) + rand2 / FINAL_SPACING; double offsetX = relativePositionOnGridX - 0.5; double offsetZ = relativePositionOnGridZ - 0.5; //determine quadrant we're placing in @@ -473,9 +499,9 @@ public class ClientFoliageManager { } } buffer.position(0); - buffer.limit(TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING * SINGLE_FOLIAGE_DATA_SIZE_BYTES); + 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,TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING); + Texture dataTexture = new Texture(Globals.renderingEngine.getOpenGLState(),buffer,SINGLE_FOLIAGE_DATA_SIZE_BYTES / 4,FINAL_SPACING * FINAL_SPACING); //create entity Entity grassEntity = EntityCreationUtils.createClientSpatialEntity(); @@ -495,6 +521,40 @@ public class ClientFoliageManager { } } + /** + * Creates a foliage chunk + * @param worldPosition The world position of the chunk + */ + private void createFoliageChunk(Vector3i worldPosition){ + FoliageChunk chunk = new FoliageChunk(worldPosition); + chunkTree.addLeaf(new Vector3d(worldPosition), chunk); + } + + /** + * Evaluates all chunk nodes + * @param chunkNode The node to evaluate + */ + private void evaluateChunks(Vector3d playerPos, OctreeNode chunkNode){ + if(chunkNode.isLeaf()){ + FoliageChunk chunk = chunkNode.getData(); + Vector3d chunkPos = Globals.clientWorldData.convertWorldToRealSpace(chunk.getWorldPos()); + } else { + for(OctreeNode child : chunkNode.getChildren()){ + if(child != null){ + evaluateChunks(playerPos, child); + } + } + } + } + + /** + * Checks that all dependencies of this manager are ready + * @return true if all are ready, false otherwise + */ + public boolean dependenciesAreReady(){ + return Globals.clientWorldData != null; + } + /** * Gets whether the voxel type supports foliage or not * @param type diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index b6d81a35..19695ea1 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -35,15 +35,21 @@ public class FoliageCell { //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 + */ + protected float density; + /** * Constructor * @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){ + protected FoliageCell(Vector3i worldPos, Vector3i voxelPos, Vector3d realPos, float density){ this.worldPosition = worldPos; this.voxelPosition = voxelPos; this.realPosition = realPos; + this.density = density; this.containedEntities = new HashSet(); } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java new file mode 100644 index 00000000..bd80c099 --- /dev/null +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java @@ -0,0 +1,40 @@ +package electrosphere.client.foliagemanager; + +import java.util.ArrayList; +import java.util.List; + +import org.joml.Vector3i; + +/** + * A whole chunk of foliage + */ +public class FoliageChunk { + + /** + * The world position of this chunk + */ + Vector3i worldPos; + + /** + * A list of all cells that are currently generated + */ + List currentlyGeneratedCellPositions; + + /** + * Constructor + * @param worldPos The world position of the chunk + */ + public FoliageChunk(Vector3i worldPos){ + this.worldPos = worldPos; + this.currentlyGeneratedCellPositions = new ArrayList(); + } + + /** + * Gets the world position of the chunk + * @return The world position of the chunk + */ + public Vector3i getWorldPos(){ + return worldPos; + } + +}