Foliage cell concept
This commit is contained in:
parent
3948f1f921
commit
058c6ac3d9
@ -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"
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"id" : 2,
|
||||
"name" : "grass",
|
||||
"ambientFoliage" : [
|
||||
"Grass"
|
||||
"Green Grass"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -41,9 +40,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 ||
|
||||
if(
|
||||
data.getWeight(currentPos) <= 0 ||
|
||||
data.getWeight(new Vector3i(x,y + 1,z)) > 0 ||
|
||||
!typeSupportsFoliage(data.getType(new Vector3i(x,y,z)))){
|
||||
//TODO: destroy
|
||||
!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,11 +245,77 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,14 +329,22 @@ public class ClientFoliageManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
*/
|
||||
@ -127,4 +119,12 @@ public class DrawCell {
|
||||
EntityUtils.cleanUpEntity(modelEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current chunk data for this draw cell
|
||||
* @return The chunk data
|
||||
*/
|
||||
public ChunkData getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -113,22 +113,6 @@ public class ClientTerrainManager {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param worldX The x component of the world coordinate
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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){
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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(){
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user