Foliage cell concept

This commit is contained in:
austin 2023-06-23 14:09:03 -04:00
parent 3948f1f921
commit 058c6ac3d9
20 changed files with 487 additions and 156 deletions

View File

@ -21,9 +21,15 @@
{ {
"name" : "Green Grass", "name" : "Green Grass",
"tokens" : [ "tokens" : [
"AMBIENT" "AMBIENT",
"REACTS_TO_WIND",
"GROWS_BACK",
"FLAMMABLE"
], ],
"modelPath" : "Models/falloak1.fbx" "growthModel": {
"growthRate" : 0.001
},
"modelPath" : "Models/grass1.fbx"
} }
] ]

View File

@ -12,7 +12,7 @@
"id" : 2, "id" : 2,
"name" : "grass", "name" : "grass",
"ambientFoliage" : [ "ambientFoliage" : [
"Grass" "Green Grass"
] ]
} }
] ]

View File

@ -7,9 +7,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
@ -22,8 +21,8 @@ import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags; import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.camera.CameraEntityUtils; import electrosphere.entity.state.foliage.AmbientFoliage;
import electrosphere.renderer.actor.instance.InstancedActor; import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
@ -40,9 +39,6 @@ public class ClientFoliageManager {
//Random for finding new positions for foliage //Random for finding new positions for foliage
Random placementRandomizer = new Random(); 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 //Used to prevent concurrent usage of grassEntities set
boolean ready = false; boolean ready = false;
@ -58,6 +54,13 @@ public class ClientFoliageManager {
static final float CELL_DISTANCE_MAX = 25f; static final float CELL_DISTANCE_MAX = 25f;
//The maximum number of foliage cells //The maximum number of foliage cells
static final int CELL_COUNT_MAX = 100; static final int CELL_COUNT_MAX = 100;
//The target number of foliage to place per cell
static final int TARGET_FOLIAGE_PER_CELL = 50;
//Stores a list of all locations that are currently invalid which map to
//the amount of frames that must pass before they are considered valid to evaluate
Map<String,Integer> locationEvaluationCooldownMap = new ConcurrentHashMap<String,Integer>();
//The number of frames that must pass before a cell can be reevaluated for foliage placement
static final int EVALUATION_COOLDOWN = 100;
@ -99,24 +102,11 @@ public class ClientFoliageManager {
* Starts up the foliage manager * Starts up the foliage manager
*/ */
public void start(){ public void start(){
//queue grass model //queue ambient foliage models
Globals.assetManager.addModelPathToQueue("models/grass1.fbx"); for(FoliageType foliageType : Globals.gameConfigCurrent.getFoliageMap().getFoliageList()){
if(foliageType.getTokens().contains(FoliageType.TOKEN_AMBIENT)){
Vector3d centerPosition = new Vector3d(0,0,0); Globals.assetManager.addModelPathToQueue(foliageType.getModelPath());
//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; ready = true;
} }
@ -126,29 +116,32 @@ public class ClientFoliageManager {
*/ */
public void update(){ public void update(){
if(ready){ if(ready){
Matrix4d modelMatrix = new Matrix4d(); //TODO: frustum cull at cell level before individual model level
Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera); for(FoliageCell cell : activeCells){
for(Entity grassEntity : grassEntities){ cell.draw(modelMatrixAttribute);
Vector3d grassPosition = EntityUtils.getPosition(grassEntity); }
Quaterniond grassRotation = EntityUtils.getRotation(grassEntity); //for each invalid cell, see if can be revalidated
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity); for(String key : locationEvaluationCooldownMap.keySet()){
InstancedActor instancedActor = InstancedActor.getInstancedActor(grassEntity); int cooldownTime = locationEvaluationCooldownMap.get(key);
//update position cooldownTime--;
if(playerPosition.distance(grassPosition) > GRASS_RELOCATION_THRESHOLD){ if(cooldownTime <= 0){
grassPosition.set(getNewPosition(playerPosition)); String split[] = key.split("_");
grassRotation.set(getNewRotation()); Vector3i 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]));
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
//evaluate
if(
data.getWeight(voxelPos) > 0 &&
data.getWeight(new Vector3i(voxelPos.x,voxelPos.y + 1,voxelPos.z)) < 0 &&
typeSupportsFoliage(data.getType(voxelPos))
){
//create foliage cell
createFoliageCell(worldPos,voxelPos,0);
}
locationEvaluationCooldownMap.remove(key);
} else {
locationEvaluationCooldownMap.put(key, cooldownTime);
} }
modelMatrix = modelMatrix.identity();
Vector3f cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
modelMatrix.translate(cameraModifiedPosition);
modelMatrix.rotate(new Quaterniond(grassRotation));
modelMatrix.scale(new Vector3d(EntityUtils.getScale(grassEntity)));
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f(modelMatrix));
//draw
// instancedActor.draw(Globals.renderingEngine.getRenderPipelineState());
} }
} }
} }
@ -191,6 +184,7 @@ public class ClientFoliageManager {
* Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor * Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor
* @param entity The entity * @param entity The entity
* @param modelPath The model path for the model to back the instanced actor * @param modelPath The model path for the model to back the instanced actor
* @param capacity The capacity of the instanced actor to draw
*/ */
public static void makeEntityInstancedFoliage(Entity entity, String modelPath, int capacity){ 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.INSTANCED_ACTOR, Globals.clientInstanceManager.createInstancedActor(modelPath, vertexPath, fragmentPath, attributes, capacity));
@ -212,26 +206,35 @@ public class ClientFoliageManager {
//can't go to very top 'cause otherwise there would be no room to put grass //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 y = 0; y < ChunkData.CHUNK_SIZE - 1; y++){
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
String key = getFoliageCellKey(worldPos, new Vector3i(x,y,z)); Vector3i currentPos = new Vector3i(x,y,z);
String key = getFoliageCellKey(worldPos, currentPos);
if(locationCellMap.get(key) != null){ if(locationCellMap.get(key) != null){
//destroy if there's no longer ground or //destroy if there's no longer ground or
//if the cell above is now occupied or //if the cell above is now occupied or
//if the lower cell is no longer supporting foliage //if the lower cell is no longer supporting foliage
if(data.getWeight(new Vector3i(x,y,z)) <= 0 || if(
data.getWeight(new Vector3i(x,y + 1,z)) > 0 || data.getWeight(currentPos) <= 0 ||
!typeSupportsFoliage(data.getType(new Vector3i(x,y,z)))){ data.getWeight(new Vector3i(x,y + 1,z)) > 0 ||
//TODO: destroy !typeSupportsFoliage(data.getType(currentPos))
){
//destroy
FoliageCell toDestroy = locationCellMap.get(key);
toDestroy.destroy();
activeCells.remove(toDestroy);
locationCellMap.remove(key);
} else { } else {
//TODO: evaluate if foliage is placed well //TODO: evaluate if foliage is placed well
} }
} else { } else {
//create if current is ground and above is air //create if current is ground and above is air
if( if(
data.getWeight(new Vector3i(x,y,z)) > 0 && !locationEvaluationCooldownMap.containsKey(key) &&
data.getWeight(currentPos) > 0 &&
data.getWeight(new Vector3i(x,y + 1,z)) < 0 && data.getWeight(new Vector3i(x,y + 1,z)) < 0 &&
typeSupportsFoliage(data.getType(new Vector3i(x,y,z))) typeSupportsFoliage(data.getType(currentPos))
){ ){
//create foliage cell //create foliage cell
createFoliageCell(worldPos,currentPos,1);
} }
} }
} }
@ -242,14 +245,80 @@ public class ClientFoliageManager {
if(aboveData != null){ if(aboveData != null){
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
if(data.getWeight(new Vector3i(x,ChunkData.CHUNK_SIZE - 1,z)) > 0 && aboveData.getWeight(new Vector3i(x,0,z)) < 0){ 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))
){
//create foliage cell
createFoliageCell(worldPos,currentPos,1);
}
} }
} }
} }
} }
} }
/**
* Creates a foliage cell at a given position
* @param worldPos The world position
* @param voxelPos The voxel position
*/
private void createFoliageCell(Vector3i worldPos, Vector3i voxelPos, float initialGrowthLevel){
//get foliage types supported
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPos)).getAmbientFoliage();
if(foliageTypesSupported != null){
FoliageCell cell = new FoliageCell(worldPos, voxelPos);
//create center foliage
for(int i = 0; i < TARGET_FOLIAGE_PER_CELL; i++){
//get type
String foliageTypeName = foliageTypesSupported.get(placementRandomizer.nextInt() % foliageTypesSupported.size());
FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(foliageTypeName);
//get position to place
double offsetX = placementRandomizer.nextDouble() * 0.6 + 0.2;
double offsetZ = placementRandomizer.nextDouble() * 0.6 + 0.2;
Vector3d absolutePosition = new Vector3d(
worldPos.x * ChunkData.CHUNK_SIZE + voxelPos.x + offsetX,
worldPos.y * ChunkData.CHUNK_SIZE + voxelPos.y + data.getWeight(voxelPos) * 0.6,
worldPos.z * ChunkData.CHUNK_SIZE + voxelPos.z + offsetZ
);
//create entity
Entity grassEntity = EntityCreationUtils.createClientSpatialEntity();
makeEntityInstancedFoliage(grassEntity, foliageType.getModelPath(), grassCapacity);
EntityUtils.getPosition(grassEntity).set(absolutePosition);
EntityUtils.getRotation(grassEntity).set(getNewRotation());
EntityUtils.getScale(grassEntity).set(new Vector3d(2.0, 2.0, 2.0));
//add ambient foliage behavior tree
AmbientFoliage.attachAmbientFoliageTree(grassEntity, initialGrowthLevel, foliageType.getGrowthModel().getGrowthRate());
cell.addEntity(grassEntity);
}
activeCells.add(cell);
locationCellMap.put(getFoliageCellKey(worldPos, voxelPos),cell);
}
}
/** /**
* Gets whether the voxel type supports foliage or not * Gets whether the voxel type supports foliage or not
* @param type * @param type
@ -258,16 +327,24 @@ public class ClientFoliageManager {
private boolean typeSupportsFoliage(int type){ private boolean typeSupportsFoliage(int type){
return Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type).getAmbientFoliage() != null; return Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type).getAmbientFoliage() != null;
} }
/** /**
* Evaluate all foliage cells to see if any should be deconstructed and any new ones should be created * Invalidates a foliage cell at a position, destroying all foliage in the cell and unregistering it.
* Furthermore, it adds it to a cooldown queue to wait until it can recreate foliage
* @param worldPosition The world position of the cell
* @param voxelPosition The voxel position of the cell
*/ */
protected void evaluateFoliageCells(){ public void invalidateCell(Vector3i worldPosition, Vector3i voxelPosition){
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity); String key = getFoliageCellKey(worldPosition, voxelPosition);
for(FoliageCell activeCell : activeCells){ if(!locationEvaluationCooldownMap.containsKey(key)){
//if cell is outside of range of player, disable cell locationEvaluationCooldownMap.put(key,EVALUATION_COOLDOWN);
if(Globals.clientWorldData.convertWorldToRealSpace(activeCell.worldPosition).distance(playerPosition) > CELL_DISTANCE_MAX){ FoliageCell cell = locationCellMap.get(key);
//TODO: destroy cell if(cell != null){
//destroy
FoliageCell toDestroy = locationCellMap.get(key);
toDestroy.destroy();
activeCells.remove(toDestroy);
locationCellMap.remove(key);
} }
} }
} }

