From 2fc984c3f12cb8aa25f5597d41a7285b04e211eb Mon Sep 17 00:00:00 2001 From: austin Date: Fri, 16 May 2025 16:37:05 -0400 Subject: [PATCH] room detection within structures --- docs/src/progress/renderertodo.md | 1 + .../electrosphere/client/ClientState.java | 6 + .../client/block/ClientBlockSelection.java | 101 ++++++ .../client/block/StructureData.java | 136 +++++++ .../client/interact/select/AreaSelection.java | 16 + .../ui/menu/editor/ImGuiStructureTab.java | 9 + .../pipelines/debug/DebugContentPipeline.java | 338 ++++++++++-------- 7 files changed, 449 insertions(+), 158 deletions(-) create mode 100644 src/main/java/electrosphere/client/block/StructureData.java diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 14c004ee..123a6697 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1849,6 +1849,7 @@ Fix caching with deleted source files Proof of concept of ui button calling engine code Script engine direct access to joml vectors Script engine passing objects back into methods successfully +Room detection within structures diff --git a/src/main/java/electrosphere/client/ClientState.java b/src/main/java/electrosphere/client/ClientState.java index 98af799d..3d19c6c7 100644 --- a/src/main/java/electrosphere/client/ClientState.java +++ b/src/main/java/electrosphere/client/ClientState.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import org.joml.Vector3f; import electrosphere.client.block.ClientBlockManager; +import electrosphere.client.block.StructureData; import electrosphere.client.block.cells.ClientBlockCellManager; import electrosphere.client.chemistry.ClientChemistryCollisionCallback; import electrosphere.client.entity.character.ClientCharacterManager; @@ -187,6 +188,11 @@ public class ClientState { */ public int openInventoriesCount = 0; + /** + * The currently selected structure's data + */ + public StructureData currentStructureData; + /** * Constructor */ diff --git a/src/main/java/electrosphere/client/block/ClientBlockSelection.java b/src/main/java/electrosphere/client/block/ClientBlockSelection.java index af8c2c57..b8e75af7 100644 --- a/src/main/java/electrosphere/client/block/ClientBlockSelection.java +++ b/src/main/java/electrosphere/client/block/ClientBlockSelection.java @@ -1,14 +1,19 @@ package electrosphere.client.block; import java.io.File; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; import org.joml.Vector3d; import org.joml.Vector3i; import electrosphere.client.interact.select.AreaSelection; +import electrosphere.client.interact.select.AreaSelection.AreaSelectionType; import electrosphere.client.scene.ClientWorldData; import electrosphere.data.block.BlockFab; import electrosphere.engine.Globals; +import electrosphere.util.math.HashUtils; /** * Class for selecting blocks on the client @@ -128,4 +133,100 @@ public class ClientBlockSelection { return fab; } + + /** + * 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 the SDF of distance from nearest solid block for the selected area + * @param boundingArea The bounding area + * @return The sdf + */ + public static int[][][] computeCavitySDF(AreaSelection boundingArea){ + 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); + + Vector3d currPos = new Vector3d(); + + //compute dist field + int[][][] distField = new int[dimX][dimY][dimZ]; + + + //sets for breadth-first search + LinkedList openSet = new LinkedList(); + Map closedSet = new HashMap(); + + //enqueue all positions + for(int x = 0; x < dimX; x++){ + for(int y = 0; y < dimY; y++){ + for(int z = 0; z < dimZ; z++){ + currPos.set(boundingArea.getRectStart()).add( + x * BlockChunkData.BLOCK_SIZE_MULTIPLIER, + y * BlockChunkData.BLOCK_SIZE_MULTIPLIER, + z * BlockChunkData.BLOCK_SIZE_MULTIPLIER + ); + Vector3i chunkPos = ClientWorldData.convertRealToChunkSpace(currPos); + Vector3i blockPos = ClientWorldData.convertRealToLocalBlockSpace(currPos); + BlockChunkData chunkData = Globals.clientState.clientBlockManager.getChunkDataAtWorldPoint(chunkPos, BlockChunkData.LOD_FULL_RES); + if(chunkData == null){ + throw new Error("Missing chunk! " + chunkPos); + } + short type = chunkData.getType(blockPos.x, blockPos.y, blockPos.z); + if(type == BlockChunkData.BLOCK_TYPE_EMPTY){ + } else { + openSet.add(HashUtils.hashIVec(x, y, z)); + closedSet.put(HashUtils.hashIVec(x, y, z),1); + } + } + } + } + + //error check initialization + if(closedSet.size() < 1){ + throw new Error("Failed to detect empty chunks!"); + } + + //search + while(openSet.size() > 0){ + Long hash = openSet.poll(); + int x = HashUtils.unhashIVec(hash, HashUtils.UNHASH_COMPONENT_X); + int y = HashUtils.unhashIVec(hash, HashUtils.UNHASH_COMPONENT_Y); + int z = HashUtils.unhashIVec(hash, HashUtils.UNHASH_COMPONENT_Z); + int currVal = closedSet.get(hash); + + for(int[] dir : NEIGHBORS) { + int nx = x + dir[0]; + int ny = y + dir[1]; + int nz = z + dir[2]; + + if( + nx >= 0 && ny >= 0 && nz >= 0 && + nx < dimX && ny < dimY && nz < dimZ + ){ + long nHash = HashUtils.hashIVec(nx, ny, nz); + if(!closedSet.containsKey(nHash)){ + //evaluate all neighbors of this neighbor + openSet.add(nHash); + //store dist of neighbor + int neighborVal = currVal + 1; + closedSet.put(nHash, neighborVal); + distField[nx][ny][nz] = neighborVal; + } + } + } + } + + return distField; + } + } diff --git a/src/main/java/electrosphere/client/block/StructureData.java b/src/main/java/electrosphere/client/block/StructureData.java new file mode 100644 index 00000000..79d29723 --- /dev/null +++ b/src/main/java/electrosphere/client/block/StructureData.java @@ -0,0 +1,136 @@ +package electrosphere.client.block; + +import java.util.LinkedList; +import java.util.List; + +import org.joml.Vector3d; +import org.joml.Vector3i; + +import electrosphere.client.interact.select.AreaSelection; +import electrosphere.client.interact.select.AreaSelection.AreaSelectionType; +import electrosphere.client.scene.ClientWorldData; + +/** + * Structure data + */ +public class StructureData { + + /** + * Maximum radius of room in blocks + */ + public static final int MAX_ROOM_SIZE = 20; + + /** + * The bounding area of the structure + */ + AreaSelection boundingArea; + + /** + * The rooms defined within the structure + */ + List rooms = new LinkedList(); + + + /** + * Creatures a structure data object + * @param boundingArea The bounding area + * @return The structure data + */ + public static StructureData create(AreaSelection boundingArea){ + StructureData rVal = new StructureData(); + rVal.boundingArea = boundingArea; + 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 localMaximums = new LinkedList(); + 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(AreaSelection room : rooms){ + if(room.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(roomArea); + } + + } + + /** + * Gets the bounding area + * @return The bounding area + */ + public AreaSelection getBoundingArea(){ + return boundingArea; + } + + /** + * Gets the list of areas that encompass rooms + * @return The list of areas + */ + public List getRooms(){ + return rooms; + } + +} + diff --git a/src/main/java/electrosphere/client/interact/select/AreaSelection.java b/src/main/java/electrosphere/client/interact/select/AreaSelection.java index 5ad33c2d..f22169d0 100644 --- a/src/main/java/electrosphere/client/interact/select/AreaSelection.java +++ b/src/main/java/electrosphere/client/interact/select/AreaSelection.java @@ -1,6 +1,7 @@ package electrosphere.client.interact.select; import org.graalvm.polyglot.HostAccess.Export; +import org.joml.AABBd; import org.joml.Vector3d; import org.joml.Vector3i; @@ -42,6 +43,11 @@ public class AreaSelection { */ private Vector3d rectEnd; + /** + * The AABB of the area selection + */ + private AABBd aabb; + /** * Creates a rectangular selection * @param start The start point @@ -62,6 +68,7 @@ public class AreaSelection { rVal.type = AreaSelectionType.RECTANGULAR; rVal.rectStart = start; rVal.rectEnd = end; + rVal.aabb = new AABBd(start, end); return rVal; } @@ -369,4 +376,13 @@ public class AreaSelection { return rectEnd; } + /** + * Checks if the area contains a point + * @param point The point + * @return true if it contains the point, false otherwise + */ + public boolean containsPoint(Vector3d point){ + return aabb.testPoint(point); + } + } diff --git a/src/main/java/electrosphere/client/ui/menu/editor/ImGuiStructureTab.java b/src/main/java/electrosphere/client/ui/menu/editor/ImGuiStructureTab.java index 6d193528..fa5a25d1 100644 --- a/src/main/java/electrosphere/client/ui/menu/editor/ImGuiStructureTab.java +++ b/src/main/java/electrosphere/client/ui/menu/editor/ImGuiStructureTab.java @@ -5,6 +5,7 @@ import java.io.File; import org.joml.Vector3d; import electrosphere.client.block.ClientBlockSelection; +import electrosphere.client.block.StructureData; import electrosphere.client.interact.select.AreaSelection; import electrosphere.data.block.BlockFab; import electrosphere.data.block.BlockFabMetadata; @@ -33,6 +34,14 @@ public class ImGuiStructureTab { } } else { BlockFab currentFab = Globals.clientState.clientLevelEditorData.getCurrentFab(); + if(Globals.clientState.currentStructureData == null){ + if(ImGui.button("Create Structure Data")){ + Globals.clientState.currentStructureData = StructureData.create(Globals.cursorState.getAreaSelection()); + } + } + if(Globals.clientState.currentStructureData != null && ImGui.button("Calculate Rooms")){ + Globals.clientState.currentStructureData.computeRoomsFromSelection(); + } if(ImGui.button("Convert current selection to room")){ AreaSelection currentSelection = Globals.cursorState.getAreaSelection(); if(currentSelection != null){ diff --git a/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java index 368fd126..bad7982b 100644 --- a/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java @@ -13,6 +13,7 @@ import org.ode4j.ode.DSphere; import electrosphere.client.block.BlockChunkData; import electrosphere.client.entity.camera.CameraEntityUtils; +import electrosphere.client.interact.select.AreaSelection; import electrosphere.collision.CollisionEngine; import electrosphere.collision.PhysicsUtils; import electrosphere.collision.collidable.Collidable; @@ -76,181 +77,43 @@ public class DebugContentPipeline implements RenderPipeline { Matrix4d modelTransformMatrix = new Matrix4d(); if(Globals.gameConfigCurrent.getSettings().getGraphicsDebugDrawCollisionSpheresClient()){ - Model hitboxModel; for(HitboxCollectionState hitboxState : Globals.clientState.clientSceneWrapper.getHitboxManager().getAllHitboxes()){ - for(DGeom geom : hitboxState.getGeometries()){ - if(geom instanceof DSphere){ - DSphere sphereView = (DSphere)geom; - HitboxState shapeStatus = hitboxState.getShapeStatus(geom); - if((hitboxModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITSPHERE)) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = PhysicsUtils.odeVecToJomlVec(sphereView.getPosition()); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - modelTransformMatrix.scale(sphereView.getRadius() * 2); - hitboxModel.setModelMatrix(modelTransformMatrix); - hitboxModel.draw(renderPipelineState,openGLState); - } - } - if(geom instanceof DCapsule){ - DCapsule capsuleView = (DCapsule)geom; - HitboxState shapeStatus = hitboxState.getShapeStatus(geom); - if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcapsule.glb")) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = PhysicsUtils.odeVecToJomlVec(capsuleView.getPosition()); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation - modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); - //the ode4j capsule's end caps are always at least radius length, the length only controls the distance between the two caps. - //unfortunately that won't be easy to replicate with rendering tech currently; instead, run logic below - double radius = capsuleView.getRadius(); - double length = capsuleView.getLength(); - if(length < radius) length = radius; - modelTransformMatrix.scale(radius,length,radius); - hitboxModel.setModelMatrix(modelTransformMatrix); - hitboxModel.draw(renderPipelineState,openGLState); - } - } - } + DebugContentPipeline.renderHitboxes(openGLState, renderPipelineState, modelTransformMatrix, hitboxState); } } if(Globals.gameConfigCurrent.getSettings().getGraphicsDebugDrawCollisionSpheresServer()){ - Model hitboxModel; int serverIdForClientEntity = Globals.clientState.clientSceneWrapper.mapClientToServerId(Globals.clientState.playerEntity.getId()); Entity serverPlayerEntity = EntityLookupUtils.getEntityById(serverIdForClientEntity); Realm playerRealm = Globals.serverState.realmManager.getEntityRealm(serverPlayerEntity); List hitboxStates = new LinkedList(playerRealm.getHitboxManager().getAllHitboxes()); for(HitboxCollectionState hitboxState : hitboxStates){ - for(DGeom geom : hitboxState.getGeometries()){ - if(geom instanceof DSphere){ - DSphere sphereView = (DSphere)geom; - HitboxState shapeStatus = hitboxState.getShapeStatus(geom); - if((hitboxModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITSPHERE)) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = PhysicsUtils.odeVecToJomlVec(sphereView.getPosition()); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - modelTransformMatrix.scale(sphereView.getRadius() * 2); - hitboxModel.setModelMatrix(modelTransformMatrix); - hitboxModel.draw(renderPipelineState,openGLState); - } - } - if(geom instanceof DCapsule){ - DCapsule capsuleView = (DCapsule)geom; - HitboxState shapeStatus = hitboxState.getShapeStatus(geom); - if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcapsule.glb")) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = PhysicsUtils.odeVecToJomlVec(capsuleView.getPosition()); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation - modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); - //the ode4j capsule's end caps are always at least radius length, the length only controls the distance between the two caps. - //unfortunately that won't be easy to replicate with rendering tech currently; instead, run logic below - double radius = capsuleView.getRadius(); - double length = capsuleView.getLength(); - if(length < radius) length = radius; - modelTransformMatrix.scale(radius,length,radius); - hitboxModel.setModelMatrix(modelTransformMatrix); - hitboxModel.draw(renderPipelineState,openGLState); - } - } - } + DebugContentPipeline.renderHitboxes(openGLState, renderPipelineState, modelTransformMatrix, hitboxState); } } if(Globals.gameConfigCurrent.getSettings().graphicsDebugDrawPhysicsObjects()){ - Model physicsGraphicsModel; CollisionEngine engine = Globals.clientState.clientSceneWrapper.getCollisionEngine(); for(Collidable collidable : engine.getCollidables()){ Entity physicsEntity = collidable.getParent(); if((boolean)physicsEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && physicsEntity.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE) != null){ CollidableTemplate template = (CollidableTemplate)physicsEntity.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE); - switch(template.getType()){ - case CollidableTemplate.COLLIDABLE_TYPE_CYLINDER: { - if((physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCYLINDER)) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png"); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = EntityUtils.getPosition(physicsEntity); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - modelTransformMatrix.rotate(EntityUtils.getRotation(physicsEntity)); - modelTransformMatrix.scale(template.getDimension1(),template.getDimension2() * 0.5,template.getDimension3()); - physicsGraphicsModel.setModelMatrix(modelTransformMatrix); - physicsGraphicsModel.draw(renderPipelineState,openGLState); - } - } break; - case CollidableTemplate.COLLIDABLE_TYPE_CUBE: { - if((physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCUBE)) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png"); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = EntityUtils.getPosition(physicsEntity); - Quaterniond rotation = EntityUtils.getRotation(physicsEntity); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - modelTransformMatrix.rotate(rotation); - modelTransformMatrix.scale(template.getDimension1(),template.getDimension2(),template.getDimension3()); - physicsGraphicsModel.setModelMatrix(modelTransformMatrix); - physicsGraphicsModel.draw(renderPipelineState,openGLState); - } - } break; - case CollidableTemplate.COLLIDABLE_TYPE_CAPSULE: { - if((physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCAPSULE)) != null){ - //set color based on collision status, type, etc - Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png"); - if(texture != null){ - texture.bind(openGLState); - } - Vector3d position = EntityUtils.getPosition(physicsEntity); - Quaterniond rotation = EntityUtils.getRotation(physicsEntity); - //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).add(template.getOffsetX(),template.getDimension1() + template.getOffsetY(),template.getOffsetZ()).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(cameraModifiedPosition); - modelTransformMatrix.rotate(rotation); - modelTransformMatrix.scale(template.getDimension1(),template.getDimension2() * 0.5 + template.getDimension1() + template.getDimension1(),template.getDimension3()); - physicsGraphicsModel.setModelMatrix(modelTransformMatrix); - physicsGraphicsModel.draw(renderPipelineState,openGLState); - } - } break; - default: { - throw new Error("Unsupported shape type!"); - } + DebugContentPipeline.renderCollidable(openGLState, renderPipelineState, modelTransformMatrix, physicsEntity, template); + } + } + } + + if(Globals.clientState.currentStructureData != null){ + if(Globals.clientState.currentStructureData.getBoundingArea() != null){ + DebugContentPipeline.renderAreaSelection( + openGLState, renderPipelineState, modelTransformMatrix, + Globals.clientState.currentStructureData.getBoundingArea(), AssetDataStrings.TEXTURE_RED_TRANSPARENT + ); + if(Globals.clientState.currentStructureData.getRooms() != null){ + for(AreaSelection roomArea : Globals.clientState.currentStructureData.getRooms()){ + DebugContentPipeline.renderAreaSelection( + openGLState, renderPipelineState, modelTransformMatrix, + roomArea, AssetDataStrings.TEXTURE_TEAL_TRANSPARENT + ); } } } @@ -306,7 +169,7 @@ public class DebugContentPipeline implements RenderPipeline { * @param data The hitbox data * @return The texture path to use */ - private String getHitboxColor(HitboxState shapeStatus, HitboxData data){ + private static String getHitboxColor(HitboxState shapeStatus, HitboxData data){ switch(data.getType()){ case HitboxData.HITBOX_TYPE_BLOCK_CONNECTED: { if(shapeStatus.getHadCollision()){ @@ -341,6 +204,165 @@ public class DebugContentPipeline implements RenderPipeline { return "Textures/transparent_grey.png"; } + /** + * Renders a collidable + * @param openGLState The opengl state + * @param renderPipelineState The render pipeline state + * @param modelTransformMatrix The model transform matrix + * @param physicsEntity The entity + * @param template The template + */ + static void renderCollidable(OpenGLState openGLState, RenderPipelineState renderPipelineState, Matrix4d modelTransformMatrix, Entity physicsEntity, CollidableTemplate template){ + Model physicsGraphicsModel; + if((boolean)physicsEntity.getData(EntityDataStrings.DATA_STRING_DRAW) && physicsEntity.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE) != null){ + switch(template.getType()){ + case CollidableTemplate.COLLIDABLE_TYPE_CYLINDER: { + if((physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCYLINDER)) != null){ + //set color based on collision status, type, etc + Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png"); + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = EntityUtils.getPosition(physicsEntity); + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.rotate(EntityUtils.getRotation(physicsEntity)); + modelTransformMatrix.scale(template.getDimension1(),template.getDimension2() * 0.5,template.getDimension3()); + physicsGraphicsModel.setModelMatrix(modelTransformMatrix); + physicsGraphicsModel.draw(renderPipelineState,openGLState); + } + } break; + case CollidableTemplate.COLLIDABLE_TYPE_CUBE: { + if((physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCUBE)) != null){ + //set color based on collision status, type, etc + Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png"); + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = EntityUtils.getPosition(physicsEntity); + Quaterniond rotation = EntityUtils.getRotation(physicsEntity); + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.rotate(rotation); + modelTransformMatrix.scale(template.getDimension1(),template.getDimension2(),template.getDimension3()); + physicsGraphicsModel.setModelMatrix(modelTransformMatrix); + physicsGraphicsModel.draw(renderPipelineState,openGLState); + } + } break; + case CollidableTemplate.COLLIDABLE_TYPE_CAPSULE: { + if((physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCAPSULE)) != null){ + //set color based on collision status, type, etc + Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png"); + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = EntityUtils.getPosition(physicsEntity); + Quaterniond rotation = EntityUtils.getRotation(physicsEntity); + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(position).add(template.getOffsetX(),template.getDimension1() + template.getOffsetY(),template.getOffsetZ()).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.rotate(rotation); + modelTransformMatrix.scale(template.getDimension1(),template.getDimension2() * 0.5 + template.getDimension1() + template.getDimension1(),template.getDimension3()); + physicsGraphicsModel.setModelMatrix(modelTransformMatrix); + physicsGraphicsModel.draw(renderPipelineState,openGLState); + } + } break; + default: { + throw new Error("Unsupported shape type!"); + } + } + } + } + + /** + * Renders a hitbox collection state + * @param openGLState The opengl state + * @param renderPipelineState The render pipeline state + * @param modelTransformMatrix The model transform matrix + * @param hitboxState The hitbox collection state + */ + static void renderHitboxes(OpenGLState openGLState, RenderPipelineState renderPipelineState, Matrix4d modelTransformMatrix, HitboxCollectionState hitboxState){ + Model hitboxModel; + for(DGeom geom : hitboxState.getGeometries()){ + if(geom instanceof DSphere){ + DSphere sphereView = (DSphere)geom; + HitboxState shapeStatus = hitboxState.getShapeStatus(geom); + if((hitboxModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITSPHERE)) != null){ + //set color based on collision status, type, etc + Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = PhysicsUtils.odeVecToJomlVec(sphereView.getPosition()); + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.scale(sphereView.getRadius() * 2); + hitboxModel.setModelMatrix(modelTransformMatrix); + hitboxModel.draw(renderPipelineState,openGLState); + } + } + if(geom instanceof DCapsule){ + DCapsule capsuleView = (DCapsule)geom; + HitboxState shapeStatus = hitboxState.getShapeStatus(geom); + if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcapsule.glb")) != null){ + //set color based on collision status, type, etc + Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = PhysicsUtils.odeVecToJomlVec(capsuleView.getPosition()); + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation + modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); + //the ode4j capsule's end caps are always at least radius length, the length only controls the distance between the two caps. + //unfortunately that won't be easy to replicate with rendering tech currently; instead, run logic below + double radius = capsuleView.getRadius(); + double length = capsuleView.getLength(); + if(length < radius) length = radius; + modelTransformMatrix.scale(radius,length,radius); + hitboxModel.setModelMatrix(modelTransformMatrix); + hitboxModel.draw(renderPipelineState,openGLState); + } + } + } + } + + /** + * Renders an area select + * @param openGLState The opengl state + * @param renderPipelineState The render pipeline state + * @param modelTransformMatrix The model transform matrix + * @param areaSelection The area selection + */ + static void renderAreaSelection(OpenGLState openGLState, RenderPipelineState renderPipelineState, Matrix4d modelTransformMatrix, AreaSelection areaSelection, String texturePath){ + Model model = Globals.assetManager.fetchModel(AssetDataStrings.UNITCUBE); + if(model != null){ + Texture texture = Globals.assetManager.fetchTexture(texturePath); + if(texture != null){ + texture.bind(openGLState); + } + Vector3d dims = new Vector3d(areaSelection.getRectEnd()).sub(areaSelection.getRectStart()); + Vector3d position = new Vector3d(areaSelection.getRectStart()).add(dims.x/2.0,dims.y/2.0,dims.z/2.0); + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.scale(dims); + model.setModelMatrix(modelTransformMatrix); + model.draw(renderPipelineState,openGLState); + } + } + /** * Gets the bone debugging pipeline * @return The bone debugging pipeline