Renderer/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java
2023-05-25 18:04:22 -04:00

195 lines
7.0 KiB
Java

package electrosphere.client.foliagemanager;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.joml.Vector3f;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
/**
* Manages foliage (grass, small plants, etc) that should be shown, typically instanced
*/
public class ClientFoliageManager {
//threshold for a grass blade to relocate to a new position
static final float GRASS_RELOCATION_THRESHOLD = 5f;
//amount of grass entities to manage
static final int grassCapacity = 10000;
//Random for finding new positions for foliage
Random placementRandomizer = new Random();
//The list of grass entities currently in use
Set<Entity> grassEntities = new HashSet<Entity>();
//Used to prevent concurrent usage of grassEntities set
boolean ready = false;
//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;
//set attributes
static {
int[] attributeIndices = new int[]{
5,6,7,8
};
modelMatrixAttribute = new ShaderAttribute(attributeIndices);
attributes.put(modelMatrixAttribute,HomogenousBufferTypes.MAT4F);
}
//shader paths
static final String vertexPath = "shaders/foliage/foliage.vs";
static final String fragmentPath = "shaders/foliage/foliage.fs";
/**
* Starts up the foliage manager
*/
public void start(){
//queue grass model
Globals.assetManager.addModelPathToQueue("models/grass1.fbx");
Vector3d centerPosition = new Vector3d(0,0,0);
//create grass entities
Entity grassEntity = EntityCreationUtils.createClientSpatialEntity();
makeEntityInstancedFoliage(grassEntity, "models/grass1.fbx", grassCapacity);
EntityUtils.getPosition(grassEntity).set(getNewPosition(centerPosition));
EntityUtils.getRotation(grassEntity).set(getNewRotation());
EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0));
grassEntities.add(grassEntity);
for(int i = 0; i < grassCapacity - 1; i++){
grassEntity = EntityCreationUtils.createClientSpatialEntity();
makeEntityInstancedFoliage(grassEntity, "models/grass1.fbx", grassCapacity);
EntityUtils.getPosition(grassEntity).set(getNewPosition(centerPosition));
EntityUtils.getRotation(grassEntity).set(getNewRotation());
EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0));
grassEntities.add(grassEntity);
}
ready = true;
}
/**
* Updates all grass entities
*/
public void update(){
if(ready){
Matrix4f modelMatrix = new Matrix4f();
Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
for(Entity grassEntity : grassEntities){
Vector3d grassPosition = EntityUtils.getPosition(grassEntity);
Quaternionf grassRotation = EntityUtils.getRotation(grassEntity);
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity);
InstancedActor instancedActor = InstancedActor.getInstancedActor(grassEntity);
//update position
if(playerPosition.distance(grassPosition) > GRASS_RELOCATION_THRESHOLD){
grassPosition.set(getNewPosition(playerPosition));
grassRotation.set(getNewRotation());
}
//update uniforms
// instancedActor.setUniform("position", grassPosition);
// instancedActor.setUniform("rotation", new Vector4d(
// grassRotation.x,
// grassRotation.y,
// grassRotation.z,
// grassRotation.w));
// instancedActor.setUniform("scale", new Vector3d(EntityUtils.getScale(grassEntity)));
modelMatrix = modelMatrix.identity();
Vector3f cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
modelMatrix.translate(cameraModifiedPosition);
modelMatrix.rotate(grassRotation);
modelMatrix.scale(EntityUtils.getScale(grassEntity));
instancedActor.setAttribute(modelMatrixAttribute, modelMatrix);
//draw
instancedActor.draw(Globals.renderingEngine.getRenderPipelineState());
}
}
}
/**
* 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() * GRASS_RELOCATION_THRESHOLD;
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 Quaternionf getNewRotation(){
return new Quaternionf().rotationX(-(float)Math.PI / 2.0f).rotateLocalY((float)Math.PI * placementRandomizer.nextFloat());
}
/**
* Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor
* @param entity The entity
* @param modelPath The model path for the model to back the instanced actor
*/
public static void makeEntityInstancedFoliage(Entity entity, String modelPath, int capacity){
entity.putData(EntityDataStrings.INSTANCED_ACTOR, Globals.clientInstanceManager.createInstancedActor(modelPath, vertexPath, fragmentPath, attributes, capacity));
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity());
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
Globals.clientScene.registerEntity(entity);
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED);
}
}