View File

@ -1,11 +1,22 @@
package electrosphere.client.foliagemanager; package electrosphere.client.foliagemanager;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.foliage.AmbientFoliage;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.buffer.ShaderAttribute;
/** /**
* Contains a set of foliage entities and groups them together. * Contains a set of foliage entities and groups them together.
@ -14,9 +25,72 @@ public class FoliageCell {
//position of the foliage cell in world coordinates //position of the foliage cell in world coordinates
protected Vector3i worldPosition; protected Vector3i worldPosition;
//position of the foliage cell in local coordinates //position of the foliage cell in local coordinates
protected Vector3i localPosition; protected Vector3i voxelPosition;
//constituent entities //constituent entities
protected Set<Entity> containedEntities; protected Set<Entity> containedEntities;
/**
* 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){
this.worldPosition = worldPos;
this.voxelPosition = voxelPos;
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){
EntityUtils.cleanUpEntity(entity);
}
clearEntities();
}
/**
* Draws all entities in the foliage cell
* @param modelMatrixAttribute The model matrix attribute to draw with
*/
protected void draw(ShaderAttribute modelMatrixAttribute){
Matrix4d modelMatrix = new Matrix4d();
Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity);
for(Entity entity : containedEntities){
Vector3d grassPosition = EntityUtils.getPosition(entity);
Quaterniond grassRotation = EntityUtils.getRotation(entity);
InstancedActor instancedActor = InstancedActor.getInstancedActor(entity);
modelMatrix = modelMatrix.identity();
Vector3f cameraModifiedPosition = new Vector3f((float)grassPosition.x,(float)grassPosition.y,(float)grassPosition.z).sub(cameraCenter);
modelMatrix.translate(cameraModifiedPosition);
modelMatrix.rotate(new Quaterniond(grassRotation));
modelMatrix.scale(new Vector3d(EntityUtils.getScale(entity)));
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f(modelMatrix));
//set priority equal to distance
instancedActor.setPriority((int)grassPosition.distance(playerPosition));
//draw
instancedActor.draw(Globals.renderingEngine.getRenderPipelineState());
}
}
} }

