break out room solver
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-16 17:05:13 -04:00
parent d1ab990a63
commit 5def6da2de
4 changed files with 103 additions and 87 deletions

View File

@ -1851,6 +1851,7 @@ Script engine direct access to joml vectors
Script engine passing objects back into methods successfully Script engine passing objects back into methods successfully
Room detection within structures Room detection within structures
Structure metadata organization Structure metadata organization
Break out room solver

View File

@ -0,0 +1,100 @@
package electrosphere.client.block.solver;
import java.util.LinkedList;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.block.ClientBlockSelection;
import electrosphere.client.interact.select.AreaSelection;
import electrosphere.client.interact.select.AreaSelection.AreaSelectionType;
import electrosphere.client.scene.ClientWorldData;
import electrosphere.data.block.fab.RoomMetadata;
import electrosphere.data.block.fab.StructureMetadata;
/**
* Solves for the rooms in a structure
*/
public class RoomSolver {
/**
* Maximum radius of room in blocks
*/
public static final int MAX_ROOM_SIZE = 20;
/**
* 6-connected neighbors (orthogonal)
*/
static final int[][] NEIGHBORS = {
{ 1, 0, 0}, {-1, 0, 0},
{ 0, 1, 0}, { 0,-1, 0},
{ 0, 0, 1}, { 0, 0,-1}
};
/**
* Computes rooms from the currently selected voxels
*/
public static void computeRoomsFromSelection(AreaSelection boundingArea, StructureMetadata structureMetadata){
if(boundingArea.getType() != AreaSelectionType.RECTANGULAR){
throw new Error("Unsupported type! " + boundingArea.getType());
}
int dimX = (int)Math.ceil((boundingArea.getRectEnd().x - boundingArea.getRectStart().x) * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
int dimY = (int)Math.ceil((boundingArea.getRectEnd().y - boundingArea.getRectStart().y) * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
int dimZ = (int)Math.ceil((boundingArea.getRectEnd().z - boundingArea.getRectStart().z) * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
int[][][] distField = ClientBlockSelection.computeCavitySDF(boundingArea);
LinkedList<Vector3i> localMaximums = new LinkedList<Vector3i>();
for(int x = 0; x < dimX; x++){
for(int y = 0; y < dimY; y++){
for(int z = 0; z < dimZ; z++){
//don't consider edges of SDF
if(x == 0 || y == 0 || z == 0 || x == dimX - 1 || y == dimY - 1 || z == dimZ - 1){
continue;
}
if(
distField[x][y][z] >= distField[x-1][y][z] &&
distField[x][y][z] >= distField[x+1][y][z] &&
distField[x][y][z] >= distField[x][y-1][z] &&
distField[x][y][z] >= distField[x][y+1][z] &&
distField[x][y][z] >= distField[x][y][z-1] &&
distField[x][y][z] >= distField[x][y][z+1]
){
localMaximums.add(new Vector3i(x,y,z));
}
}
}
}
while(localMaximums.size() > 0){
Vector3i toConsider = localMaximums.poll();
Vector3d selectionStart = new Vector3d(boundingArea.getRectStart()).add(
toConsider.x * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
toConsider.y * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
toConsider.z * BlockChunkData.BLOCK_SIZE_MULTIPLIER
);
//make sure it's not already interecting any solved rooms
boolean contained = false;
for(RoomMetadata room : structureMetadata.getRooms()){
if(room.getArea().containsPoint(selectionStart)){
contained = true;
break;
}
}
if(contained){
continue;
}
//create the new room
Vector3i roomCenterChunkPos = ClientWorldData.convertRealToChunkSpace(selectionStart);
Vector3i roomCenterBlockPos = ClientWorldData.convertRealToLocalBlockSpace(selectionStart);
AreaSelection roomArea = AreaSelection.selectRectangularBlockCavity(roomCenterChunkPos, roomCenterBlockPos, MAX_ROOM_SIZE);
structureMetadata.getRooms().add(new RoomMetadata(roomArea));
}
}
}

View File

@ -5,6 +5,7 @@ import java.io.File;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.client.block.ClientBlockSelection; import electrosphere.client.block.ClientBlockSelection;
import electrosphere.client.block.solver.RoomSolver;
import electrosphere.client.interact.select.AreaSelection; import electrosphere.client.interact.select.AreaSelection;
import electrosphere.data.block.fab.BlockFab; import electrosphere.data.block.fab.BlockFab;
import electrosphere.data.block.fab.BlockFabMetadata; import electrosphere.data.block.fab.BlockFabMetadata;
@ -40,7 +41,7 @@ public class ImGuiStructureTab {
} }
} }
if(Globals.clientState.currentStructureData != null && ImGui.button("Calculate Rooms")){ if(Globals.clientState.currentStructureData != null && ImGui.button("Calculate Rooms")){
Globals.clientState.currentStructureData.computeRoomsFromSelection(); RoomSolver.computeRoomsFromSelection(Globals.cursorState.getAreaSelection(),Globals.clientState.currentStructureData);
} }
if(ImGui.button("Convert current selection to room")){ if(ImGui.button("Convert current selection to room")){
AreaSelection currentSelection = Globals.cursorState.getAreaSelection(); AreaSelection currentSelection = Globals.cursorState.getAreaSelection();

View File

@ -3,25 +3,13 @@ package electrosphere.data.block.fab;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.block.ClientBlockSelection;
import electrosphere.client.interact.select.AreaSelection; import electrosphere.client.interact.select.AreaSelection;
import electrosphere.client.interact.select.AreaSelection.AreaSelectionType;
import electrosphere.client.scene.ClientWorldData;
/** /**
* Structure data * Structure data
*/ */
public class StructureMetadata { public class StructureMetadata {
/**
* Maximum radius of room in blocks
*/
public static final int MAX_ROOM_SIZE = 20;
/** /**
* The bounding area of the structure * The bounding area of the structure
*/ */
@ -44,80 +32,6 @@ public class StructureMetadata {
return rVal; return rVal;
} }
/**
* 6-connected neighbors (orthogonal)
*/
static final int[][] NEIGHBORS = {
{ 1, 0, 0}, {-1, 0, 0},
{ 0, 1, 0}, { 0,-1, 0},
{ 0, 0, 1}, { 0, 0,-1}
};
/**
* Computes rooms from the currently selected voxels
*/
public void computeRoomsFromSelection(){
if(boundingArea.getType() != AreaSelectionType.RECTANGULAR){
throw new Error("Unsupported type! " + boundingArea.getType());
}
int dimX = (int)Math.ceil((boundingArea.getRectEnd().x - boundingArea.getRectStart().x) * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
int dimY = (int)Math.ceil((boundingArea.getRectEnd().y - boundingArea.getRectStart().y) * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
int dimZ = (int)Math.ceil((boundingArea.getRectEnd().z - boundingArea.getRectStart().z) * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
int[][][] distField = ClientBlockSelection.computeCavitySDF(boundingArea);
LinkedList<Vector3i> localMaximums = new LinkedList<Vector3i>();
for(int x = 0; x < dimX; x++){
for(int y = 0; y < dimY; y++){
for(int z = 0; z < dimZ; z++){
//don't consider edges of SDF
if(x == 0 || y == 0 || z == 0 || x == dimX - 1 || y == dimY - 1 || z == dimZ - 1){
continue;
}
if(
distField[x][y][z] >= distField[x-1][y][z] &&
distField[x][y][z] >= distField[x+1][y][z] &&
distField[x][y][z] >= distField[x][y-1][z] &&
distField[x][y][z] >= distField[x][y+1][z] &&
distField[x][y][z] >= distField[x][y][z-1] &&
distField[x][y][z] >= distField[x][y][z+1]
){
localMaximums.add(new Vector3i(x,y,z));
}
}
}
}
while(localMaximums.size() > 0){
Vector3i toConsider = localMaximums.poll();
Vector3d selectionStart = new Vector3d(boundingArea.getRectStart()).add(
toConsider.x * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
toConsider.y * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
toConsider.z * BlockChunkData.BLOCK_SIZE_MULTIPLIER
);
//make sure it's not already interecting any solved rooms
boolean contained = false;
for(RoomMetadata room : rooms){
if(room.getArea().containsPoint(selectionStart)){
contained = true;
break;
}
}
if(contained){
continue;
}
//create the new room
Vector3i roomCenterChunkPos = ClientWorldData.convertRealToChunkSpace(selectionStart);
Vector3i roomCenterBlockPos = ClientWorldData.convertRealToLocalBlockSpace(selectionStart);
AreaSelection roomArea = AreaSelection.selectRectangularBlockCavity(roomCenterChunkPos, roomCenterBlockPos, MAX_ROOM_SIZE);
rooms.add(new RoomMetadata(roomArea));
}
}
/** /**
* Gets the bounding area * Gets the bounding area
* @return The bounding area * @return The bounding area