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",
"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,
"name" : "grass",
"ambientFoliage" : [
"Grass"
"Green Grass"
]
}
]

View File

@ -7,9 +7,8 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
@ -22,8 +21,8 @@ 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.entity.state.foliage.AmbientFoliage;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
@ -40,9 +39,6 @@ public class ClientFoliageManager {
//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;
@ -58,6 +54,13 @@ public class ClientFoliageManager {
static final float CELL_DISTANCE_MAX = 25f;
//The maximum number of foliage cells
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
*/
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);
//queue ambient foliage models
for(FoliageType foliageType : Globals.gameConfigCurrent.getFoliageMap().getFoliageList()){
if(foliageType.getTokens().contains(FoliageType.TOKEN_AMBIENT)){
Globals.assetManager.addModelPathToQueue(foliageType.getModelPath());
}
}
ready = true;
}
@ -126,29 +116,32 @@ public class ClientFoliageManager {
*/
public void update(){
if(ready){
Matrix4d modelMatrix = new Matrix4d();
Vector3f cameraCenter = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
for(Entity grassEntity : grassEntities){
Vector3d grassPosition = EntityUtils.getPosition(grassEntity);
Quaterniond 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());
//TODO: frustum cull at cell level before individual model level
for(FoliageCell cell : activeCells){
cell.draw(modelMatrixAttribute);
}
//for each invalid cell, see if can be revalidated
for(String key : locationEvaluationCooldownMap.keySet()){
int cooldownTime = locationEvaluationCooldownMap.get(key);
cooldownTime--;
if(cooldownTime <= 0){
String split[] = key.split("_");
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
* @param entity The entity
* @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){
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
for(int y = 0; y < ChunkData.CHUNK_SIZE - 1; y++){
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){
//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(new Vector3i(x,y,z)) <= 0 ||
data.getWeight(new Vector3i(x,y + 1,z)) > 0 ||
!typeSupportsFoliage(data.getType(new Vector3i(x,y,z)))){
//TODO: destroy
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(
data.getWeight(new Vector3i(x,y,z)) > 0 &&
!locationEvaluationCooldownMap.containsKey(key) &&
data.getWeight(currentPos) > 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
createFoliageCell(worldPos,currentPos,1);
}
}
}
@ -242,14 +245,80 @@ public class ClientFoliageManager {
if(aboveData != null){
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
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
* @param type
@ -258,16 +327,24 @@ public class ClientFoliageManager {
private boolean typeSupportsFoliage(int type){
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(){
Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity);
for(FoliageCell activeCell : activeCells){
//if cell is outside of range of player, disable cell
if(Globals.clientWorldData.convertWorldToRealSpace(activeCell.worldPosition).distance(playerPosition) > CELL_DISTANCE_MAX){
//TODO: destroy cell
public void invalidateCell(Vector3i worldPosition, Vector3i voxelPosition){
String key = getFoliageCellKey(worldPosition, voxelPosition);
if(!locationEvaluationCooldownMap.containsKey(key)){
locationEvaluationCooldownMap.put(key,EVALUATION_COOLDOWN);
FoliageCell cell = locationCellMap.get(key);
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;
import java.util.HashSet;
import java.util.Set;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector3i;
import electrosphere.engine.Globals;
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.
@ -14,9 +25,72 @@ public class FoliageCell {
//position of the foliage cell in world coordinates
protected Vector3i worldPosition;
//position of the foliage cell in local coordinates
protected Vector3i localPosition;
protected Vector3i voxelPosition;
//constituent entities
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;
import java.util.HashSet;
import java.util.Set;
import org.joml.Vector3i;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
@ -19,6 +22,10 @@ public class ChunkData {
//How much of that terrain type is in this voxel
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
@ -33,6 +40,22 @@ public class ChunkData {
* @param voxelType The voxel type array
*/
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;
}
@ -49,6 +72,22 @@ public class ChunkData {
* @param voxelWeight The voxel weight array
*/
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;
}
@ -63,6 +102,11 @@ public class ChunkData {
public void updatePosition(int localX, int localY, int localZ, float weight, int type){
voxelWeight[localX][localY][localZ] = weight;
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];
}
/**
* 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.program = program;
rVal.data = data;
System.out.println("Create cell");
return rVal;
}
/**
* Generates a drawable entity based on this chunk
* @param stride The stride between indices used to generate "sparse" meshes
*/
public void generateDrawableEntity(){
if(modelEntity != null){
@ -94,6 +94,11 @@ public class DrawCell {
// modelEntity.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
// LoggerInterface.loggerRenderer.INFO("New cell @ " + cellX * dynamicInterpolationRatio + "," + cellY * dynamicInterpolationRatio);
// EntityUtils.getPosition(modelEntity).set(getRealPos());
for(Vector3i position : data.getModifiedPositions()){
Globals.clientFoliageManager.invalidateCell(worldPos, position);
}
data.resetModifiedPositions();
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
*/
@ -126,5 +118,13 @@ public class DrawCell {
collisionEngine.destroyEntityThatHasPhysics(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).generateDrawableEntity();
//evaluate for foliage
Globals.clientFoliageManager.evaluateChunk(worldPos);
}
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]));
}
/**
* 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){
updateable.add(getCellKey(chunkX, chunkY, chunkZ));
}

View File

@ -112,22 +112,6 @@ public class ClientTerrainManager {
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

View File

@ -943,7 +943,7 @@ public class ControlHandler {
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
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(){
@ -956,7 +956,7 @@ public class ControlHandler {
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
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);

View File

@ -136,7 +136,7 @@ public class Main {
//debug: create terrain/world viewer
TerrainViewer.runViewer();
// TerrainViewer.runViewer();
//create the drawing context
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_TYPE = "foliageType";
public static final String FOLIAGE_AMBIENT_TREE = "foliageAmbientTree";
/*
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;
/**
*
* @author amaterasu
* A foliage object, ambient or otherwise
*/
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;
//the model path of the foliage
String modelPath;
//the physics object(s) for the foliage
List<PhysicsObject> physicsObjects;
//the model of growth characterists
GrowthModel growthModel;
//the list of tokens
List<String> tokens;
/**
* Gets the name of the foliage type
* @return The name
*/
public String getName() {
return name;
}
/**
* Gets the model path of the foliage type
* @return The model path
*/
public String getModelPath() {
return modelPath;
}
/**
* Gets the physics object(s)
* @return The physics object(s)
*/
public List<PhysicsObject> getPhysicsObjects() {
return physicsObjects;
}
/**
* Gets the tokens
* @return The tokens
*/
public List<String> getTokens() {
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;
import java.util.Set;
import java.util.List;
/**
* Data about a particular type of voxel
@ -11,7 +11,7 @@ public class VoxelType {
//the name of the type
String name;
//any ambient foliage that can be placed on this voxel type
Set<String> ambientFoliage;
List<String> ambientFoliage;
/**
* 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
* @return The set of names
*/
public Set<String> getAmbientFoliage(){
public List<String> getAmbientFoliage(){
return ambientFoliage;
}
}

View File

@ -1492,6 +1492,10 @@ public class RenderingEngine {
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(){
return renderPipelineState;
}

View File

@ -38,7 +38,7 @@ public class InstanceData {
int drawCalls = 0;
//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<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
//Map of index -> actor used for buffer evictions
@ -60,6 +60,7 @@ public class InstanceData {
this.capacity = capacity;
this.vertexShaderPath = vertexPath;
this.fragmentShaderPath = fragmentPath;
actorQueue = new PriorityQueue<InstancedActor>(this.capacity);
}
/**
@ -275,53 +276,12 @@ public class InstanceData {
}
//increment
i++;
if(i > capacity){
if(i >= capacity){
break;
}
}
//reset all buffers
for(ShaderAttribute attribute : attributeIndices){
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;
}
}
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
* @param actor The instanced actor
* @param priority The priority of this actor
*/
protected void addToQueue(InstancedActor actor){
InstanceData data = pathToInstanceData.get(actor.getModelPath());

View File

@ -15,7 +15,7 @@ import electrosphere.renderer.RenderPipelineState;
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> {
//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);
}
/**
* 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 weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][0][weightZ] = 0.1f;
values[weightX][0][weightZ] = 1;
values[weightX][0][weightZ] = 2;
}
}
}