View File

@ -1,5 +1,8 @@
package electrosphere.client.terrain.cache; package electrosphere.client.terrain.cache;
import java.util.HashSet;
import java.util.Set;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
@ -19,6 +22,10 @@ public class ChunkData {
//How much of that terrain type is in this voxel //How much of that terrain type is in this voxel
float[][][] voxelWeight; float[][][] voxelWeight;
//the list of positions modified since the last call to resetModifiedPositions
//Used in DrawCell to keep track of which positions to invalidate
Set<String> modifiedSinceLastGeneration = new HashSet<String>();
/** /**
* Gets the voxel type array in this container * Gets the voxel type array in this container
@ -33,6 +40,22 @@ public class ChunkData {
* @param voxelType The voxel type array * @param voxelType The voxel type array
*/ */
public void setVoxelType(int[][][] voxelType){ public void setVoxelType(int[][][] voxelType){
//mark changed cells
if(this.voxelType != null){
for(int x = 0; x < CHUNK_SIZE; x++){
for(int y = 0; y < CHUNK_SIZE; y++){
for(int z = 0; z < CHUNK_SIZE; z++){
if(voxelType[x][y][z] != this.voxelType[x][y][z]){
String key = getVoxelPositionKey(new Vector3i(x,y,z));
if(!modifiedSinceLastGeneration.contains(key)){
modifiedSinceLastGeneration.add(key);
}
}
}
}
}
}
//update data
this.voxelType = voxelType; this.voxelType = voxelType;
} }
@ -49,6 +72,22 @@ public class ChunkData {
* @param voxelWeight The voxel weight array * @param voxelWeight The voxel weight array
*/ */
public void setVoxelWeight(float[][][] voxelWeight){ public void setVoxelWeight(float[][][] voxelWeight){
//mark changed cells
if(this.voxelWeight != null){
for(int x = 0; x < CHUNK_SIZE; x++){
for(int y = 0; y < CHUNK_SIZE; y++){
for(int z = 0; z < CHUNK_SIZE; z++){
if(voxelWeight[x][y][z] != this.voxelWeight[x][y][z]){
String key = getVoxelPositionKey(new Vector3i(x,y,z));
if(!modifiedSinceLastGeneration.contains(key)){
modifiedSinceLastGeneration.add(key);
}
}
}
}
}
}
//update data
this.voxelWeight = voxelWeight; this.voxelWeight = voxelWeight;
} }
@ -63,6 +102,11 @@ public class ChunkData {
public void updatePosition(int localX, int localY, int localZ, float weight, int type){ public void updatePosition(int localX, int localY, int localZ, float weight, int type){
voxelWeight[localX][localY][localZ] = weight; voxelWeight[localX][localY][localZ] = weight;
voxelType[localX][localY][localZ] = type; voxelType[localX][localY][localZ] = type;
//store as modified in cache
String key = getVoxelPositionKey(new Vector3i(localX,localY,localZ));
if(!modifiedSinceLastGeneration.contains(key)){
modifiedSinceLastGeneration.add(key);
}
} }
/** /**
@ -83,5 +127,34 @@ public class ChunkData {
return voxelType[localPosition.x][localPosition.y][localPosition.z]; return voxelType[localPosition.x][localPosition.y][localPosition.z];
} }
/**
* Resets the cache of modified positions
*/
public void resetModifiedPositions(){
this.modifiedSinceLastGeneration.clear();
}
/**
* Gets the set of all modified positions since the last call to resetModifiedPositions
* @return The set of all modified positions
*/
public Set<Vector3i> getModifiedPositions(){
Set<Vector3i> rVal = new HashSet<Vector3i>();
for(String key : modifiedSinceLastGeneration){
String[] split = key.split("_");
rVal.add(new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2])));
}
return rVal;
}
/**
* Gets a key for the modifiedSinceLastGeneration set based on a voxel position
* @param position The voxel position
* @return The key
*/
private String getVoxelPositionKey(Vector3i position){
return position.x + "_" + position.y + "_" + position.z;
}
} }

