Fix terrain editing
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-11-23 16:47:33 -05:00
parent a34dc4a4dc
commit 5fee2d06af
15 changed files with 199 additions and 55 deletions

View File

@ -1132,6 +1132,8 @@ Disable tunnel noise to align foliage better + adjust manual value for foliage p
(11/23/2024)
Clean up top level folder
Break out dependency documentation into a dedicated file
Fix terrain editing
Fix foliage not updating at edited chunk
# TODO
@ -1168,6 +1170,7 @@ Bug Fixes
- Fix particles not spawning in correct positions
- Fix flickering when applying yoga signal (may need to rethink arch here)
- Fix virtual scrollables not working
- Fix foliage flickering on edit
Startup Performance
- Allow texture map to bind multiple model paths to a single set of mesh->textures

View File

@ -154,6 +154,31 @@ public class ClientWorldData {
);
}
/**
* Converts a relative voxel position to its absolute voxel equivalent
* @param voxelPos The relative voxel position
* @param worldPos The position of the chunk
* @return The absolute voxel position ie the voxel-aligned position not clamped to the current chunk
*/
public Vector3i convertRelativeVoxelToAbsoluteVoxelSpace(Vector3i voxelPos, Vector3i worldPos){
return new Vector3i(
worldPos.x * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos.x,
worldPos.y * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos.y,
worldPos.z * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos.z
);
}
/**
* Converts a relative voxel position to its absolute voxel equivalent
* @param voxelPos The relative voxel position
* @param worldPos The position of the chunk
* @return The absolute voxel position ie the voxel-aligned position not clamped to the current chunk
*/
public int convertRelativeVoxelToAbsoluteVoxelSpace(int voxelPos, int worldPos){
return worldPos * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos;
}
/**
* Converts a world space vector to a real space vector
* @param position The world space vector

View File

@ -14,6 +14,11 @@ import electrosphere.entity.Entity;
*/
public class ScriptClientVoxelUtils {
/**
* Increment to edit terrain by
*/
static final float EDIT_INCREMENT = 0.1f;
/**
* Applies the current voxel palette where the player's cursor is looking
*/
@ -29,7 +34,7 @@ public class ScriptClientVoxelUtils {
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), CollisionEngine.DEFAULT_INTERACT_DISTANCE);
if(Globals.clientSelectedVoxelType != null){
TerrainEditing.editTerrain(cursorPos, 1.1f, Globals.clientSelectedVoxelType.getId(), 0.1f);
TerrainEditing.editTerrain(cursorPos, 1.1f, Globals.clientSelectedVoxelType.getId(), EDIT_INCREMENT);
}
}
}

View File

