remove deprecated foliage manager
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-03-27 18:18:40 -04:00
parent 097c23ef28
commit d7329210b0
5 changed files with 3 additions and 1127 deletions

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Thu Mar 27 17:44:45 EDT 2025 #Thu Mar 27 18:17:28 EDT 2025
buildNumber=607 buildNumber=608

View File

@ -1343,6 +1343,7 @@ Increase number of invalid openAL IDs
Fix audio engine not cleaning up audio sources Fix audio engine not cleaning up audio sources
More accurate block test types More accurate block test types
Block meshgen work Block meshgen work
Delete deprecated foliage manager

View File

@ -1,165 +0,0 @@
package electrosphere.client.foliagemanager;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3i;
import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils;
@Deprecated
/**
* Manages ambient foliage (grass, small plants, etc) that should be shown, typically instanced
*/
public class ClientFoliageManager {
//Used to prevent concurrent usage of grassEntities set
boolean ready = false;
/**
* The target density for placing foliage
*/
float targetDensity = 1.0f;
/**
* Number of chunks to check
*/
int chunkRadius = 2;
/**
* The list of all chunks with foliage, currently
*/
List<FoliageChunk> chunks = null;
/**
* Cache used to transfer still-valid chunks to the next frame
*/
List<FoliageChunk> chunkUpdateCache = null;
/**
* Starts up the foliage manager
*/
public void start(){
FoliageCell.init();
chunks = new LinkedList<FoliageChunk>();
chunkUpdateCache = new LinkedList<FoliageChunk>();
ready = true;
}
/**
* Updates all grass entities
*/
public void update(){
Globals.profiler.beginCpuSample("ClientFoliageManager.update");
if(ready && Globals.userSettings.getGraphicsPerformanceEnableFoliageManager() && this.dependenciesAreReady()){
this.flipUpdateCache();
for(int x = -chunkRadius; x < chunkRadius+1; x++){
for(int y = -chunkRadius; y < chunkRadius+1; y++){
for(int z = -chunkRadius; z < chunkRadius+1; z++){
Vector3i worldPos = Globals.clientWorldData.convertRealToWorldSpace(EntityUtils.getPosition(Globals.playerEntity)).add(x,y,z);
if(Globals.clientWorldData.worldPosInBounds(worldPos)){
this.updatePosition(worldPos);
}
}
}
}
Globals.profiler.beginCpuSample("ClientFoliageManager.update - destroy chunks");
for(FoliageChunk chunk : this.chunkUpdateCache){
chunk.destroy();
}
this.chunkUpdateCache.clear();
Globals.profiler.endCpuSample();
}
Globals.profiler.endCpuSample();
}
/**
* Evaluates a position in the world
* @param worldPos The world position
*/
private void updatePosition(Vector3i worldPos){
Globals.profiler.beginCpuSample("ClientFoliageManager.updatePosition");
FoliageChunk foundChunk = null;
for(FoliageChunk chunk : chunkUpdateCache){
if(chunk.getWorldPos().equals(worldPos)){
this.chunks.add(chunk);
foundChunk = chunk;
break;
}
}
if(foundChunk == null){
foundChunk = new FoliageChunk(worldPos);
this.chunks.add(foundChunk);
foundChunk.initCells();
} else {
chunkUpdateCache.remove(foundChunk);
}
foundChunk.updateCells();
Globals.profiler.endCpuSample();
}
/**
* Flips references for the chunk list and chunk update cache
*/
private void flipUpdateCache(){
List<FoliageChunk> list1 = this.chunks;
this.chunks = this.chunkUpdateCache;
this.chunkUpdateCache = list1;
if(this.chunks.size() > 0){
throw new IllegalStateException("Update cache should have be empty and it isn't!");
}
}
/**
* Gets a key for a foliage cell in the localCellMap
* @param worldPosition The world position of the cell
* @param voxelPosition The voxel position of the cell
* @return The key for the cell
*/
protected static String getFoliageCellKey(Vector3i worldPosition, Vector3i voxelPosition){
return worldPosition.x + "_" + worldPosition.y + "_" + worldPosition.z + "_" + voxelPosition.x + "_" + voxelPosition.y + "_" + voxelPosition.z;
}
/**
* Checks that all dependencies of this manager are ready
* @return true if all are ready, false otherwise
*/
public boolean dependenciesAreReady(){
return
Globals.clientWorldData != null &&
Globals.clientDrawCellManager != null &&
Globals.playerEntity != null
;
}
/**
* Evaluates the foliage chunk at the position
* @param worldPos The position
*/
public void evaluateChunk(Vector3i worldPos){
for(FoliageChunk chunk : chunkUpdateCache){
if(chunk.getWorldPos().equals(worldPos)){
chunk.updateCells();
break;
}
}
}
/**
* Draws all foliage in the foliage manager
*/
public void draw(){
for(FoliageChunk chunk : chunks){
chunk.draw();
}
}
}