View File

@ -61,12 +61,12 @@ public class DrawCell {
rVal.worldPos = worldPos; rVal.worldPos = worldPos;
rVal.program = program; rVal.program = program;
rVal.data = data; rVal.data = data;
System.out.println("Create cell");
return rVal; return rVal;
} }
/** /**
* Generates a drawable entity based on this chunk * Generates a drawable entity based on this chunk
* @param stride The stride between indices used to generate "sparse" meshes
*/ */
public void generateDrawableEntity(){ public void generateDrawableEntity(){
if(modelEntity != null){ if(modelEntity != null){
@ -94,6 +94,11 @@ public class DrawCell {
// modelEntity.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); // modelEntity.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
// LoggerInterface.loggerRenderer.INFO("New cell @ " + cellX * dynamicInterpolationRatio + "," + cellY * dynamicInterpolationRatio); // LoggerInterface.loggerRenderer.INFO("New cell @ " + cellX * dynamicInterpolationRatio + "," + cellY * dynamicInterpolationRatio);
// EntityUtils.getPosition(modelEntity).set(getRealPos()); // EntityUtils.getPosition(modelEntity).set(getRealPos());
for(Vector3i position : data.getModifiedPositions()){
Globals.clientFoliageManager.invalidateCell(worldPos, position);
}
data.resetModifiedPositions();
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos()); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
} }
@ -105,19 +110,6 @@ public class DrawCell {
); );
} }
// public void generatePhysics(){
// //if we're in no-graphics mode, need to generate the entity
// if(modelEntity == null){
// modelEntity = EntityCreationUtils.createClientSpatialEntity();
// modelEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
// EntityUtils.getPosition(modelEntity).set(new Vector3f(cellX * dynamicInterpolationRatio, 0.0f, cellY * dynamicInterpolationRatio));
// }
// //then actually perform the attach
// physicsObject = PhysicsUtils.attachTerrainRigidBody(modelEntity,heightmap,false);
// Globals.clientSceneWrapper.getCollisionEngine().registerPhysicsEntity(modelEntity);
// // System.out.println("generate physics");
// }
/** /**
* Destroys a drawcell including its physics * Destroys a drawcell including its physics
*/ */
@ -126,5 +118,13 @@ public class DrawCell {
collisionEngine.destroyEntityThatHasPhysics(modelEntity); collisionEngine.destroyEntityThatHasPhysics(modelEntity);
EntityUtils.cleanUpEntity(modelEntity); EntityUtils.cleanUpEntity(modelEntity);
} }
/**
* Gets the current chunk data for this draw cell
* @return The chunk data
*/
public ChunkData getData(){
return data;
}
} }

