From 058c6ac3d9e7b9a5ebd88f08e2190f785059f4a7 Mon Sep 17 00:00:00 2001 From: austin Date: Fri, 23 Jun 2023 14:09:03 -0400 Subject: [PATCH] Foliage cell concept --- assets/Data/foliage.json | 10 +- assets/Data/voxelTypes.json | 2 +- .../foliagemanager/ClientFoliageManager.java | 205 ++++++++++++------ .../client/foliagemanager/FoliageCell.java | 78 ++++++- .../client/terrain/cache/ChunkData.java | 73 +++++++ .../client/terrain/cells/DrawCell.java | 28 +-- .../client/terrain/cells/DrawCellManager.java | 8 +- .../terrain/manager/ClientTerrainManager.java | 16 -- .../controls/ControlHandler.java | 4 +- src/main/java/electrosphere/engine/Main.java | 2 +- .../entity/EntityDataStrings.java | 1 + .../entity/state/foliage/AmbientFoliage.java | 82 +++++++ .../game/data/foliage/type/FoliageType.java | 36 ++- .../game/data/foliage/type/GrowthModel.java | 19 ++ .../game/data/voxel/VoxelType.java | 6 +- .../renderer/RenderingEngine.java | 4 + .../renderer/actor/instance/InstanceData.java | 48 +--- .../actor/instance/InstanceManager.java | 1 - .../actor/instance/InstancedActor.java | 18 +- .../terrain/manager/ServerTerrainChunk.java | 2 +- 20 files changed, 487 insertions(+), 156 deletions(-) create mode 100644 src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java create mode 100644 src/main/java/electrosphere/game/data/foliage/type/GrowthModel.java diff --git a/assets/Data/foliage.json b/assets/Data/foliage.json index fb0129fc..71bc90d5 100644 --- a/assets/Data/foliage.json +++ b/assets/Data/foliage.json @@ -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" } ] diff --git a/assets/Data/voxelTypes.json b/assets/Data/voxelTypes.json index 04c10a1e..228d4d81 100644 --- a/assets/Data/voxelTypes.json +++ b/assets/Data/voxelTypes.json @@ -12,7 +12,7 @@ "id" : 2, "name" : "grass", "ambientFoliage" : [ - "Grass" + "Green Grass" ] } ] diff --git a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java index dca760ba..7dc9e80e 100644 --- a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java +++ b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java @@ -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 grassEntities = new HashSet(); //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 locationEvaluationCooldownMap = new ConcurrentHashMap(); + //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 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); } } } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index 20e2068e..96a08a10 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -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 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(); + } + + /** + * 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()); + } + } } diff --git a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java index 13bb9e05..49751010 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java +++ b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java @@ -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 modifiedSinceLastGeneration = new HashSet(); + /** * 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 getModifiedPositions(){ + Set rVal = new HashSet(); + 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; + } + } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 30573ca5..82ea9a28 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -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; + } } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java index fe63818a..13106226 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java @@ -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)); } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index ce1854db..207d4daf 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -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 diff --git a/src/main/java/electrosphere/controls/ControlHandler.java b/src/main/java/electrosphere/controls/ControlHandler.java index 858074a7..8dad1953 100644 --- a/src/main/java/electrosphere/controls/ControlHandler.java +++ b/src/main/java/electrosphere/controls/ControlHandler.java @@ -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); diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index 2961d586..5da5e629 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -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){ diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index be1a25f3..4cdac42e 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -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 diff --git a/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java b/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java new file mode 100644 index 00000000..d6e54979 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java @@ -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); + } + +} diff --git a/src/main/java/electrosphere/game/data/foliage/type/FoliageType.java b/src/main/java/electrosphere/game/data/foliage/type/FoliageType.java index 4c04ee8c..1287e097 100644 --- a/src/main/java/electrosphere/game/data/foliage/type/FoliageType.java +++ b/src/main/java/electrosphere/game/data/foliage/type/FoliageType.java @@ -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 physicsObjects; + //the model of growth characterists + GrowthModel growthModel; + //the list of tokens List 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 getPhysicsObjects() { return physicsObjects; } + /** + * Gets the tokens + * @return The tokens + */ public List getTokens() { return tokens; } + + /** + * Gets the growth model + * @return The growth model + */ + public GrowthModel getGrowthModel(){ + return growthModel; + } } diff --git a/src/main/java/electrosphere/game/data/foliage/type/GrowthModel.java b/src/main/java/electrosphere/game/data/foliage/type/GrowthModel.java new file mode 100644 index 00000000..840074d2 --- /dev/null +++ b/src/main/java/electrosphere/game/data/foliage/type/GrowthModel.java @@ -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; + } + +} diff --git a/src/main/java/electrosphere/game/data/voxel/VoxelType.java b/src/main/java/electrosphere/game/data/voxel/VoxelType.java index 422ff939..989a82f2 100644 --- a/src/main/java/electrosphere/game/data/voxel/VoxelType.java +++ b/src/main/java/electrosphere/game/data/voxel/VoxelType.java @@ -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 ambientFoliage; + List 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 getAmbientFoliage(){ + public List getAmbientFoliage(){ return ambientFoliage; } } diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index fd6f292b..21f703e7 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -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; } diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java b/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java index 3ae8b454..9f9b0e3f 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstanceData.java @@ -38,7 +38,7 @@ public class InstanceData { int drawCalls = 0; //The priority queue of instanced actors to draw - PriorityQueue actorQueue = new PriorityQueue(1000); + PriorityQueue actorQueue = null; //Map of actor to index in the buffers that are emitted Map actorIndexMap = new HashMap(); //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(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(){ diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java b/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java index fb804345..5755776e 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java @@ -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()); diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java b/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java index 1a7d39e8..55668ef1 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java @@ -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 { //path of the model that this instanced actor uses @@ -107,4 +107,20 @@ public class InstancedActor implements Comparable { 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; + } + } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java index a8e8dfa8..0469df8d 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java @@ -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; } } }