View File

@ -1,602 +0,0 @@
package electrosphere.client.foliagemanager;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Sphered;
import org.joml.Vector3d;
import org.joml.Vector3i;
import org.lwjgl.BufferUtils;
import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.foliage.AmbientFoliage;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.actor.instance.TextureInstancedActor;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.texture.Texture;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
@Deprecated
/**
* Contains a set of foliage entities and groups them together.
*/
public class FoliageCell {
/**
* the interval to space along
*/
static final int TARGET_FOLIAGE_SPACING = 50;
/**
* The target number of foliage to place per cell
*/
static final int TARGET_FOLIAGE_PER_CELL = TARGET_FOLIAGE_SPACING * TARGET_FOLIAGE_SPACING;
/**
* the length of the ray to ground test with
*/
static final float RAY_LENGTH = 1.0f;
/**
* the height above the chunk to start from when sampling downwards
*/
static final float SAMPLE_START_HEIGHT = 0.5f;
/**
* The ID of the air voxel
*/
static final int AIR_VOXEL_ID = 0;
/**
* <p>
* Size of a single item of foliage in the texture buffer
* </p>
* A lot of these are x 4 to account for size of float
* 3 x 4 for position
* 2 x 4 for euler rotation
*
*
* eventually:
* grass type
* color
* wind characteristics?
*/
static final int SINGLE_FOLIAGE_DATA_SIZE_BYTES = 3 * 4 + 2 * 4;
/**
* The map of all attributes for instanced foliage
*/
static final Map<ShaderAttribute,HomogenousBufferTypes> attributes = new HashMap<ShaderAttribute,HomogenousBufferTypes>();
/**
* model matrix shader attribute
*/
static ShaderAttribute modelMatrixAttribute;
/**
* The list of voxel type ids that should have grass generated on top of them
*/
static final List<Integer> grassGeneratingVoxelIds = new ArrayList<Integer>();
//set attributes
static {
int[] attributeIndices = new int[]{
5,6,7,8
};
modelMatrixAttribute = new ShaderAttribute(attributeIndices);
attributes.put(modelMatrixAttribute,HomogenousBufferTypes.MAT4F);
//set grass generating voxel ids
grassGeneratingVoxelIds.add(2);
}
/**
* Vertex shader path
*/
static final String vertexPath = "Shaders/entities/foliage/foliage.vs";
/**
* fragment shader path
*/
static final String fragmentPath = "Shaders/entities/foliage/foliage.fs";
/**
* Random for finding new positions for foliage
*/
Random placementRandomizer = new Random();
/**
* The scale of the chunk
*/
int scale;
/**
* Position of the foliage cell in world coordinates
*/
protected Vector3i worldPosition;
/**
* 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
*/
protected Vector3d realPosition;
/**
* 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);
/**
* Tracks whether the cell has generated or not
*/
boolean hasGenerated = false;
/**
* Tracks whether this cell should be evaluated or not
*/
boolean shouldEvaluate = true;
/**
* Inits the foliage cell data
*/
static void init(){
//queue ambient foliage models
for(FoliageType foliageType : Globals.gameConfigCurrent.getFoliageMap().getFoliageList()){
if(foliageType.getTokens().contains(FoliageType.TOKEN_AMBIENT)){
Globals.assetManager.addModelPathToQueue(foliageType.getGraphicsTemplate().getModel().getPath());
Globals.assetManager.addShaderToQueue(vertexPath, fragmentPath);
}
}
}
/**
* 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, int scale){
this.worldPosition = worldPos;
this.voxelPosition = voxelPos;
this.realPosition = realPos;
this.scale = scale;
this.containedEntities = new HashSet<Entity>();
}
/**
* Adds an entity to this foliage cell
* @param entity The entity to add
*/
protected void addEntity(Entity entity){
containedEntities.add(entity);
}
/**
* Clears all entities in this foliage cell
*/
protected void clearEntities(){
containedEntities.clear();
}
/**
* Destroys all entities in this foliage cell
*/
protected void destroy(){
for(Entity entity : containedEntities){
ClientEntityUtils.destroyEntity(entity);
}
clearEntities();
}
/**
* Generates the foliage cell
*/
protected void generate(){
boolean shouldGenerate = false;
if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){
return;
}
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition,ChunkData.NO_STRIDE);
if(data == null){
return;
}
if(!Globals.clientDrawCellManager.isFullLOD(worldPosition)){
return;
}
//get foliage types supported
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 && foliageTypesSupported.size() > 0 && 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
ByteBuffer buffer = BufferUtils.createByteBuffer(TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES);
if(buffer.capacity() < TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES){
LoggerInterface.loggerEngine.WARNING("Failed to allocate data for foliage cell! " + buffer.limit());
}
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 + this.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.getGraphicsTemplate().getModel().getPath(), 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);
}
}
this.shouldEvaluate = false;
this.hasGenerated = true;
}
/**
* 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) == AIR_VOXEL_ID;
if(foliageTypesSupported != null && airAbove){
shouldGenerate = true;
}
}
if(shouldGenerate){
//construct simple grid to place foliage on
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);
float height_01 = (float)(sample_01 != null ? sample_01.y : height_11);
float height_02 = (float)(sample_02 != null ? sample_02.y : height_11);
float height_10 = (float)(sample_10 != null ? sample_10.y : height_11);
float height_12 = (float)(sample_12 != null ? sample_12.y : height_11);
float height_20 = (float)(sample_20 != null ? sample_20.y : height_11);
float height_21 = (float)(sample_21 != null ? sample_21.y : height_11);
float height_22 = (float)(sample_22 != null ? sample_22.y : height_11);
//each height is in real world coordinates that are absolute
//when rendering, there's already a y offset for the center of the field of grass (based on the model matrix)
//so when offseting the position of the blade of grass RELATIVE to the overall instance being drawn, need to subtract the real world coordinates of the overall instance
//in other words realPos SPECIFICALLY for the y dimension, for x and z you don't need to worry about it
//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
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 * 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
double offsetY = 0;
boolean addBlade = false;
if(relativePositionOnGridX >=0.5){
if(relativePositionOnGridZ >= 0.5){
relativePositionOnGridX = relativePositionOnGridX - 0.5;
relativePositionOnGridZ = relativePositionOnGridZ - 0.5;
relativePositionOnGridX /= 0.5;
relativePositionOnGridZ /= 0.5;
// System.out.println(relativePositionOnGridX + " " + relativePositionOnGridZ);
//if we have heights for all four surrounding spots, interpolate for y value
if(sample_11 != null && sample_12 != null && sample_21 != null && sample_22 != null){
offsetY =
height_11 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_12 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
height_21 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_22 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
addBlade = true;
}
} else {
relativePositionOnGridX = relativePositionOnGridX - 0.5;
relativePositionOnGridX /= 0.5;
relativePositionOnGridZ /= 0.5;
//if we have heights for all four surrounding spots, interpolate for y value
if(sample_10 != null && sample_11 != null && sample_20 != null && sample_21 != null){
offsetY =
height_10 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_11 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
height_20 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_21 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
addBlade = true;
}
}
} else {
if(relativePositionOnGridZ >= 0.5){
relativePositionOnGridZ = relativePositionOnGridZ - 0.5;
relativePositionOnGridX /= 0.5;
relativePositionOnGridZ /= 0.5;
//if we have heights for all four surrounding spots, interpolate for y value
if(sample_01 != null && sample_02 != null && sample_11 != null && sample_12 != null){
offsetY =
height_01 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_02 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
height_11 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_12 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
addBlade = true;
}
} else {
relativePositionOnGridX /= 0.5;
relativePositionOnGridZ /= 0.5;
//if we have heights for all four surrounding spots, interpolate for y value
if(sample_00 != null && sample_01 != null && sample_10 != null && sample_11 != null){
offsetY =
height_00 * (1-relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_01 * (1-relativePositionOnGridX) * ( relativePositionOnGridZ) +
height_10 * ( relativePositionOnGridX) * (1-relativePositionOnGridZ) +
height_11 * ( relativePositionOnGridX) * ( relativePositionOnGridZ);
addBlade = true;
}
}
}
if(addBlade){
//convert y to relative to chunk
offsetY = offsetY - realPosition.y;
double rotVar = placementRandomizer.nextDouble() * Math.PI * 2;
double rotVar2 = placementRandomizer.nextDouble();
if(floatBufferView.limit() >= floatBufferView.position() + 4){
floatBufferView.put((float)offsetX + vX);
floatBufferView.put((float)offsetY + vY);
floatBufferView.put((float)offsetZ + vZ);
floatBufferView.put((float)rotVar);
floatBufferView.put((float)rotVar2);
rVal++;
}
}
}
}
}
}
return rVal;
}
/**
* Gets a good position to put a new blade of grass
* @param centerPosition The player's position
* @return The new position for the blade of grass
*/
protected Vector3d getNewPosition(Vector3d centerPosition){
double angle = placementRandomizer.nextDouble() * Math.PI * 2;
double radius = placementRandomizer.nextDouble();
return new Vector3d(
centerPosition.x + Math.cos(angle) * radius,
centerPosition.y,
centerPosition.z + Math.sin(angle) * radius
);
}
/**
* Gets a new rotation for a blade of grass
* @return The rotation
*/
protected Quaterniond getNewRotation(){
return new Quaterniond().rotationX(-Math.PI / 2.0f).rotateLocalY(Math.PI * placementRandomizer.nextFloat()).normalize();
}
/**
* Draws all entities in the foliage cell
*/
protected void draw(){
if(this.containedEntities.size() > 0){
Matrix4d modelMatrix = new Matrix4d();
Vector3d cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
RenderPipelineState renderPipelineState = Globals.renderingEngine.getRenderPipelineState();
OpenGLState openGLState = Globals.renderingEngine.getOpenGLState();
Vector3d cameraModifiedPosition = new Vector3d(realPosition).sub(cameraCenter);
//frustum check entire cell
boolean shouldRender = renderPipelineState.getFrustumIntersection().testSphere((float)(cameraModifiedPosition.x + boundingSphere.x), (float)(cameraModifiedPosition.y + boundingSphere.y), (float)(cameraModifiedPosition.z + boundingSphere.z), (float)(boundingSphere.r));
if(shouldRender){
//disable frustum check and instead perform at cell level
boolean currentFrustumCheckState = renderPipelineState.shouldFrustumCheck();
renderPipelineState.setFrustumCheck(false);
for(Entity entity : containedEntities){
Vector3d grassPosition = EntityUtils.getPosition(entity);
Quaterniond grassRotation = EntityUtils.getRotation(entity);
TextureInstancedActor actor = TextureInstancedActor.getTextureInstancedActor(entity);
modelMatrix = modelMatrix.identity();
modelMatrix.translate(cameraModifiedPosition);
modelMatrix.rotate(new Quaterniond(grassRotation));
modelMatrix.scale(new Vector3d(EntityUtils.getScale(entity)));
actor.applySpatialData(modelMatrix,grassPosition);
//draw
actor.draw(renderPipelineState, openGLState);
}
renderPipelineState.setFrustumCheck(currentFrustumCheckState);
}
}
}
/**
* Gets whether the cell has generated or not
* @return true if has generated, false otherwise
*/
public boolean hasGenerated(){
return this.hasGenerated;
}
/**
* Checks if the cell should be continuously evaluated
* @return true if should be evaluated, false otherwise
*/
public boolean shouldEvaluate() {
return shouldEvaluate;
}
/**
* Sets if the cell should be continuously evaluated
* @param shouldEvaluate true if should be evaluated, false otherwise
*/
public void setShouldEvaluate(boolean shouldEvaluate) {
this.shouldEvaluate = shouldEvaluate;
}
/**
* SCAFFOLDING FOR BUILDING SCALE>1 CELLS AND ALSO FOR TOP LEVEL CELL CHECKING
*/
/**
* Evaluates a chunk to see where foliage cells should be created or updated
* @param worldPos The world position of the chunk
*/
// public void evaluateChunk(Vector3i worldPos){
// ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
// for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
// //can't go to very top 'cause otherwise there would be no room to put grass
// for(int y = 0; y < ChunkData.CHUNK_SIZE - 1; y++){
// for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
// Vector3i currentPos = new Vector3i(x,y,z);
// String key = getFoliageCellKey(worldPos, currentPos);
// if(locationCellMap.get(key) != null){
// //destroy if there's no longer ground or
// //if the cell above is now occupied or
// //if the lower cell is no longer supporting foliage
// if(
// data.getWeight(currentPos) <= 0 ||
// data.getWeight(new Vector3i(x,y + 1,z)) > 0 ||
// !typeSupportsFoliage(data.getType(currentPos))
// ){
// //destroy
// FoliageCell toDestroy = locationCellMap.get(key);
// toDestroy.destroy();
// activeCells.remove(toDestroy);
// locationCellMap.remove(key);
// } else {
// //TODO: evaluate if foliage is placed well
// }
// } else {
// //create if current is ground and above is air
// if(
// !locationEvaluationCooldownMap.containsKey(key) &&
// data.getWeight(currentPos) > 0 &&
// data.getWeight(new Vector3i(x,y + 1,z)) < 0 &&
// typeSupportsFoliage(data.getType(currentPos)) &&
// activeCells.size() < CELL_COUNT_MAX
// ){
// //create foliage cell
// createFoliageCell(worldPos,currentPos,1,targetDensity);
// }
// }
// }
// }
// }
// //evaluate top cells if chunk above this one exists
// ChunkData aboveData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0));
// if(aboveData != null){
// for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
// for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
// Vector3i currentPos = new Vector3i(x,ChunkData.CHUNK_SIZE-1,z);
// String key = getFoliageCellKey(worldPos, currentPos);
// if(locationCellMap.get(key) != null){
// //destroy if there's no longer ground or
// //if the cell above is now occupied or
// //if the lower cell is no longer supporting foliage
// if(
// data.getWeight(currentPos) <= 0 ||
// aboveData.getWeight(new Vector3i(x,0,z)) > 0 ||
// !typeSupportsFoliage(data.getType(currentPos))
// ){
// //destroy
// FoliageCell toDestroy = locationCellMap.get(key);
// toDestroy.destroy();
// activeCells.remove(toDestroy);
// locationCellMap.remove(key);
// } else {
// //TODO: evaluate if foliage is placed well
// }
// } else {
// //create if current is ground and above is air
// if(
// data.getWeight(currentPos) > 0 &&
// aboveData.getWeight(new Vector3i(x,0,z)) < 0 &&
// typeSupportsFoliage(data.getType(currentPos)) &&
// activeCells.size() < CELL_COUNT_MAX
// ){
// //create foliage cell
// createFoliageCell(worldPos,currentPos,1,targetDensity);
// }
// }
// }
// }
// }
// }
}

