Voxel system improvements
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-11-17 15:40:59 -05:00
parent 596b0f6624
commit a44255060d
9 changed files with 150 additions and 31 deletions

View File

@ -1061,6 +1061,10 @@ Fix hitbox destruction logic to not double-delete
(11/17/2024) (11/17/2024)
Mountain generation work 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

View File

@ -209,7 +209,8 @@ public class ClientDrawCellManager {
Globals.profiler.beginCpuSample("ClientDrawCellManager.split"); Globals.profiler.beginCpuSample("ClientDrawCellManager.split");
//perform op //perform op
WorldOctTreeNode<DrawCell> container = chunkTree.split(node); 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()); container.getData().transferChunkData(node.getData());
//do creations //do creations
@ -220,6 +221,7 @@ public class ClientDrawCellManager {
child.getMinBound().z child.getMinBound().z
); );
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos,this.chunkTree.getMaxLevel() - child.getLevel()); DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos,this.chunkTree.getMaxLevel() - child.getLevel());
drawCell.registerNotificationTarget(node.getData());
child.setLeaf(true); child.setLeaf(true);
child.setData(drawCell); child.setData(drawCell);
evaluationMap.put(child,true); evaluationMap.put(child,true);
@ -233,13 +235,13 @@ public class ClientDrawCellManager {
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
updated = true; updated = true;
} else if(shouldRequest(playerPos, node, minLeafLod, distCache)){ } else if(this.shouldRequest(playerPos, node, minLeafLod, distCache)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.request"); Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
//calculate what to request //calculate what to request
DrawCell cell = node.getData(); DrawCell cell = node.getData();
List<DrawCellFace> highResFaces = null; List<DrawCellFace> highResFaces = null;
if(shouldSolveFaces(node,playerPos, distCache)){ if(this.shouldSolveFaces(node,playerPos, distCache)){
highResFaces = this.solveHighResFace(node); highResFaces = this.solveHighResFace(node);
} }
@ -251,17 +253,17 @@ public class ClientDrawCellManager {
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
updated = true; updated = true;
} else if(shouldGenerate(playerPos, node, minLeafLod, distCache)){ } else if(this.shouldGenerate(playerPos, node, minLeafLod, distCache)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate"); Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
int lodLevel = this.getLODLevel(node); int lodLevel = this.getLODLevel(node);
//high res faces //high res faces
List<DrawCellFace> highResFaces = null; List<DrawCellFace> highResFaces = null;
if(shouldSolveFaces(node,playerPos, distCache)){ if(this.shouldSolveFaces(node,playerPos, distCache)){
highResFaces = this.solveHighResFace(node); highResFaces = this.solveHighResFace(node);
} }
if(containsDataToGenerate(node,highResFaces)){ if(this.containsDataToGenerate(node,highResFaces)){
node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces); node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces);
if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){ if(node.getData().getFailedGenerationAttempts() > FAILED_GENERATION_ATTEMPT_THRESHOLD){
node.getData().setHasRequested(false); node.getData().setHasRequested(false);
@ -284,21 +286,28 @@ public class ClientDrawCellManager {
this.validCellCount++; this.validCellCount++;
List<WorldOctTreeNode<DrawCell>> children = node.getChildren(); List<WorldOctTreeNode<DrawCell>> children = node.getChildren();
boolean isHomogenous = true; boolean isHomogenous = true;
boolean fullyGenerated = true;
for(int i = 0; i < 8; i++){ for(int i = 0; i < 8; i++){
WorldOctTreeNode<DrawCell> child = children.get(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){ if(childUpdate == true){
updated = true; updated = true;
} }
if(!child.getData().hasGenerated() || !child.getData().isHomogenous()){ if(!child.getData().hasGenerated()){
fullyGenerated = false;
}
if(!child.getData().isHomogenous()){
isHomogenous = false; isHomogenous = false;
} }
} }
WorldOctTreeNode<DrawCell> newNode = null;
if(isHomogenous){ if(isHomogenous){
WorldOctTreeNode<DrawCell> newNode = this.join(node); newNode = this.join(node);
newNode.getData().setHasGenerated(true);
newNode.getData().setHomogenous(true); newNode.getData().setHomogenous(true);
} }
if(fullyGenerated && newNode != null){
newNode.getData().setHasGenerated(true);
}
if((this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod){ if((this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod){
evaluationMap.put(node,true); evaluationMap.put(node,true);
} }
@ -331,12 +340,17 @@ public class ClientDrawCellManager {
if( if(
lastPlayerPos.x / 16 != currentPlayerPos.x / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16 || lastPlayerPos.z / 16 != currentPlayerPos.z / 16 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( if(
lastPlayerPos.x / 8 != currentPlayerPos.x / 8 || lastPlayerPos.z / 8 != currentPlayerPos.z / 8 || lastPlayerPos.z / 8 != currentPlayerPos.z / 8 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( if(
lastPlayerPos.x / 4 != currentPlayerPos.x / 4 || lastPlayerPos.z / 4 != currentPlayerPos.z / 4 || lastPlayerPos.z / 4 != currentPlayerPos.z / 4 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){ private WorldOctTreeNode<DrawCell> join(WorldOctTreeNode<DrawCell> node){
Globals.profiler.beginCpuSample("ClientDrawCellManager.join"); 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 //queue destructions prior to join -- the join operator clears all children on node
this.twoLayerDestroy(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 //update neighbors
this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel()); this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel());
@ -663,7 +679,6 @@ public class ClientDrawCellManager {
*/ */
public boolean shouldGenerate(Vector3i pos, WorldOctTreeNode<DrawCell> node, int minLeafLod, int distCache){ public boolean shouldGenerate(Vector3i pos, WorldOctTreeNode<DrawCell> node, int minLeafLod, int distCache){
return return
node.getData() != null &&
!node.getData().hasGenerated() && !node.getData().hasGenerated() &&
(this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod && (this.chunkTree.getMaxLevel() - node.getLevel()) <= minLeafLod &&
( (
@ -725,7 +740,10 @@ public class ClientDrawCellManager {
*/ */
private void recursivelyDestroy(WorldOctTreeNode<DrawCell> node){ private void recursivelyDestroy(WorldOctTreeNode<DrawCell> node){
if(node.getChildren().size() > 0){ 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){ if(node.getData() != null){
node.getData().destroy(); node.getData().destroy();

View File

@ -7,7 +7,6 @@ import org.joml.Vector3d;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
@ -88,6 +87,16 @@ public class DrawCell {
* The cached minimum distance * The cached minimum distance
*/ */
double cachedMinDistance = -1; 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){ modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(
ClientEntityUtils.destroyEntity(modelEntity); this.chunkData,
} this.notifyTarget,
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(this.chunkData, lod, atlas, this.hasPolygons()); this.modelEntity,
lod,
atlas,
this.hasPolygons()
);
ClientEntityUtils.initiallyPositionEntity(modelEntity, this.getRealPos(), new Quaterniond()); ClientEntityUtils.initiallyPositionEntity(modelEntity, this.getRealPos(), new Quaterniond());
this.setHasGenerated(true); this.setHasGenerated(true);
} }
@ -188,6 +201,24 @@ public class DrawCell {
protected Vector3i getWorldPos(){ protected Vector3i getWorldPos(){
return new Vector3i(worldPos); 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 * Destroys a drawcell including its physics
@ -200,8 +231,6 @@ public class DrawCell {
if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){ if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){
framesSimulated++; framesSimulated++;
} else { } else {
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
collisionEngine.destroyPhysics(modelEntity);
ClientEntityUtils.destroyEntity(modelEntity); ClientEntityUtils.destroyEntity(modelEntity);
Globals.clientScene.deregisterBehaviorTree(this); Globals.clientScene.deregisterBehaviorTree(this);
} }

View File

@ -17,8 +17,11 @@ import electrosphere.client.scene.ClientWorldData;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.client.terrain.cache.ClientTerrainCache; import electrosphere.client.terrain.cache.ClientTerrainCache;
import electrosphere.client.terrain.cells.ClientDrawCellManager; import electrosphere.client.terrain.cells.ClientDrawCellManager;
import electrosphere.client.terrain.cells.DrawCell;
import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.cells.VoxelTextureAtlas;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.entity.types.terrain.TerrainChunkData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
@ -277,11 +280,11 @@ public class ClientTerrainManager {
* @param data The chunk data (triangles, normals, etc) * @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 * @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 = ""; String promisedHash = "";
UUID newUUID = UUID.randomUUID(); UUID newUUID = UUID.randomUUID();
promisedHash = newUUID.toString(); promisedHash = newUUID.toString();
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas); TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas, notifyTarget, toDelete);
lock.acquireUninterruptibly(); lock.acquireUninterruptibly();
terrainChunkGenerationQueue.add(queueItem); terrainChunkGenerationQueue.add(queueItem);
lock.release(); lock.release();
@ -297,6 +300,12 @@ public class ClientTerrainManager {
for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){ for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas()); Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas());
Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash()); Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash());
if(queueItem.notifyTarget != null){
queueItem.notifyTarget.alertToGeneration();
}
if(queueItem.toDelete != null){
ClientEntityUtils.destroyEntity(queueItem.toDelete);
}
} }
terrainChunkGenerationQueue.clear(); terrainChunkGenerationQueue.clear();
lock.release(); lock.release();

View File

@ -1,6 +1,8 @@
package electrosphere.client.terrain.manager; package electrosphere.client.terrain.manager;
import electrosphere.client.terrain.cells.DrawCell;
import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.cells.VoxelTextureAtlas;
import electrosphere.entity.Entity;
import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.entity.types.terrain.TerrainChunkData;
/** /**
@ -15,16 +17,35 @@ public class TerrainChunkGenQueueItem {
//the texture atlas //the texture atlas
VoxelTextureAtlas 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 * Creates a queue item
* @param data * @param data
* @param promisedHash * @param promisedHash
* @param atlas * @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.data = data;
this.promisedHash = promisedHash; this.promisedHash = promisedHash;
this.atlas = atlas; this.atlas = atlas;
this.notifyTarget = notifyTarget;
this.toDelete = toDelete;
} }
/** /**

View File

@ -250,6 +250,9 @@ public class ClientLoading {
DrawableUtils.disableCulling(skybox); DrawableUtils.disableCulling(skybox);
EntityUtils.getRotation(skybox).rotateX((float)(-Math.PI/2.0f)); EntityUtils.getRotation(skybox).rotateX((float)(-Math.PI/2.0f));
EntityUtils.getScale(skybox).mul(600000.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"); Globals.assetManager.queueOverrideMeshShader("Models/environment/skyboxSphere.fbx", "Sphere", "Shaders/entities/skysphere/skysphere.vs", "Shaders/entities/skysphere/skysphere.fs");
//cloud ring pseudo skybox //cloud ring pseudo skybox

View File

@ -47,6 +47,7 @@ public class EntityDataStrings {
Terrain Entity Terrain Entity
*/ */
public static final String TERRAIN_IS_TERRAIN = "terrainEntity"; public static final String TERRAIN_IS_TERRAIN = "terrainEntity";
public static final String CORRESPONDING_DRAW_CELL = "correspondingDrawCell";
/* /*
* Fluid Entity * Fluid Entity

View File

@ -155,6 +155,16 @@ public class Scene {
behaviorTreeList.add(tree); 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 * Deregisters a behavior tree from the scene
* @param tree The behavior tree to deregister * @param tree The behavior tree to deregister

View File

@ -7,10 +7,12 @@ import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.client.terrain.cells.ClientDrawCellManager; import electrosphere.client.terrain.cells.ClientDrawCellManager;
import electrosphere.client.terrain.cells.DrawCell;
import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.cells.VoxelTextureAtlas;
import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.client.terrain.manager.ClientTerrainManager;
import electrosphere.collision.PhysicsEntityUtils; import electrosphere.collision.PhysicsEntityUtils;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
@ -36,11 +38,20 @@ public class TerrainChunk {
/** /**
* Creates a client terrain chunk based on weights and values provided * Creates a client terrain chunk based on weights and values provided
* @param chunkData the chunk data to generate with * @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 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 * @param hasPolygons true if the chunk has polygons to generate a model with, false otherwise
* @return The terrain chunk entity * @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"); Globals.profiler.beginAggregateCpuSample("TerrainChunk.clientCreateTerrainChunkEntity");
Entity rVal = EntityCreationUtils.createClientSpatialEntity(); Entity rVal = EntityCreationUtils.createClientSpatialEntity();
@ -52,7 +63,7 @@ public class TerrainChunk {
data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
data.constructBuffers(); data.constructBuffers();
if(Globals.clientScene.containsEntity(rVal)){ if(Globals.clientScene.containsEntity(rVal)){
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas); String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas, notifyTarget, toDelete);
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD){ if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD){
PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data);
@ -63,6 +74,12 @@ public class TerrainChunk {
} }
rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true); rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
} else { } 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."); LoggerInterface.loggerEngine.DEBUG("Finished generating terrain polygons; however, entity has already been deleted.");
} }
} catch (Error e){ } catch (Error e){
@ -71,6 +88,13 @@ public class TerrainChunk {
LoggerInterface.loggerEngine.ERROR(e); LoggerInterface.loggerEngine.ERROR(e);
} }
}); });
} else {
if(notifyTarget != null){
notifyTarget.alertToGeneration();
}
if(toDelete != null){
ClientEntityUtils.destroyEntity(toDelete);
}
} }
rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);