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
#Thu Mar 27 17:44:45 EDT 2025
buildNumber=607
#Thu Mar 27 18:17:28 EDT 2025
buildNumber=608

View File

@ -1343,6 +1343,7 @@ Increase number of invalid openAL IDs
Fix audio engine not cleaning up audio sources
More accurate block test types
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();
}
}
}