View File

@ -1,358 +0,0 @@
package electrosphere.client.foliagemanager;
import java.util.List;
import java.util.LinkedList;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.client.terrain.cache.ChunkData;
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;
@Deprecated
/**
* A whole chunk of foliage
*/
public class FoliageChunk {
/**
* The world position of this chunk
*/
Vector3i worldPos;
Vector3d realPos;
/**
* The distance to draw at full resolution
*/
static final double FULL_RES_DIST = 15;
/**
* The distance for half resolution
*/
static final double HALF_RES_DIST = 30;
/**
* Tracks whether this chunk contains a foliage voxel or not
*/
boolean containsFoliageVoxel = false;
/**
* Tracks whether this foliage chunk has properly initialized or not.
* It is only considered initialized once it has been evaluated with the data present in cache.
*/
boolean initialized = false;
/**
* The octree holding all the chunks to evaluate
*/
ChunkTree<FoliageCell> chunkTree;
/**
* Data for the current chunk
*/
ChunkData currentChunkData;
/**
* Data for the above chunk
*/
ChunkData aboveChunkData;
/**
* Constructor
* @param worldPos The world position of the chunk
*/
public FoliageChunk(Vector3i worldPos){
this.worldPos = worldPos;
this.realPos = Globals.clientWorldData.convertWorldToRealSpace(worldPos);
this.chunkTree = new ChunkTree<FoliageCell>();
}
/**
* Gets the world position of the chunk
* @return The world position of the chunk
*/
public Vector3i getWorldPos(){
return worldPos;
}
/**
* Initializes all cells in the chunk
*/
public void initCells(){
Globals.profiler.beginCpuSample("FoliageChunk.initCells");
this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos,ChunkData.NO_STRIDE);
// //evaluate top cells if chunk above this one exists
this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0),ChunkData.NO_STRIDE);
this.updateCells();
Globals.profiler.endCpuSample();
}
/**
* Updates all cells in the chunk
*/
public void updateCells(){
Globals.profiler.beginCpuSample("FoliageChunk.updateCells");
//re-evaluate whether contains foliage voxel or not
boolean hasData = Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE);
boolean hasPhysics = Globals.clientDrawCellManager.hasGeneratedPhysics(worldPos.x, worldPos.y, worldPos.z);
if(containsFoliageVoxel && hasData && hasPhysics){
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
//the sets to iterate through
boolean updated = true;
int attempts = 0;
while(updated && attempts < 3){
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
updated = this.recursivelyUpdateCells(rootNode, playerPos);
attempts++;
}
}
if(!hasData){
Globals.clientTerrainManager.requestChunk(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE);
}
if(hasData && !this.initialized) {
this.evaluateContainsFoliage();
this.initialized = true;
}
Globals.profiler.endCpuSample();
}
/**
* Checks if the chunk contains a foliage voxel or not
* @return true if contains foliage voxel, false otherwise
*/
private boolean checkContainsFoliageVoxel(){
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(this.getWorldPos(),ChunkData.NO_STRIDE);
if(data == null){
return false;
}
for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){
for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(new Vector3i(x,y,z))).getAmbientFoliage();
if(foliageTypesSupported != null && foliageTypesSupported.size() > 0){
return true;
}
}
}
}
return false;
}
/**
* Instructs the chunk to evaluate whether it has foliage or not
*/
public void evaluateContainsFoliage(){
this.containsFoliageVoxel = this.checkContainsFoliageVoxel();
}
/**
* Recursively update child nodes
* @param node The root node
* @param playerPos The player's position
*/
private boolean recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){
boolean updated = false;
Globals.profiler.beginRecursiveCpuSample("FoliageChunk.recursivelyUpdateCells");
if(this.shouldSplit(playerPos, node)){
Globals.profiler.beginAggregateCpuSample("FoliageChunk.split");
//perform op
ChunkTreeNode<FoliageCell> container = chunkTree.split(node);
//do deletions
this.recursivelyDestroy(node);
//do creations
container.getChildren().forEach(child -> {
Vector3d realPos = new Vector3d(
worldPos.x * ChunkData.CHUNK_DATA_SIZE + child.getMinBound().x,
worldPos.y * ChunkData.CHUNK_DATA_SIZE + child.getMinBound().y,
worldPos.z * ChunkData.CHUNK_DATA_SIZE + child.getMinBound().z
);
child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel()));
});
Globals.profiler.endCpuSample();
updated = true;
} else if(this.shouldJoin(playerPos, node)) {
Globals.profiler.beginAggregateCpuSample("FoliageChunk.join");
//perform op
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node);
//do deletions
this.recursivelyDestroy(node);
//do creations
newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel()));
Globals.profiler.endCpuSample();
updated = true;
} else if(shouldGenerate(playerPos, node)){
Globals.profiler.beginAggregateCpuSample("FoliageChunk.generate");
node.getData().generate();
Globals.profiler.endCpuSample();
updated = true;
} else if(!node.isLeaf()){
List<ChunkTreeNode<FoliageCell>> children = new LinkedList<ChunkTreeNode<FoliageCell>>(node.getChildren());
for(ChunkTreeNode<FoliageCell> child : children){
boolean childUpdate = recursivelyUpdateCells(child, playerPos);
updated = childUpdate || updated;
}
}
Globals.profiler.endCpuSample();
return updated;
}
/**
* Gets the minimum distance from a node to a point
* @param pos the position to check against
* @param node the node
* @return the distance
*/
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;
return GeomUtils.getMinDistanceAABB(pos, new Vector3d(minX,minY,minZ), new Vector3d(maxX,maxY,maxZ));
}
/**
* Gets whether this should be split or not
* @param pos the player position
* @param node The node
* @return true if should split, false otherwise
*/
public boolean shouldSplit(Vector3d pos, ChunkTreeNode<FoliageCell> node){
//breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls
return
node.isLeaf() &&
node.canSplit() &&
(
(
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
)
)
;
}
/**
* Gets whether this should be joined or not
* @param pos the player position
* @param node The node
* @return true if should be joined, false otherwise
*/
public boolean shouldJoin(Vector3d pos, ChunkTreeNode<FoliageCell> node){
//breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls
return
node.getLevel() > 0 &&
!node.isLeaf() &&
(
(
node.getLevel() == ChunkTree.MAX_LEVEL &&
this.getMinDistance(pos, node) > FULL_RES_DIST
)
||
(
this.getMinDistance(pos, node) > HALF_RES_DIST
)
)
;
}
/**
* Checks if this cell should generate
* @param pos the player's position
* @param node the node
* @return true if should generate, false otherwise
*/
public boolean shouldGenerate(Vector3d pos, ChunkTreeNode<FoliageCell> node){
return
node.isLeaf() &&
node.getData() != null &&
!node.getData().hasGenerated() &&
(
(
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
)
)
;
}
/**
* Checks if the node should have destroy called on it
* @param node The node
* @return true if should destroy, false otherwise
*/
public boolean shouldDestroy(ChunkTreeNode<FoliageCell> node){
return
node.getData() != null &&
node.getData().containedEntities.size() > 0
;
}
/**
* Destroys the foliage chunk
*/
protected void destroy(){
this.recursivelyDestroy(this.chunkTree.getRoot());
}
/**
* Recursively destroy a tree
* @param node The root of the tree
*/
private void recursivelyDestroy(ChunkTreeNode<FoliageCell> node){
if(node.getChildren().size() > 0){
node.getChildren().forEach(child -> recursivelyDestroy(child));
}
if(node.getData() != null){
node.getData().destroy();
}
}
/**
* Draws all cells in the chunk
*/
protected void draw(){
recursivelyDraw(this.chunkTree.getRoot());
}
/**
* Recursively draws all nodes
* @param node The root node
*/
private void recursivelyDraw(ChunkTreeNode<FoliageCell> node){
if(node.getChildren().size() > 0){
node.getChildren().forEach(child -> recursivelyDraw(child));
}
if(node.getData() != null){
node.getData().draw();
}
}
}