variable density for foliage cells
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-09-11 12:49:23 -04:00
parent 11d468fe14
commit f234c6423c
3 changed files with 122 additions and 16 deletions

View File

@ -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<FoliageChunk> chunkTree;
//The map of all attributes for instanced foliage
static final Map<ShaderAttribute,HomogenousBufferTypes> attributes = new HashMap<ShaderAttribute,HomogenousBufferTypes>();
@ -126,6 +134,7 @@ public class ClientFoliageManager {
Globals.assetManager.addShaderToQueue(vertexPath, fragmentPath);
}
}
this.chunkTree = new Octree<FoliageChunk>(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<FoliageCell> invalidationList = new LinkedList<FoliageCell>();
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<String> 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<FoliageChunk> chunkNode){
if(chunkNode.isLeaf()){
FoliageChunk chunk = chunkNode.getData();
Vector3d chunkPos = Globals.clientWorldData.convertWorldToRealSpace(chunk.getWorldPos());
} else {
for(OctreeNode<FoliageChunk> 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

View File

@ -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<Entity>();
}

View File

@ -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<Vector3i> currentlyGeneratedCellPositions;
/**
* Constructor
* @param worldPos The world position of the chunk
*/
public FoliageChunk(Vector3i worldPos){
this.worldPos = worldPos;
this.currentlyGeneratedCellPositions = new ArrayList<Vector3i>();
}
/**
* Gets the world position of the chunk
* @return The world position of the chunk
*/
public Vector3i getWorldPos(){
return worldPos;
}
}