View File

@ -206,8 +206,6 @@ public class DrawCellManager {
// } // }
keyCellMap.get(targetKey).destroy(); keyCellMap.get(targetKey).destroy();
keyCellMap.get(targetKey).generateDrawableEntity(); keyCellMap.get(targetKey).generateDrawableEntity();
//evaluate for foliage
Globals.clientFoliageManager.evaluateChunk(worldPos);
} }
drawable.add(targetKey); drawable.add(targetKey);
} }
@ -399,6 +397,12 @@ public class DrawCellManager {
return new Vector3i(Integer.parseInt(keyComponents[0]),Integer.parseInt(keyComponents[1]),Integer.parseInt(keyComponents[2])); return new Vector3i(Integer.parseInt(keyComponents[0]),Integer.parseInt(keyComponents[1]),Integer.parseInt(keyComponents[2]));
} }
/**
* Marks a data cell as updateable (can be regenerated with a new model because the underlying data has changed)
* @param chunkX The chunk x coordinate
* @param chunkY The chunk y coordinate
* @param chunkZ The chunk z coordinate
*/
public void markUpdateable(int chunkX, int chunkY, int chunkZ){ public void markUpdateable(int chunkX, int chunkY, int chunkZ){
updateable.add(getCellKey(chunkX, chunkY, chunkZ)); updateable.add(getCellKey(chunkX, chunkY, chunkZ));
} }

