Voxel system improvements
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
596b0f6624
commit
a44255060d
@ -1061,6 +1061,10 @@ Fix hitbox destruction logic to not double-delete
|
||||
|
||||
(11/17/2024)
|
||||
Mountain generation work
|
||||
Fix flickering chunks on unload
|
||||
Fix draw cell distance cache busting on far distances
|
||||
Fix skybox not updating position with player entity
|
||||
Add Scene shorthand for registering runnables as behavior trees
|
||||
|
||||
|
||||
|
||||
|
||||
@ -209,7 +209,8 @@ public class ClientDrawCellManager {
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.split");
|
||||
//perform op
|
||||
WorldOctTreeNode<DrawCell> container = chunkTree.split(node);
|
||||
container.setData(DrawCell.generateTerrainCell(container.getMinBound(), this.chunkTree.getMaxLevel() - container.getLevel()));
|
||||
DrawCell containerCell = DrawCell.generateTerrainCell(container.getMinBound(), this.chunkTree.getMaxLevel() - container.getLevel());
|
||||
container.setData(containerCell);
|
||||
container.getData().transferChunkData(node.getData());
|
||||
|
||||
//do creations
|
||||
@ -220,6 +221,7 @@ public class ClientDrawCellManager {
|
||||
child.getMinBound().z
|
||||
);
|
||||
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos,this.chunkTree.getMaxLevel() - child.getLevel());
|
||||
drawCell.registerNotificationTarget(node.getData());
|
||||
child.setLeaf(true);
|
||||
child.setData(drawCell);
|
||||
evaluationMap.put(child,true);
|
||||
@ -233,13 +235,13 @@ public class ClientDrawCellManager {
|
||||
|
||||
Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(shouldRequest(playerPos, node, minLeafLod, distCache)){
|
||||
} else if(this.shouldRequest(playerPos, node, minLeafLod, distCache)){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
|
||||
|
||||
//calculate what to request
|
||||
DrawCell cell = node.getData();
|
||||
List<DrawCellFace> highResFaces = null;
|
||||
if(shouldSolveFaces(node,playerPos, distCache)){
|
||||
if(this.shouldSolveFaces(node,playerPos, distCache)){
|
||||
highResFaces = this.solveHighResFace(node);
|
||||
}
|
||||
|
||||
@ -251,17 +253,17 @@ public class ClientDrawCellManager {
|
||||
|
||||
Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(shouldGenerate(playerPos, node, minLeafLod, distCache)){
|
||||
} else if(this.shouldGenerate(playerPos, node, minLeafLod, distCache)){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
|
||||
int lodLevel = this.getLODLevel(node);
|
||||
|
||||
//high res faces
|
||||
List<DrawCellFace> highResFaces = null;
|
||||
if(shouldSolveFaces(node,playerPos, distCache)){
|
||||
if(this.shouldSolveFaces(node,playerPos, distCache)){
|
||||
highResFaces = this.solveHighResFace(node);
|
||||
}
|
||||
|
||||
if(containsDataToGenerate(node,highResFaces)){
|
||||
if(this.containsDataToGenerate(node,highResFaces)){
|
||||
node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces);
|
||||
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
|
||||
node.getData().setHasRequested(false);
|
||||
@ -284,21 +286,28 @@ public class ClientDrawCellManager {
|
||||
this.validCellCount++;
|
||||
List<WorldOctTreeNode<DrawCell>> children = node.getChildren();
|
||||
boolean isHomogenous = true;
|
||||
boolean fullyGenerated = true;
|
||||
for(int i = 0; i < 8; i++){
|
||||
WorldOctTreeNode<DrawCell> child = children.get(i);
|
||||
boolean childUpdate = recursivelyUpdateCells(child, playerPos, evaluationMap, minLeafLod, distCache);
|
||||
boolean childUpdate = this.recursivelyUpdateCells(child, playerPos, evaluationMap, minLeafLod, distCache);
|
||||
if(childUpdate == true){
|
||||
updated = true;
|
||||
}
|
||||
if(!child.getData().hasGenerated() || !child.getData().isHomogenous()){
|
||||
if(!child.getData().hasGenerated()){
|
||||
fullyGenerated = false;
|
||||
}
|
||||
if(!child.getData().isHomogenous()){
|
||||
isHomogenous = false;
|
||||
}
|
||||
}
|
||||
WorldOctTreeNode<DrawCell> newNode = null;
|
||||
if(isHomogenous){
|
||||
WorldOctTreeNode<DrawCell> newNode = this.join(node);
|
||||
newNode.getData().setHasGenerated(true);
|
||||
newNode = this.join(node);
|
||||
newNode.getData().setHomogenous(true);
|
||||
}
|
||||
if(fullyGenerated && newNode != null){
|
||||
newNode.getData().setHasGenerated(true);
|
||||
}
|
||||
if((this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod){
|
||||
evaluationMap.put(node,true);
|
||||
}
|
||||
@ -331,12 +340,17 @@ public class ClientDrawCellManager {
|
||||
if(
|
||||
lastPlayerPos.x / 16 != currentPlayerPos.x / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16
|
||||
){
|
||||
return SIXTEENTH_RES_LOD;
|
||||
return this.chunkTree.getMaxLevel();
|
||||
}
|
||||
if(
|
||||
lastPlayerPos.x / 16 != currentPlayerPos.x / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16
|
||||
){
|
||||
return SIXTEENTH_RES_LOD + 2;
|
||||
}
|
||||
if(
|
||||
lastPlayerPos.x / 8 != currentPlayerPos.x / 8 || lastPlayerPos.z / 8 != currentPlayerPos.z / 8 || lastPlayerPos.z / 8 != currentPlayerPos.z / 8
|
||||
){
|
||||
return EIGHTH_RES_LOD;
|
||||
return SIXTEENTH_RES_LOD + 1;
|
||||
}
|
||||
if(
|
||||
lastPlayerPos.x / 4 != currentPlayerPos.x / 4 || lastPlayerPos.z / 4 != currentPlayerPos.z / 4 || lastPlayerPos.z / 4 != currentPlayerPos.z / 4
|
||||
@ -593,12 +607,14 @@ public class ClientDrawCellManager {
|
||||
*/
|
||||
private WorldOctTreeNode<DrawCell> join(WorldOctTreeNode<DrawCell> node){
|
||||
Globals.profiler.beginCpuSample("ClientDrawCellManager.join");
|
||||
//perform op
|
||||
WorldOctTreeNode<DrawCell> newLeaf = chunkTree.join(node, DrawCell.generateTerrainCell(node.getMinBound(),node.getData().lod));
|
||||
newLeaf.getData().transferChunkData(node.getData());
|
||||
|
||||
//do deletions
|
||||
this.twoLayerDestroy(node);
|
||||
//queue destructions prior to join -- the join operator clears all children on node
|
||||
this.recursivelyDestroy(node);
|
||||
|
||||
//perform op
|
||||
DrawCell newLeafCell = DrawCell.generateTerrainCell(node.getMinBound(),node.getData().lod);
|
||||
WorldOctTreeNode<DrawCell> newLeaf = chunkTree.join(node, newLeafCell);
|
||||
newLeaf.getData().transferChunkData(node.getData());
|
||||
|
||||
//update neighbors
|
||||
this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel());
|
||||
@ -663,7 +679,6 @@ public class ClientDrawCellManager {
|
||||
*/
|
||||
public boolean shouldGenerate(Vector3i pos, WorldOctTreeNode<DrawCell> node, int minLeafLod, int distCache){
|
||||
return
|
||||
node.getData() != null &&
|
||||
!node.getData().hasGenerated() &&
|
||||
(this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod &&
|
||||
(
|
||||
@ -725,7 +740,10 @@ public class ClientDrawCellManager {
|
||||
*/
|
||||
private void recursivelyDestroy(WorldOctTreeNode<DrawCell> node){
|
||||
if(node.getChildren().size() > 0){
|
||||
node.getChildren().forEach(child -> recursivelyDestroy(child));
|
||||
for(WorldOctTreeNode<DrawCell> child : node.getChildren()){
|
||||
child.getData().destroy();
|
||||
}
|
||||
// node.getChildren().forEach(child -> recursivelyDestroy(child));
|
||||
}
|
||||
if(node.getData() != null){
|
||||
node.getData().destroy();
|
||||
|
||||
@ -7,7 +7,6 @@ import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
@ -88,6 +87,16 @@ public class DrawCell {
|
||||
* The cached minimum distance
|
||||
*/
|
||||
double cachedMinDistance = -1;
|
||||
|
||||
/**
|
||||
* Target to notify on generation completion
|
||||
*/
|
||||
DrawCell notifyTarget = null;
|
||||
|
||||
/**
|
||||
* The number of cells that have alerted this one
|
||||
*/
|
||||
int generationAlertCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
@ -161,10 +170,14 @@ public class DrawCell {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(modelEntity != null){
|
||||
ClientEntityUtils.destroyEntity(modelEntity);
|
||||
}
|
||||
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(this.chunkData, lod, atlas, this.hasPolygons());
|
||||
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(
|
||||
this.chunkData,
|
||||
this.notifyTarget,
|
||||
this.modelEntity,
|
||||
lod,
|
||||
atlas,
|
||||
this.hasPolygons()
|
||||
);
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, this.getRealPos(), new Quaterniond());
|
||||
this.setHasGenerated(true);
|
||||
}
|
||||
@ -188,6 +201,24 @@ public class DrawCell {
|
||||
protected Vector3i getWorldPos(){
|
||||
return new Vector3i(worldPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a target draw cell to notify once this one has completed generating its model
|
||||
* @param notifyTarget The target to notify
|
||||
*/
|
||||
public void registerNotificationTarget(DrawCell notifyTarget){
|
||||
this.notifyTarget = notifyTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alerts this draw cell that a child it is waiting on has generated
|
||||
*/
|
||||
public void alertToGeneration(){
|
||||
this.generationAlertCount++;
|
||||
if(this.generationAlertCount > 8){
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a drawcell including its physics
|
||||
@ -200,8 +231,6 @@ public class DrawCell {
|
||||
if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){
|
||||
framesSimulated++;
|
||||
} else {
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
collisionEngine.destroyPhysics(modelEntity);
|
||||
ClientEntityUtils.destroyEntity(modelEntity);
|
||||
Globals.clientScene.deregisterBehaviorTree(this);
|
||||
}
|
||||
|
||||
@ -17,8 +17,11 @@ import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.cache.ClientTerrainCache;
|
||||
import electrosphere.client.terrain.cells.ClientDrawCellManager;
|
||||
import electrosphere.client.terrain.cells.DrawCell;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
@ -277,11 +280,11 @@ public class ClientTerrainManager {
|
||||
* @param data The chunk data (triangles, normals, etc)
|
||||
* @return The model path that is promised to eventually reflect the terrain model when it makes it to gpu
|
||||
*/
|
||||
public static String queueTerrainGridGeneration(TerrainChunkData data, VoxelTextureAtlas atlas){
|
||||
public static String queueTerrainGridGeneration(TerrainChunkData data, VoxelTextureAtlas atlas, DrawCell notifyTarget, Entity toDelete){
|
||||
String promisedHash = "";
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
promisedHash = newUUID.toString();
|
||||
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas);
|
||||
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas, notifyTarget, toDelete);
|
||||
lock.acquireUninterruptibly();
|
||||
terrainChunkGenerationQueue.add(queueItem);
|
||||
lock.release();
|
||||
@ -297,6 +300,12 @@ public class ClientTerrainManager {
|
||||
for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){
|
||||
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas());
|
||||
Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash());
|
||||
if(queueItem.notifyTarget != null){
|
||||
queueItem.notifyTarget.alertToGeneration();
|
||||
}
|
||||
if(queueItem.toDelete != null){
|
||||
ClientEntityUtils.destroyEntity(queueItem.toDelete);
|
||||
}
|
||||
}
|
||||
terrainChunkGenerationQueue.clear();
|
||||
lock.release();
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package electrosphere.client.terrain.manager;
|
||||
|
||||
import electrosphere.client.terrain.cells.DrawCell;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
|
||||
/**
|
||||
@ -15,16 +17,35 @@ public class TerrainChunkGenQueueItem {
|
||||
//the texture atlas
|
||||
VoxelTextureAtlas atlas;
|
||||
|
||||
/**
|
||||
* The draw cell to notify once this model is fully available to render
|
||||
*/
|
||||
DrawCell notifyTarget;
|
||||
|
||||
/**
|
||||
* The optional entity to delete on generation of this target
|
||||
*/
|
||||
Entity toDelete;
|
||||
|
||||
/**
|
||||
* Creates a queue item
|
||||
* @param data
|
||||
* @param promisedHash
|
||||
* @param atlas
|
||||
* @param notifyTarget The draw cell to notify once this model is fully available to render
|
||||
*/
|
||||
public TerrainChunkGenQueueItem(TerrainChunkData data, String promisedHash, VoxelTextureAtlas atlas){
|
||||
public TerrainChunkGenQueueItem(
|
||||
TerrainChunkData data,
|
||||
String promisedHash,
|
||||
VoxelTextureAtlas atlas,
|
||||
DrawCell notifyTarget,
|
||||
Entity toDelete
|
||||
){
|
||||
this.data = data;
|
||||
this.promisedHash = promisedHash;
|
||||
this.atlas = atlas;
|
||||
this.notifyTarget = notifyTarget;
|
||||
this.toDelete = toDelete;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -250,6 +250,9 @@ public class ClientLoading {
|
||||
DrawableUtils.disableCulling(skybox);
|
||||
EntityUtils.getRotation(skybox).rotateX((float)(-Math.PI/2.0f));
|
||||
EntityUtils.getScale(skybox).mul(600000.0f);
|
||||
Globals.clientScene.registerBehaviorTree(() -> {
|
||||
EntityUtils.getPosition(skybox).set(EntityUtils.getPosition(Globals.playerEntity));
|
||||
});
|
||||
Globals.assetManager.queueOverrideMeshShader("Models/environment/skyboxSphere.fbx", "Sphere", "Shaders/entities/skysphere/skysphere.vs", "Shaders/entities/skysphere/skysphere.fs");
|
||||
|
||||
//cloud ring pseudo skybox
|
||||
|
||||
@ -47,6 +47,7 @@ public class EntityDataStrings {
|
||||
Terrain Entity
|
||||
*/
|
||||
public static final String TERRAIN_IS_TERRAIN = "terrainEntity";
|
||||
public static final String CORRESPONDING_DRAW_CELL = "correspondingDrawCell";
|
||||
|
||||
/*
|
||||
* Fluid Entity
|
||||
|
||||
@ -155,6 +155,16 @@ public class Scene {
|
||||
behaviorTreeList.add(tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new behavior tree that executes a task
|
||||
* @param task The task
|
||||
*/
|
||||
public void registerBehaviorTree(Runnable task){
|
||||
this.registerBehaviorTree(new BehaviorTree(){public void simulate(float deltaTime) {
|
||||
task.run();
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters a behavior tree from the scene
|
||||
* @param tree The behavior tree to deregister
|
||||
|
||||
@ -7,10 +7,12 @@ import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import electrosphere.client.terrain.cells.ClientDrawCellManager;
|
||||
import electrosphere.client.terrain.cells.DrawCell;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.collision.PhysicsEntityUtils;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
@ -36,11 +38,20 @@ public class TerrainChunk {
|
||||
/**
|
||||
* Creates a client terrain chunk based on weights and values provided
|
||||
* @param chunkData the chunk data to generate with
|
||||
* @param toDelete The entity to delete on full generation of this entity
|
||||
* @param notifyTarget The target draw cell to notify once this has successfully generated its model
|
||||
* @param levelOfDetail Increasing value that increments level of detail. 0 would be full resolution, 1 would be half resolution and so on. Only generates physics if levelOfDetail is 0
|
||||
* @param hasPolygons true if the chunk has polygons to generate a model with, false otherwise
|
||||
* @return The terrain chunk entity
|
||||
*/
|
||||
public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas, boolean hasPolygons){
|
||||
public static Entity clientCreateTerrainChunkEntity(
|
||||
TransvoxelChunkData chunkData,
|
||||
DrawCell notifyTarget,
|
||||
Entity toDelete,
|
||||
int levelOfDetail,
|
||||
VoxelTextureAtlas atlas,
|
||||
boolean hasPolygons
|
||||
){
|
||||
Globals.profiler.beginAggregateCpuSample("TerrainChunk.clientCreateTerrainChunkEntity");
|
||||
|
||||
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
|
||||
@ -52,7 +63,7 @@ public class TerrainChunk {
|
||||
data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
|
||||
data.constructBuffers();
|
||||
if(Globals.clientScene.containsEntity(rVal)){
|
||||
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas);
|
||||
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas, notifyTarget, toDelete);
|
||||
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
|
||||
if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD){
|
||||
PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data);
|
||||
@ -63,6 +74,12 @@ public class TerrainChunk {
|
||||
}
|
||||
rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
|
||||
} else {
|
||||
if(notifyTarget != null){
|
||||
notifyTarget.alertToGeneration();
|
||||
}
|
||||
if(toDelete != null){
|
||||
ClientEntityUtils.destroyEntity(toDelete);
|
||||
}
|
||||
LoggerInterface.loggerEngine.DEBUG("Finished generating terrain polygons; however, entity has already been deleted.");
|
||||
}
|
||||
} catch (Error e){
|
||||
@ -71,6 +88,13 @@ public class TerrainChunk {
|
||||
LoggerInterface.loggerEngine.ERROR(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if(notifyTarget != null){
|
||||
notifyTarget.alertToGeneration();
|
||||
}
|
||||
if(toDelete != null){
|
||||
ClientEntityUtils.destroyEntity(toDelete);
|
||||
}
|
||||
}
|
||||
|
||||
rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user