room detection within structures
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-16 16:37:05 -04:00
parent d656d75535
commit 2fc984c3f1
7 changed files with 449 additions and 158 deletions

View File

@ -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

View File

@ -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
*/

View File

@ -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<Long> openSet = new LinkedList<Long>();
Map<Long,Integer> closedSet = new HashMap<Long,Integer>();
//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;
}
}

View File

@ -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<AreaSelection> rooms = new LinkedList<AreaSelection>();
/**
* 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<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(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<AreaSelection> getRooms(){
return rooms;
}
}

View File

@ -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);
}
}

View File

@ -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){

View File

@ -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,121 +77,144 @@ 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<HitboxCollectionState> hitboxStates = new LinkedList<HitboxCollectionState>(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);
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
);
}
}
}
}
//
//Draw grid alignment data
if(Globals.gameConfigCurrent.getSettings().getGraphicsDebugDrawGridAlignment()){
Model physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCUBE);
for(Entity entity : Globals.clientState.clientSceneWrapper.getScene().getEntityList()){
CommonEntityType data = CommonEntityUtils.getCommonData(entity);
if(data == null){
continue;
}
if(data.getGridAlignedData() != null){
GridAlignedData gridAlignedData = data.getGridAlignedData();
Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png");
if(texture != null){
texture.bind(openGLState);
}
Vector3d position = EntityUtils.getPosition(entity);
//calculate camera-modified vector3d
Vector3d cameraModifiedPosition = new Vector3d(position).add(0,gridAlignedData.getHeight() * BlockChunkData.BLOCK_SIZE_MULTIPLIER / 2.0f,0).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera));
modelTransformMatrix.identity();
modelTransformMatrix.translate(cameraModifiedPosition);
modelTransformMatrix.rotate(EntityUtils.getRotation(entity));
modelTransformMatrix.scale(
gridAlignedData.getWidth() * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
gridAlignedData.getHeight() * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
gridAlignedData.getLength() * BlockChunkData.BLOCK_SIZE_MULTIPLIER
);
physicsGraphicsModel.setModelMatrix(modelTransformMatrix);
physicsGraphicsModel.draw(renderPipelineState,openGLState);
}
}
}
//update pipeline state to use mats again
renderPipelineState.setUseMaterial(true);
if(Globals.gameConfigCurrent.getSettings().graphicsDebugDrawNavmesh()){
throw new Error("Not yet implemented!");
}
debugBonesPipeline.render(openGLState, renderPipelineState);
Globals.profiler.endCpuSample();
}
/**
* Gets the color texture to use to draw a hitbox
* @param shapeStatus The hitbox status
* @param data The hitbox data
* @return The texture path to use
*/
private static String getHitboxColor(HitboxState shapeStatus, HitboxData data){
switch(data.getType()){
case HitboxData.HITBOX_TYPE_BLOCK_CONNECTED: {
if(shapeStatus.getHadCollision()){
return "Textures/color/transparent_yellow.png";
}
if(shapeStatus.isActive()){
return "Textures/transparent_blue.png";
}
return "Textures/transparent_grey.png";
}
case HitboxData.HITBOX_TYPE_HIT:
case HitboxData.HITBOX_TYPE_HIT_CONNECTED: {
if(shapeStatus.getHadCollision()){
return "Textures/color/transparent_yellow.png";
}
if(shapeStatus.isActive()){
if(shapeStatus.isBlockOverride()){
return "Textures/transparent_blue.png";
}
return "Textures/transparent_red.png";
}
return "Textures/transparent_grey.png";
}
case HitboxData.HITBOX_TYPE_HURT:
case HitboxData.HITBOX_TYPE_HURT_CONNECTED: {
if(shapeStatus.getHadCollision()){
return "Textures/color/transparent_yellow.png";
}
return "Textures/transparent_grey.png";
}
}
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){
@ -254,91 +278,89 @@ public class DebugContentPipeline implements RenderPipeline {
}
}
}
}
//
//Draw grid alignment data
if(Globals.gameConfigCurrent.getSettings().getGraphicsDebugDrawGridAlignment()){
Model physicsGraphicsModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCUBE);
for(Entity entity : Globals.clientState.clientSceneWrapper.getScene().getEntityList()){
CommonEntityType data = CommonEntityUtils.getCommonData(entity);
if(data == null){
continue;
}
if(data.getGridAlignedData() != null){
GridAlignedData gridAlignedData = data.getGridAlignedData();
Texture texture = Globals.assetManager.fetchTexture("Textures/transparent_blue.png");
/**
* 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 = EntityUtils.getPosition(entity);
Vector3d position = PhysicsUtils.odeVecToJomlVec(sphereView.getPosition());
//calculate camera-modified vector3d
Vector3d cameraModifiedPosition = new Vector3d(position).add(0,gridAlignedData.getHeight() * BlockChunkData.BLOCK_SIZE_MULTIPLIER / 2.0f,0).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera));
Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera));
modelTransformMatrix.identity();
modelTransformMatrix.translate(cameraModifiedPosition);
modelTransformMatrix.rotate(EntityUtils.getRotation(entity));
modelTransformMatrix.scale(
gridAlignedData.getWidth() * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
gridAlignedData.getHeight() * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
gridAlignedData.getLength() * BlockChunkData.BLOCK_SIZE_MULTIPLIER
);
physicsGraphicsModel.setModelMatrix(modelTransformMatrix);
physicsGraphicsModel.draw(renderPipelineState,openGLState);
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);
}
}
}
//update pipeline state to use mats again
renderPipelineState.setUseMaterial(true);
if(Globals.gameConfigCurrent.getSettings().graphicsDebugDrawNavmesh()){
throw new Error("Not yet implemented!");
}
debugBonesPipeline.render(openGLState, renderPipelineState);
Globals.profiler.endCpuSample();
}
/**
* Gets the color texture to use to draw a hitbox
* @param shapeStatus The hitbox status
* @param data The hitbox data
* @return The texture path to use
* 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
*/
private String getHitboxColor(HitboxState shapeStatus, HitboxData data){
switch(data.getType()){
case HitboxData.HITBOX_TYPE_BLOCK_CONNECTED: {
if(shapeStatus.getHadCollision()){
return "Textures/color/transparent_yellow.png";
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);
}
if(shapeStatus.isActive()){
return "Textures/transparent_blue.png";
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);
}
return "Textures/transparent_grey.png";
}
case HitboxData.HITBOX_TYPE_HIT:
case HitboxData.HITBOX_TYPE_HIT_CONNECTED: {
if(shapeStatus.getHadCollision()){
return "Textures/color/transparent_yellow.png";
}
if(shapeStatus.isActive()){
if(shapeStatus.isBlockOverride()){
return "Textures/transparent_blue.png";
}
return "Textures/transparent_red.png";
}
return "Textures/transparent_grey.png";
}
case HitboxData.HITBOX_TYPE_HURT:
case HitboxData.HITBOX_TYPE_HURT_CONNECTED: {
if(shapeStatus.getHadCollision()){
return "Textures/color/transparent_yellow.png";
}
return "Textures/transparent_grey.png";
}
}
return "Textures/transparent_grey.png";
}
/**