View File

@ -112,22 +112,6 @@ public class ClientTerrainManager {
clientWorldData.convertRealToChunkSpace(z) clientWorldData.convertRealToChunkSpace(z)
); );
} }
/**
* This is only for short term testing and should be removed
* @param worldX
* @param worldY
* @param worldZ
* @param localX
* @param localY
* @param localZ
* @param weight
* @param type
*/
public void updateChunk(int worldX, int worldY, int worldZ, int localX, int localY, int localZ, float weight, int type){
terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ).updatePosition(localX, localY, localZ, weight, type);
Globals.drawCellManager.markUpdateable(worldX, worldY, worldZ);
}
/** /**
* Gets the chunk data at a given world position * Gets the chunk data at a given world position

View File

@ -943,7 +943,7 @@ public class ControlHandler {
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera)); Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera)); Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0); Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
TerrainEditing.editTerrain(cursorPos, 1.1f, 1, 0.01f); TerrainEditing.editTerrain(cursorPos, 1.1f, 2, 0.01f);
} }
}}); }});
controls.get(INPUT_CODE_PLACE_TERRAIN).setOnRepeat(new ControlMethod(){public void execute(){ controls.get(INPUT_CODE_PLACE_TERRAIN).setOnRepeat(new ControlMethod(){public void execute(){
@ -956,7 +956,7 @@ public class ControlHandler {
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera)); Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera)); Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0); Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
TerrainEditing.editTerrain(cursorPos, 1.1f, 1, 0.01f); TerrainEditing.editTerrain(cursorPos, 1.1f, 2, 0.01f);
} }
}}); }});
controls.get(INPUT_CODE_PLACE_TERRAIN).setRepeatTimeout(0.2f * Main.targetFrameRate); controls.get(INPUT_CODE_PLACE_TERRAIN).setRepeatTimeout(0.2f * Main.targetFrameRate);

View File

@ -136,7 +136,7 @@ public class Main {
//debug: create terrain/world viewer //debug: create terrain/world viewer
TerrainViewer.runViewer(); // TerrainViewer.runViewer();
//create the drawing context //create the drawing context
if(Globals.RUN_CLIENT && !Globals.HEADLESS){ if(Globals.RUN_CLIENT && !Globals.HEADLESS){

View File

@ -224,6 +224,7 @@ public class EntityDataStrings {
*/ */
public static final String FOLIAGE_IS_FOLIAGE = "foliageIsFoliage"; public static final String FOLIAGE_IS_FOLIAGE = "foliageIsFoliage";
public static final String FOLIAGE_TYPE = "foliageType"; public static final String FOLIAGE_TYPE = "foliageType";
public static final String FOLIAGE_AMBIENT_TREE = "foliageAmbientTree";
/* /*
Equip state Equip state

View File

@ -0,0 +1,82 @@
package electrosphere.entity.state.foliage;
import org.joml.Vector3d;
import org.joml.Vector3f;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.BehaviorTree;
import electrosphere.entity.types.camera.CameraEntityUtils;
/**
* Behavior tree for ambient foliage. Controls regrowing, wind movement, etc
*/
public class AmbientFoliage implements BehaviorTree {
//the parent entity
Entity parent;
//The current offset by wind (used to snap back to 0)
float windOffset = 0;
//The current growth level
float growthLevel = MAX_GROWTH_LEVEL;
//The increment to increase growth level to until 1
float growthRate = MAX_GROWTH_LEVEL;
//the maximum growth level
static final float MAX_GROWTH_LEVEL = 1;
/**
* Constructor
* @param parent The parent entity
* @param regrowFactor The initial growth level
* @param growthRate The growth rate
*/
private AmbientFoliage(Entity parent, float growthLevel, float growthRate){
this.growthLevel = growthLevel;
this.growthRate = growthRate;
this.parent = parent;
}
@Override
public void simulate(float deltaTime) {
//increase growth factor if relevant
if(growthLevel < MAX_GROWTH_LEVEL){
growthLevel = growthLevel + growthRate;
if(growthLevel > MAX_GROWTH_LEVEL){
growthLevel = MAX_GROWTH_LEVEL;
}
}
EntityUtils.getScale(parent).set(growthLevel);
//rotate to face cameras
// Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
// EntityUtils.getRotation(parent).rotateTo(new Vector3d(1,0,0), new Vector3d(cameraEyeVector));
//TODO: simulate wind offset
}
/**
* Attaches an ambient foliage behavior tree to the provided entity
* @param parent The entity
* @param growthLevel The initial growth level of the foliage
* @param growthRate The rate of growth of the foliage
*/
public static void attachAmbientFoliageTree(Entity parent, float growthLevel, float growthRate){
AmbientFoliage tree = new AmbientFoliage(parent, growthLevel, growthRate);
parent.putData(EntityDataStrings.FOLIAGE_AMBIENT_TREE, tree);
Globals.clientSceneWrapper.getScene().registerBehaviorTree(tree);
}
/**
* Gets the ambient foliage tree on a given entity if it exists
* @param entity The entity
* @return The ambient foliage tree if it exists, null otherwise
*/
public static AmbientFoliage getAmbientFoliageTree(Entity entity){
return (AmbientFoliage) entity.getData(EntityDataStrings.FOLIAGE_AMBIENT_TREE);
}
}