@ -159,7 +159,7 @@ public class ChunkData {
voxelWeight[localX][localY][localZ] = weight;
voxelType[localX][localY][localZ] = type;
//store as modified in cache
String key = getVoxelPositionKey(new Vector3i(localX,localY,localZ));
String key = this.getVoxelPositionKey(new Vector3i(localX,localY,localZ));
if(!modifiedSinceLastGeneration.contains(key)){
modifiedSinceLastGeneration.add(key);
}

View File

@ -799,8 +799,11 @@ public class ClientDrawCellManager {
* @param worldY The world y position
* @param worldZ The world z position
*/
public void markUpdateable(float worldX, float worldY, float worldZ){
throw new Error("Unimplemented");
public void markUpdateable(int worldX, int worldY, int worldZ){
DrawCell drawCell = this.getDrawCell(worldX, worldY, worldZ);
drawCell.ejectChunkData();
drawCell.setHasGenerated(false);
drawCell.setHasRequested(false);
}
/**

View File

@ -175,10 +175,11 @@ public class DrawCell {
}
}
}
Entity toDelete = this.modelEntity;
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(
this.chunkData,
this.notifyTarget,
this.modelEntity,
toDelete,
lod,
atlas,
this.hasPolygons()
@ -555,6 +556,13 @@ public class DrawCell {
this.failedGenerationAttempts = this.failedGenerationAttempts + attempts;
}
/**
* Ejects the chunk data
*/
public void ejectChunkData(){
this.chunkData = null;
}
/**
* Gets whether this draw cell is homogenous or not
* @return true if it is homogenous, false otherwise

View File

@ -19,7 +19,6 @@ import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.renderer.OpenGLState;
@ -267,7 +266,6 @@ public class FoliageCell {
this.chunkData = new TransvoxelChunkData(currentChunk.getVoxelWeight(), currentChunk.getVoxelType(), 0);
}
this.generate();
this.setHasGenerated(true);
}
@ -276,9 +274,6 @@ public class FoliageCell {
*/
protected void generate(){
boolean shouldGenerate = false;
if(!Globals.clientDrawCellManager.hasGeneratedPhysics(worldPos.x, worldPos.y, worldPos.z)){
return;
}
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos,ChunkData.NO_STRIDE);
if(data == null){
return;
@ -315,9 +310,12 @@ public class FoliageCell {
if(shouldGenerate){
Entity oldEntity = this.modelEntity;
//create entity
this.modelEntity = EntityCreationUtils.createClientSpatialEntity();
FoliageModel.clientCreateFoliageChunkEntity(foliageTypesSupported,scale,this.modelEntity,this.getRealPos(),worldPos,voxelPos,notifyTarget,oldEntity);
this.modelEntity = FoliageModel.clientCreateFoliageChunkEntity(foliageTypesSupported,scale,this.getRealPos(),worldPos,voxelPos,notifyTarget,oldEntity);
} else {
if(this.modelEntity != null){
ClientEntityUtils.destroyEntity(this.modelEntity);
this.modelEntity = null;
}
this.homogenous = true;
}
this.hasGenerated = true;
@ -650,6 +648,13 @@ public class FoliageCell {
this.failedGenerationAttempts = this.failedGenerationAttempts + attempts;
}
/**
* Ejects the chunk data
*/
public void ejectChunkData(){
this.chunkData = null;
}
/**
* Gets whether this foliage cell is homogenous or not
* @return true if it is homogenous, false otherwise

View File

@ -723,9 +723,18 @@ public class FoliageCellManager {
* @param worldX The world x position
* @param worldY The world y position
* @param worldZ The world z position
* @param voxelX The voxel x position
* @param voxelY The voxel y position
* @param voxelZ The voxel z position
*/
public void markUpdateable(float worldX, float worldY, float worldZ){
throw new Error("Unimplemented");
public void markUpdateable(int worldX, int worldY, int worldZ, int voxelX, int voxelY, int voxelZ){
int absVoxelX = Globals.clientWorldData.convertRelativeVoxelToAbsoluteVoxelSpace(voxelX,worldX);
int absVoxelY = Globals.clientWorldData.convertRelativeVoxelToAbsoluteVoxelSpace(voxelY,worldY);
int absVoxelZ = Globals.clientWorldData.convertRelativeVoxelToAbsoluteVoxelSpace(voxelZ,worldZ);
FoliageCell foliageCell = this.getFoliageCell(absVoxelX, absVoxelY, absVoxelZ);
foliageCell.ejectChunkData();
foliageCell.setHasGenerated(false);
foliageCell.setHasRequested(false);
}
/**

View File

@ -137,7 +137,6 @@ public class FoliageModel {
public static Entity clientCreateFoliageChunkEntity(
List<String> foliageTypesSupported,
int scale,
Entity modelEntity,
Vector3d realPos,
Vector3i worldPos,
Vector3i voxelPos,
@ -218,11 +217,17 @@ public class FoliageModel {
QueuedTexture queuedAsset = new QueuedTexture(buffer,textureWidth,textureHeight);
Globals.assetManager.queuedAsset(queuedAsset);
TextureInstancedActor.attachTextureInstancedActor(modelEntity, foliageType.getGraphicsTemplate().getModel().getPath(), vertexPath, fragmentPath, queuedAsset, drawCount, textureHeight);
ClientEntityUtils.initiallyPositionEntity(modelEntity, realPos, new Quaterniond());
EntityUtils.getScale(modelEntity).set(1,1,1);
TextureInstancedActor.attachTextureInstancedActor(rVal, foliageType.getGraphicsTemplate().getModel().getPath(), vertexPath, fragmentPath, queuedAsset, drawCount, textureHeight);
ClientEntityUtils.initiallyPositionEntity(rVal, realPos, new Quaterniond());
EntityUtils.getScale(rVal).set(1,1,1);
//add ambient foliage behavior tree
AmbientFoliage.attachAmbientFoliageTree(modelEntity, 1.0f, foliageType.getGrowthModel().getGrowthRate());
AmbientFoliage.attachAmbientFoliageTree(rVal, 1.0f, foliageType.getGrowthModel().getGrowthRate());
}
if(toDelete != null){
ClientEntityUtils.destroyEntity(toDelete);
}
if(notifyTarget != null){
notifyTarget.alertToGeneration();
}
} catch (Error e){
LoggerInterface.loggerEngine.ERROR(e);

View File

@ -10,6 +10,7 @@ import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.renderer.actor.ActorUtils;
import electrosphere.renderer.actor.instance.TextureInstancedActor;
/**
* Client only entity utility functions
@ -55,6 +56,12 @@ public class ClientEntityUtils {
//deregister all behavior trees
EntityUtils.cleanUpEntity(entity);
//instanced actor
if(TextureInstancedActor.getTextureInstancedActor(entity) != null){
TextureInstancedActor actor = TextureInstancedActor.getTextureInstancedActor(entity);
actor.free();
}
if(Globals.clientSceneWrapper != null){
Globals.clientSceneWrapper.getScene().deregisterEntity(entity);
Globals.clientSceneWrapper.deregisterTranslationMapping(entity);

View File

@ -107,13 +107,67 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
//
//mark all relevant drawcells as updateable
for(Vector3i worldPosToUpdate : positionsToUpdate){
if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z, ChunkData.NO_STRIDE)){
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z, ChunkData.NO_STRIDE);
if(data != null){
Globals.clientDrawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
if(
worldPosToUpdate.x >= 0 && worldPosToUpdate.x < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPosToUpdate.y >= 0 && worldPosToUpdate.y < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPosToUpdate.z >= 0 && worldPosToUpdate.z < Globals.clientWorldData.getWorldDiscreteSize()
){
//
//mark terrain chunk for update
Globals.clientDrawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
//
//update foliage manager
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX(), message.getvoxelY(), message.getvoxelZ()
);
if(message.getvoxelX() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX() - 1, message.getvoxelY(), message.getvoxelZ()
);
if(message.getvoxelY() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX() - 1, message.getvoxelY() - 1, message.getvoxelZ()
);
if(message.getvoxelZ() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX() - 1, message.getvoxelY() - 1, message.getvoxelZ() - 1
);
}
} else {
if(message.getvoxelZ() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX() - 1, message.getvoxelY(), message.getvoxelZ() - 1
);
}
}
} else {
if(message.getvoxelY() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX(), message.getvoxelY() - 1, message.getvoxelZ()
);
if(message.getvoxelZ() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX(), message.getvoxelY() - 1, message.getvoxelZ() - 1
);
}
} else {
if(message.getvoxelZ() > 0){
Globals.foliageCellManager.markUpdateable(
worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z,
message.getvoxelX(), message.getvoxelY(), message.getvoxelZ() - 1
);
}
}
}
}
// Globals.clientFoliageManager.evaluateChunk(worldPos);
}
} break;
case SENDFLUIDDATA: {

View File

@ -28,13 +28,13 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
public TerrainMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) {
switch(message.getMessageSubtype()){
case REQUESTCHUNKDATA: {
sendWorldSubChunkAsync(connectionHandler,
TerrainProtocol.sendWorldSubChunkAsync(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ()
);
return null;
}
case REQUESTREDUCEDCHUNKDATA: {
sendWorldSubChunkAsyncStrided(connectionHandler,
TerrainProtocol.sendWorldSubChunkAsyncStrided(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution()
);
return null;
@ -49,25 +49,25 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
public void handleSyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) {
switch(message.getMessageSubtype()){
case REQUESTMETADATA: {
sendWorldMetadata(connectionHandler);
TerrainProtocol.sendWorldMetadata(connectionHandler);
} break;
case REQUESTCHUNKDATA: {
LoggerInterface.loggerNetworking.DEBUG("(Server) Received request for terrain " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
// System.out.println("Received request for terrain " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
sendWorldSubChunk(connectionHandler,
TerrainProtocol.sendWorldSubChunk(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ()
);
} break;
case REQUESTEDITVOXEL: {
attemptTerrainEdit(connectionHandler, message);
TerrainProtocol.attemptTerrainEdit(connectionHandler, message);
} break;
case REQUESTUSETERRAINPALETTE: {
attemptUseTerrainEditPalette(connectionHandler, message);
TerrainProtocol.attemptUseTerrainEditPalette(connectionHandler, message);
} break;
case REQUESTFLUIDDATA: {
LoggerInterface.loggerNetworking.DEBUG("(Server) Received request for fluid " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
// System.out.println("Received request for fluid " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
sendWorldFluidSubChunk(connectionHandler,
TerrainProtocol.sendWorldFluidSubChunk(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ()
);
} break;

View File

@ -185,4 +185,13 @@ public class TextureInstancedActor {
model.setWorldPos(worldPos);
}
}
/**
* Frees the texture instanced actor
*/
public void free(){
if(this.queuedTexture != null && this.queuedTexture.getTexture() != null){
Globals.assetManager.queueTextureForDeletion(this.queuedTexture.getTexture().getPath());
}
}
}

View File

@ -653,8 +653,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
*/
public void editChunk(Vector3i worldPosition, Vector3i voxelPosition, float weight, int type) {
terrainEditLock.acquireUninterruptibly();
//update terrain
serverTerrainManager.deformTerrainAtLocationToValue(worldPosition, voxelPosition, weight, type);
List<Vector3i> worldPositionsToUpdate = new LinkedList<Vector3i>();
worldPositionsToUpdate.add(worldPosition);
if(voxelPosition.x < 1){
@ -664,32 +662,45 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(1,1,1));
}
} else {
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(1,0,1));
}
}
} else {
if(voxelPosition.y < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(0,1,0));
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(0,1,1));
}
} else {
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(0,0,1));
}
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(1,0,1));
}
}
if(voxelPosition.y < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(0,1,0));
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(0,1,1));
}
}
if(voxelPosition.z < 1){
worldPositionsToUpdate.add(new Vector3i(worldPosition).sub(0,0,1));
}
//update all loaded cells
for(Vector3i toUpdate : worldPositionsToUpdate){
ServerDataCell cell = groundDataCells.get(getServerDataCellKey(toUpdate));
if(cell != null){
this.createTerrainPhysicsEntities(toUpdate);
cell.broadcastNetworkMessage(TerrainMessage.constructUpdateVoxelMessage(
worldPosition.x, worldPosition.y, worldPosition.z,
voxelPosition.x, voxelPosition.y, voxelPosition.z,
weight, type));
if(
toUpdate.x >= 0 && toUpdate.x < this.serverWorldData.getWorldSizeDiscrete() &&
toUpdate.y >= 0 && toUpdate.y < this.serverWorldData.getWorldSizeDiscrete() &&
toUpdate.z >= 0 && toUpdate.z < this.serverWorldData.getWorldSizeDiscrete()
){
//update terrain
int localVoxelX = voxelPosition.x + (ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 1) * (worldPosition.x - toUpdate.x);
int localVoxelY = voxelPosition.y + (ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 1) * (worldPosition.y - toUpdate.y);
int localVoxelZ = voxelPosition.z + (ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 1) * (worldPosition.z - toUpdate.z);
serverTerrainManager.deformTerrainAtLocationToValue(toUpdate, new Vector3i(localVoxelX, localVoxelY, localVoxelZ), weight, type);
//update anything loaded
ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(toUpdate));
if(cell != null){
//update physics
this.createTerrainPhysicsEntities(toUpdate);
//broadcast update
cell.broadcastNetworkMessage(TerrainMessage.constructUpdateVoxelMessage(
toUpdate.x, toUpdate.y, toUpdate.z,
localVoxelX, localVoxelY, localVoxelZ,
weight, type));
}
}
}
terrainEditLock.release();

View File

@ -223,7 +223,7 @@ public class WorldOctTree <T> {
){
throw new Error("Trying to search for node outside tree range!");
}
WorldOctTreeNode<T> searchResult = recursiveSearchUnsafe(root,position,maxLevel);
WorldOctTreeNode<T> searchResult = this.recursiveSearchUnsafe(root,position,maxLevel);
if(!returnNonLeaf && !searchResult.isLeaf()){
return null;
}