View File

@ -3,30 +3,62 @@ package electrosphere.game.data.foliage.type;
import java.util.List; import java.util.List;
/** /**
* * A foliage object, ambient or otherwise
* @author amaterasu
*/ */
public class FoliageType { public class FoliageType {
//Denotes an ambient foliage that will be placed on a voxel
public static final String TOKEN_AMBIENT = "AMBIENT";
//the name of the foliage type
String name; String name;
//the model path of the foliage
String modelPath; String modelPath;
//the physics object(s) for the foliage
List<PhysicsObject> physicsObjects; List<PhysicsObject> physicsObjects;
//the model of growth characterists
GrowthModel growthModel;
//the list of tokens
List<String> tokens; List<String> tokens;
/**
* Gets the name of the foliage type
* @return The name
*/
public String getName() { public String getName() {
return name; return name;
} }
/**
* Gets the model path of the foliage type
* @return The model path
*/
public String getModelPath() { public String getModelPath() {
return modelPath; return modelPath;
} }
/**
* Gets the physics object(s)
* @return The physics object(s)
*/
public List<PhysicsObject> getPhysicsObjects() { public List<PhysicsObject> getPhysicsObjects() {
return physicsObjects; return physicsObjects;
} }
/**
* Gets the tokens
* @return The tokens
*/
public List<String> getTokens() { public List<String> getTokens() {
return tokens; return tokens;
} }
/**
* Gets the growth model
* @return The growth model
*/
public GrowthModel getGrowthModel(){
return growthModel;
}
} }

View File

@ -0,0 +1,19 @@
package electrosphere.game.data.foliage.type;
/**
* Model of the growth characteristics of this piece of foliage
*/
public class GrowthModel {
//the rate at which the foliage will grow
float growthRate;
/**
* Gets the growth rate
* @return the growth rate
*/
public float getGrowthRate(){
return growthRate;
}
}

View File

@ -1,6 +1,6 @@
package electrosphere.game.data.voxel; package electrosphere.game.data.voxel;
import java.util.Set; import java.util.List;
/** /**
* Data about a particular type of voxel * Data about a particular type of voxel
@ -11,7 +11,7 @@ public class VoxelType {
//the name of the type //the name of the type
String name; String name;
//any ambient foliage that can be placed on this voxel type //any ambient foliage that can be placed on this voxel type
Set<String> ambientFoliage; List<String> ambientFoliage;
/** /**
* Gets the id of the voxel type * Gets the id of the voxel type
@ -33,7 +33,7 @@ public class VoxelType {
* Gets the names of all ambient foliage that can be placed on this voxel type * Gets the names of all ambient foliage that can be placed on this voxel type
* @return The set of names * @return The set of names
*/ */
public Set<String> getAmbientFoliage(){ public List<String> getAmbientFoliage(){
return ambientFoliage; return ambientFoliage;
} }
} }

View File

@ -1492,6 +1492,10 @@ public class RenderingEngine {
Globals.projectionMatrix.setPerspective(radVerticalFOV, RenderingEngine.aspectRatio, nearClip, Globals.userSettings.getGraphicsViewDistance()); Globals.projectionMatrix.setPerspective(radVerticalFOV, RenderingEngine.aspectRatio, nearClip, Globals.userSettings.getGraphicsViewDistance());
} }
/**
* Gets the current render pipeline state
* @return The current render pipeline state
*/
public RenderPipelineState getRenderPipelineState(){ public RenderPipelineState getRenderPipelineState(){
return renderPipelineState; return renderPipelineState;
} }

View File

@ -38,7 +38,7 @@ public class InstanceData {
int drawCalls = 0; int drawCalls = 0;
//The priority queue of instanced actors to draw //The priority queue of instanced actors to draw
PriorityQueue<InstancedActor> actorQueue = new PriorityQueue<InstancedActor>(1000); PriorityQueue<InstancedActor> actorQueue = null;
//Map of actor to index in the buffers that are emitted //Map of actor to index in the buffers that are emitted
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>(); Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
//Map of index -> actor used for buffer evictions //Map of index -> actor used for buffer evictions
@ -60,6 +60,7 @@ public class InstanceData {
this.capacity = capacity; this.capacity = capacity;
this.vertexShaderPath = vertexPath; this.vertexShaderPath = vertexPath;
this.fragmentShaderPath = fragmentPath; this.fragmentShaderPath = fragmentPath;
actorQueue = new PriorityQueue<InstancedActor>(this.capacity);
} }
/** /**
@ -275,53 +276,12 @@ public class InstanceData {
} }
//increment //increment
i++; i++;
if(i > capacity){ if(i >= capacity){
break; break;
} }
} }
//reset all buffers //reset all buffers
for(ShaderAttribute attribute : attributeIndices){ flip();
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
// System.out.println(buffer.position() + " " + buffer.limit());
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
// System.out.println(buffer.position() + " " + buffer.limit());
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
buffer.flip();
} break;
}
}
} }
protected void flip(){ protected void flip(){

View File

@ -27,7 +27,6 @@ public class InstanceManager {
/** /**
* Adds an instanced actor to the list of actors to be priority sorted into drawn and not drawn * Adds an instanced actor to the list of actors to be priority sorted into drawn and not drawn
* @param actor The instanced actor * @param actor The instanced actor
* @param priority The priority of this actor
*/ */
protected void addToQueue(InstancedActor actor){ protected void addToQueue(InstancedActor actor){
InstanceData data = pathToInstanceData.get(actor.getModelPath()); InstanceData data = pathToInstanceData.get(actor.getModelPath());

View File

@ -15,7 +15,7 @@ import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.ShaderAttribute;
/** /**
* An instanced actor is a static (not animated) actor for an instanced model (eg grass, trees, leaves, rocks, etc) * An instanced actor is a static (not bone animated) actor for an instanced model (eg grass, trees, leaves, rocks, etc)
*/ */
public class InstancedActor implements Comparable<InstancedActor> { public class InstancedActor implements Comparable<InstancedActor> {
//path of the model that this instanced actor uses //path of the model that this instanced actor uses
@ -107,4 +107,20 @@ public class InstancedActor implements Comparable<InstancedActor> {
return (InstancedActor)entity.getData(EntityDataStrings.INSTANCED_ACTOR); return (InstancedActor)entity.getData(EntityDataStrings.INSTANCED_ACTOR);
} }
/**
* Sets the draw priority of the instanced actor
* @param priority The priority value (lower is higher priority)
*/
public void setPriority(int priority){
this.priority = priority;
}
/**
* Gets the priority of this instanced actor
* @return The priority of the instanced actor
*/
public int getPriority(){
return this.priority;
}
} }

View File

@ -54,7 +54,7 @@ public class ServerTerrainChunk {
for(int weightX = 0; weightX < CHUNK_DATA_GENERATOR_SIZE; weightX++){ for(int weightX = 0; weightX < CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){ for(int weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][0][weightZ] = 0.1f; weights[weightX][0][weightZ] = 0.1f;
values[weightX][0][weightZ] = 1; values[weightX][0][weightZ] = 2;
} }
} }
} }