capsule-block collision correction
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
9566bf2720
commit
cafc5f25c8
@ -388,9 +388,9 @@
|
||||
]
|
||||
},
|
||||
"collidable" : {
|
||||
"type" : "CYLINDER",
|
||||
"type" : "CAPSULE",
|
||||
"dimension1" : 0.35,
|
||||
"dimension2" : 1.6,
|
||||
"dimension2" : 0.7,
|
||||
"dimension3" : 0.35,
|
||||
"linearFriction": 0.001,
|
||||
"mass": 0.3,
|
||||
@ -399,7 +399,7 @@
|
||||
"rotZ": 0,
|
||||
"rotW": 1,
|
||||
"offsetX" : 0,
|
||||
"offsetY" : 0.8,
|
||||
"offsetY" : 0.7,
|
||||
"offsetZ" : 0,
|
||||
"angularlyStatic" : true
|
||||
},
|
||||
|
||||
@ -1838,6 +1838,9 @@ Fix virtual scrollable working with certain panels
|
||||
Humans use capsule shape now
|
||||
Physics numbers reworked
|
||||
|
||||
(05/16/2025)
|
||||
Capsule-BlockChunk collision correction in collidable trees
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.interact.select.AreaSelection;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.data.block.BlockFab;
|
||||
import electrosphere.engine.Globals;
|
||||
|
||||
@ -95,8 +96,8 @@ public class ClientBlockSelection {
|
||||
if(!startChunk.equals(endChunk)){
|
||||
throw new Error("Unsupported case! Selected are coverts multiple chunks.. " + startChunk + " " + endChunk);
|
||||
}
|
||||
Vector3i blockStart = Globals.clientState.clientWorldData.convertRealToBlockSpace(selection.getRectStart());
|
||||
Vector3i blockEnd = Globals.clientState.clientWorldData.convertRealToBlockSpace(selection.getRectEnd());
|
||||
Vector3i blockStart = ClientWorldData.convertRealToLocalBlockSpace(selection.getRectStart());
|
||||
Vector3i blockEnd = ClientWorldData.convertRealToLocalBlockSpace(selection.getRectEnd());
|
||||
|
||||
BlockChunkData chunk = Globals.clientState.clientBlockManager.getChunkDataAtWorldPoint(startChunk, 0);
|
||||
if(chunk == null){
|
||||
|
||||
@ -8,6 +8,7 @@ import java.util.Set;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.engine.Globals;
|
||||
@ -322,14 +323,14 @@ public class FluidCellManager {
|
||||
for(int z = -(int)drawRadius; z < drawRadius; z = z + ChunkData.CHUNK_DATA_SIZE){
|
||||
Vector3d newPos = new Vector3d(playerPos.x + x, playerPos.y + y, playerPos.z + z);
|
||||
Vector3i worldPos = new Vector3i(
|
||||
Globals.clientState.clientWorldData.convertRealToChunkSpace(newPos.x),
|
||||
Globals.clientState.clientWorldData.convertRealToChunkSpace(newPos.y),
|
||||
Globals.clientState.clientWorldData.convertRealToChunkSpace(newPos.z)
|
||||
ClientWorldData.convertRealToChunkSpace(newPos.x),
|
||||
ClientWorldData.convertRealToChunkSpace(newPos.y),
|
||||
ClientWorldData.convertRealToChunkSpace(newPos.z)
|
||||
);
|
||||
Vector3d chunkRealSpace = new Vector3d(
|
||||
Globals.clientState.clientWorldData.convertChunkToRealSpace(worldPos.x),
|
||||
Globals.clientState.clientWorldData.convertChunkToRealSpace(worldPos.y),
|
||||
Globals.clientState.clientWorldData.convertChunkToRealSpace(worldPos.z)
|
||||
ClientWorldData.convertChunkToRealSpace(worldPos.x),
|
||||
ClientWorldData.convertChunkToRealSpace(worldPos.y),
|
||||
ClientWorldData.convertChunkToRealSpace(worldPos.z)
|
||||
);
|
||||
if(
|
||||
playerPos.distance(chunkRealSpace) < drawRadius &&
|
||||
|
||||
@ -113,9 +113,9 @@ public class ClientFluidManager {
|
||||
public boolean containsChunkDataAtRealPoint(double x, double y, double z){
|
||||
assert clientWorldData != null;
|
||||
return fluidCache.containsChunkDataAtWorldPoint(
|
||||
clientWorldData.convertRealToChunkSpace(x),
|
||||
clientWorldData.convertRealToChunkSpace(y),
|
||||
clientWorldData.convertRealToChunkSpace(z)
|
||||
ClientWorldData.convertRealToChunkSpace(x),
|
||||
ClientWorldData.convertRealToChunkSpace(y),
|
||||
ClientWorldData.convertRealToChunkSpace(z)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import org.ode4j.ode.DBody;
|
||||
|
||||
import electrosphere.client.block.BlockChunkData;
|
||||
import electrosphere.client.entity.camera.CameraEntityUtils;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.ui.menu.ingame.InteractionTargetMenu;
|
||||
import electrosphere.collision.CollisionBodyCreation;
|
||||
@ -279,7 +280,7 @@ public class ClientInteractionEngine {
|
||||
//grab block at point
|
||||
BlockChunkData blockChunkData = Globals.clientState.clientBlockManager.getChunkDataAtWorldPoint(Globals.clientState.clientWorldData.convertRealToWorldSpace(collisionPosition), 0);
|
||||
if(blockChunkData != null){
|
||||
Vector3i blockPos = Globals.clientState.clientWorldData.convertRealToBlockSpace(new Vector3d(collisionPosition).add(new Vector3d(eyePos).mul(-BlockChunkData.BLOCK_SIZE_MULTIPLIER / 2.0f)));
|
||||
Vector3i blockPos = ClientWorldData.convertRealToLocalBlockSpace(new Vector3d(collisionPosition).add(new Vector3d(eyePos).mul(-BlockChunkData.BLOCK_SIZE_MULTIPLIER / 2.0f)));
|
||||
if(!blockChunkData.isEmpty(blockPos.x, blockPos.y, blockPos.z)){
|
||||
short type = blockChunkData.getType(blockPos.x, blockPos.y, blockPos.z);
|
||||
String text = Globals.gameConfigCurrent.getBlockData().getTypeFromId(type).getName();
|
||||
@ -293,7 +294,7 @@ public class ClientInteractionEngine {
|
||||
if(!set){
|
||||
ChunkData chunkData = Globals.clientState.clientTerrainManager.getChunkDataAtWorldPoint(Globals.clientState.clientWorldData.convertRealToWorldSpace(collisionPosition), 0);
|
||||
if(chunkData != null){
|
||||
int voxelType = chunkData.getType(Globals.clientState.clientWorldData.convertRealToVoxelSpace(new Vector3d(collisionPosition).add(new Vector3d(ServerTerrainChunk.VOXEL_SIZE / 2.0f))));
|
||||
int voxelType = chunkData.getType(ClientWorldData.convertRealToVoxelSpace(new Vector3d(collisionPosition).add(new Vector3d(ServerTerrainChunk.VOXEL_SIZE / 2.0f))));
|
||||
if(voxelType != ServerTerrainChunk.VOXEL_TYPE_AIR){
|
||||
String text = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(voxelType).getName();
|
||||
InteractionTargetMenu.setInteractionTargetString(text);
|
||||
|
||||
@ -64,11 +64,19 @@ public class ClientWorldData {
|
||||
}
|
||||
|
||||
|
||||
public int convertRealToChunkSpace(double real){
|
||||
public static int convertRealToChunkSpace(double real){
|
||||
return (int)Math.floor(real / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
}
|
||||
|
||||
public float convertChunkToRealSpace(int chunk){
|
||||
public static Vector3i convertRealToChunkSpace(Vector3d real){
|
||||
return new Vector3i(
|
||||
ClientWorldData.convertRealToChunkSpace(real.x),
|
||||
ClientWorldData.convertRealToChunkSpace(real.y),
|
||||
ClientWorldData.convertRealToChunkSpace(real.z)
|
||||
);
|
||||
}
|
||||
|
||||
public static float convertChunkToRealSpace(int chunk){
|
||||
return chunk * ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
}
|
||||
|
||||
@ -185,11 +193,11 @@ public class ClientWorldData {
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3i convertRealToVoxelSpace(Vector3d position){
|
||||
public static Vector3i convertRealToVoxelSpace(Vector3d position){
|
||||
return new Vector3i(
|
||||
(int)Math.floor(position.x - this.convertChunkToRealSpace(this.convertRealToChunkSpace(position.x))),
|
||||
(int)Math.floor(position.y - this.convertChunkToRealSpace(this.convertRealToChunkSpace(position.y))),
|
||||
(int)Math.floor(position.z - this.convertChunkToRealSpace(this.convertRealToChunkSpace(position.z)))
|
||||
(int)Math.floor(position.x - ClientWorldData.convertChunkToRealSpace(ClientWorldData.convertRealToChunkSpace(position.x))),
|
||||
(int)Math.floor(position.y - ClientWorldData.convertChunkToRealSpace(ClientWorldData.convertRealToChunkSpace(position.y))),
|
||||
(int)Math.floor(position.z - ClientWorldData.convertChunkToRealSpace(ClientWorldData.convertRealToChunkSpace(position.z)))
|
||||
);
|
||||
}
|
||||
|
||||
@ -198,7 +206,7 @@ public class ClientWorldData {
|
||||
* @param real The real position
|
||||
* @return The closest block position
|
||||
*/
|
||||
public int convertRealToLocalBlockSpace(double real){
|
||||
public static int convertRealToLocalBlockSpace(double real){
|
||||
return (int)Math.floor(real * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE % BlockChunkData.CHUNK_DATA_WIDTH);
|
||||
}
|
||||
|
||||
@ -207,11 +215,11 @@ public class ClientWorldData {
|
||||
* @param position The real-space position
|
||||
* @return The nearest block-space position
|
||||
*/
|
||||
public Vector3i convertRealToBlockSpace(Vector3d position){
|
||||
public static Vector3i convertRealToLocalBlockSpace(Vector3d position){
|
||||
return new Vector3i(
|
||||
this.convertRealToLocalBlockSpace(position.x),
|
||||
this.convertRealToLocalBlockSpace(position.y),
|
||||
this.convertRealToLocalBlockSpace(position.z)
|
||||
ClientWorldData.convertRealToLocalBlockSpace(position.x),
|
||||
ClientWorldData.convertRealToLocalBlockSpace(position.y),
|
||||
ClientWorldData.convertRealToLocalBlockSpace(position.z)
|
||||
);
|
||||
}
|
||||
|
||||
@ -249,4 +257,17 @@ public class ClientWorldData {
|
||||
chunkPos.x < this.worldDiscreteSize && chunkPos.y < this.worldDiscreteSize && chunkPos.z < this.worldDiscreteSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps a real space position to the closest block space position
|
||||
* @param realPos The real space position
|
||||
* @return The real space position that is clamped to the closest block space position
|
||||
*/
|
||||
public static Vector3d clampRealToBlock(Vector3d realPos){
|
||||
return new Vector3d(
|
||||
realPos.x - realPos.x % BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
realPos.y - realPos.y % BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
realPos.z - realPos.z % BlockChunkData.BLOCK_SIZE_MULTIPLIER
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.interact.select.AreaSelection;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.controls.cursor.CursorState;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
@ -22,7 +23,7 @@ public class ScriptClientAreaUtils {
|
||||
// Vector3d blockCursorPos = Globals.cursorState.getBlockCursorPos();
|
||||
Vector3d cursorPos = new Vector3d(EntityUtils.getPosition(Globals.cursorState.playerCursor));
|
||||
Vector3i chunkPos = Globals.clientState.clientWorldData.convertRealToWorldSpace(cursorPos);
|
||||
Vector3i blockPos = Globals.clientState.clientWorldData.convertRealToBlockSpace(cursorPos);
|
||||
Vector3i blockPos = ClientWorldData.convertRealToLocalBlockSpace(cursorPos);
|
||||
AreaSelection selection = AreaSelection.selectRectangularBlockCavity(chunkPos, blockPos, AreaSelection.DEFAULT_SELECTION_RADIUS);
|
||||
Globals.cursorState.selectRectangularArea(selection);
|
||||
CursorState.makeAreaVisible();
|
||||
|
||||
@ -7,6 +7,7 @@ import org.joml.Vector3i;
|
||||
import electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType;
|
||||
import electrosphere.audio.movement.MovementAudioService.InteractionType;
|
||||
import electrosphere.client.entity.camera.CameraEntityUtils;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.editing.TerrainEditing;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.controls.cursor.CursorState;
|
||||
@ -145,7 +146,7 @@ public class ScriptClientVoxelUtils {
|
||||
String fabPath = Globals.cursorState.getSelectedFabPath();
|
||||
Vector3d fabCursorPos = EntityUtils.getPosition(CursorState.getFabCursor());
|
||||
Vector3i chunkPos = Globals.clientState.clientWorldData.convertRealToWorldSpace(fabCursorPos);
|
||||
Vector3i voxelPos = Globals.clientState.clientWorldData.convertRealToBlockSpace(fabCursorPos);
|
||||
Vector3i voxelPos = ClientWorldData.convertRealToLocalBlockSpace(fabCursorPos);
|
||||
int rotation = Globals.cursorState.getFabCursorRotation();
|
||||
Globals.clientState.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestPlaceFabMessage(
|
||||
chunkPos.x, chunkPos.y, chunkPos.z,
|
||||
|
||||
@ -2,6 +2,7 @@ package electrosphere.client.terrain.editing;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.script.ScriptClientVoxelUtils;
|
||||
import electrosphere.engine.Globals;
|
||||
|
||||
@ -16,7 +17,7 @@ public class BlockEditing {
|
||||
* @param metadata The metadata of the block
|
||||
*/
|
||||
public static void editBlock(short type, short metadata){
|
||||
Vector3i cornerVoxel = Globals.clientState.clientWorldData.convertRealToBlockSpace(Globals.cursorState.getBlockCursorPos());
|
||||
Vector3i cornerVoxel = ClientWorldData.convertRealToLocalBlockSpace(Globals.cursorState.getBlockCursorPos());
|
||||
int blockSize = Globals.cursorState.getBlockSize();
|
||||
Vector3i chunkPos = Globals.clientState.clientWorldData.convertRealToWorldSpace(Globals.cursorState.getBlockCursorPos());
|
||||
ScriptClientVoxelUtils.clientRequestEditBlock(chunkPos, cornerVoxel, type, metadata, blockSize);
|
||||
@ -26,7 +27,7 @@ public class BlockEditing {
|
||||
* Destroy blocks
|
||||
*/
|
||||
public static void destroyBlock(){
|
||||
Vector3i cornerVoxel = Globals.clientState.clientWorldData.convertRealToBlockSpace(Globals.cursorState.getBlockCursorPos());
|
||||
Vector3i cornerVoxel = ClientWorldData.convertRealToLocalBlockSpace(Globals.cursorState.getBlockCursorPos());
|
||||
int blockSize = Globals.cursorState.getBlockSize();
|
||||
Vector3i chunkPos = Globals.clientState.clientWorldData.convertRealToWorldSpace(Globals.cursorState.getBlockCursorPos());
|
||||
ScriptClientVoxelUtils.clientRequestEditBlock(chunkPos, cornerVoxel, (short)0, (short)0, blockSize);
|
||||
|
||||
@ -3,6 +3,7 @@ package electrosphere.client.terrain.sampling;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
@ -36,7 +37,7 @@ public class ClientVoxelSampler {
|
||||
public static int getVoxelType(Vector3d realPos){
|
||||
int voxelId = 0;
|
||||
Vector3i chunkSpacePos = Globals.clientState.clientWorldData.convertRealToWorldSpace(realPos);
|
||||
Vector3i voxelSpacePos = Globals.clientState.clientWorldData.convertRealToVoxelSpace(realPos);
|
||||
Vector3i voxelSpacePos = ClientWorldData.convertRealToVoxelSpace(realPos);
|
||||
if(Globals.clientState.clientTerrainManager.containsChunkDataAtWorldPoint(chunkSpacePos, ChunkData.NO_STRIDE)){
|
||||
ChunkData chunkData = Globals.clientState.clientTerrainManager.getChunkDataAtWorldPoint(chunkSpacePos, ChunkData.NO_STRIDE);
|
||||
voxelId = chunkData.getType(voxelSpacePos);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.collision;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.server.datacell.ServerWorldData;
|
||||
@ -45,7 +46,7 @@ public class CollisionWorldData {
|
||||
|
||||
public int convertRealToWorld(double real){
|
||||
if(Globals.clientState.clientWorldData != null){
|
||||
return Globals.clientState.clientWorldData.convertRealToChunkSpace(real);
|
||||
return ClientWorldData.convertRealToChunkSpace(real);
|
||||
} else {
|
||||
return ServerWorldData.convertRealToChunkSpace(real);
|
||||
}
|
||||
@ -53,7 +54,7 @@ public class CollisionWorldData {
|
||||
|
||||
public double convertWorldToReal(int world){
|
||||
if(Globals.clientState.clientWorldData != null){
|
||||
return Globals.clientState.clientWorldData.convertChunkToRealSpace(world);
|
||||
return ClientWorldData.convertChunkToRealSpace(world);
|
||||
} else {
|
||||
return ServerWorldData.convertChunkToRealSpace(world);
|
||||
}
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
package electrosphere.collision.physics;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
|
||||
/**
|
||||
* The result of a collision
|
||||
*/
|
||||
public class CollisionResult {
|
||||
|
||||
/**
|
||||
* The penetration of two bodies
|
||||
*/
|
||||
double penetration;
|
||||
|
||||
/**
|
||||
* The normal of the collision
|
||||
*/
|
||||
Vector3d normal;
|
||||
|
||||
/**
|
||||
* Gets the penetration of the collision result
|
||||
* @return The penetration
|
||||
*/
|
||||
public double getPenetration() {
|
||||
return penetration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the penetration of the collision result
|
||||
* @param penetration The penetration
|
||||
*/
|
||||
public void setPenetration(double penetration) {
|
||||
this.penetration = penetration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the normal of the collision result
|
||||
* @return The normal
|
||||
*/
|
||||
public Vector3d getNormal() {
|
||||
return normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the normal of the collision result
|
||||
* @param normal The normal
|
||||
*/
|
||||
public void setNormal(Vector3d normal) {
|
||||
this.normal = normal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,16 +1,24 @@
|
||||
package electrosphere.entity.state.collidable;
|
||||
|
||||
import org.joml.AABBd;
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
import org.ode4j.ode.DBody;
|
||||
import org.ode4j.ode.DCapsule;
|
||||
|
||||
import electrosphere.client.block.BlockChunkData;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.collision.PhysicsUtils;
|
||||
import electrosphere.collision.collidable.Collidable;
|
||||
import electrosphere.collision.physics.CollisionResult;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.btree.BehaviorTree;
|
||||
import electrosphere.entity.types.collision.CollisionObjUtils;
|
||||
import electrosphere.util.math.CollisionUtils;
|
||||
|
||||
/**
|
||||
* Client collidable tree
|
||||
@ -65,6 +73,91 @@ public class ClientCollidableTree implements BehaviorTree {
|
||||
}
|
||||
}
|
||||
PhysicsUtils.setRigidBodyTransform(Globals.clientState.clientSceneWrapper.getCollisionEngine(), newPosition, rotation, body);
|
||||
|
||||
//capsule-specific block collision logic
|
||||
if(body.isEnabled() && body.getFirstGeom() != null && (body.getFirstGeom() instanceof DCapsule)){
|
||||
//get capsule params
|
||||
DCapsule capsuleGeom = (DCapsule)body.getFirstGeom();
|
||||
double length = capsuleGeom.getLength();
|
||||
double halfLength = length / 2.0;
|
||||
double radius = capsuleGeom.getRadius();
|
||||
Vector3d bodyOffset = PhysicsUtils.odeVecToJomlVec(body.getFirstGeom().getOffsetPosition());
|
||||
|
||||
//entity spatial transforms
|
||||
Vector3d entRealPos = EntityUtils.getPosition(parent);
|
||||
Quaterniond entRot = EntityUtils.getRotation(parent);
|
||||
|
||||
//start and end of capsule
|
||||
Vector3d realStart = new Vector3d(0,-halfLength,0).rotate(entRot).add(entRealPos).add(bodyOffset);
|
||||
Vector3d realEnd = new Vector3d(0,halfLength,0).rotate(entRot).add(entRealPos).add(bodyOffset);
|
||||
|
||||
|
||||
//block position of body
|
||||
Vector3d blockPos = ClientWorldData.clampRealToBlock(entRealPos);
|
||||
Vector3d currBlockPos = new Vector3d();
|
||||
|
||||
//get dims to scan along (ceil to overcompensate -- better to over scan than underscan)
|
||||
int halfRadBlockLen = (int)Math.ceil(halfLength / BlockChunkData.BLOCK_SIZE_MULTIPLIER);
|
||||
int radBlockLen = (int)Math.ceil(radius / BlockChunkData.BLOCK_SIZE_MULTIPLIER);
|
||||
|
||||
//final corrected position
|
||||
Vector3d corrected = new Vector3d(entRealPos);
|
||||
|
||||
//scan for all potential blocks
|
||||
for(int x = -radBlockLen; x <= radBlockLen; x++){
|
||||
for(int z = -radBlockLen; z <= radBlockLen; z++){
|
||||
for(int y = -halfRadBlockLen; y <= halfRadBlockLen; y++){
|
||||
currBlockPos.set(blockPos).add(
|
||||
x * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
y * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
z * BlockChunkData.BLOCK_SIZE_MULTIPLIER
|
||||
);
|
||||
Vector3i chunkPos = ClientWorldData.convertRealToChunkSpace(currBlockPos);
|
||||
Vector3i entBlockPos = ClientWorldData.convertRealToLocalBlockSpace(currBlockPos);
|
||||
|
||||
//error check bounds
|
||||
if(chunkPos.x < 0 || chunkPos.y < 0 || chunkPos.z < 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
//get block data for block to check
|
||||
BlockChunkData data = Globals.clientState.clientBlockManager.getChunkDataAtWorldPoint(chunkPos, BlockChunkData.LOD_FULL_RES);
|
||||
if(data != null){
|
||||
//check type of voxel to skip math
|
||||
short type = data.getType(entBlockPos.x, entBlockPos.y, entBlockPos.z);
|
||||
if(type != BlockChunkData.BLOCK_TYPE_EMPTY){
|
||||
|
||||
//AABB for the voxel
|
||||
AABBd voxelBox = new AABBd(
|
||||
currBlockPos.x,
|
||||
currBlockPos.y,
|
||||
currBlockPos.z,
|
||||
currBlockPos.x + BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
currBlockPos.y + BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
currBlockPos.z + BlockChunkData.BLOCK_SIZE_MULTIPLIER
|
||||
);
|
||||
|
||||
//actually collision check
|
||||
CollisionResult collisionResult = CollisionUtils.collideCapsuleAABB(realStart, realEnd, radius, voxelBox);
|
||||
if(collisionResult != null){
|
||||
double pen = collisionResult.getPenetration();
|
||||
double forceMul = pen * 0.3;
|
||||
Vector3d normal = collisionResult.getNormal().mul(forceMul);
|
||||
if(normal != null){
|
||||
// body.addForce(normal.x, normal.y, normal.z);
|
||||
//correct the position of the capsule
|
||||
corrected.add(normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//apply correction
|
||||
CollisionObjUtils.clientPositionCharacter(parent, newPosition, entRot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,40 +1,66 @@
|
||||
package electrosphere.entity.state.collidable;
|
||||
|
||||
import electrosphere.client.block.BlockChunkData;
|
||||
import electrosphere.collision.PhysicsUtils;
|
||||
import electrosphere.collision.collidable.Collidable;
|
||||
import electrosphere.collision.physics.CollisionResult;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.btree.BehaviorTree;
|
||||
import electrosphere.entity.state.gravity.ServerGravityTree;
|
||||
import electrosphere.entity.state.movement.fall.ServerFallTree;
|
||||
import electrosphere.entity.types.collision.CollisionObjUtils;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.datacell.ServerWorldData;
|
||||
import electrosphere.server.datacell.interfaces.VoxelCellManager;
|
||||
import electrosphere.util.math.CollisionUtils;
|
||||
|
||||
import org.joml.AABBd;
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
import org.ode4j.ode.DBody;
|
||||
import org.ode4j.ode.DCapsule;
|
||||
|
||||
/**
|
||||
* Server collidable tree
|
||||
*/
|
||||
public class ServerCollidableTree implements BehaviorTree {
|
||||
|
||||
/**
|
||||
* The parent of the collidable
|
||||
*/
|
||||
Entity parent;
|
||||
|
||||
/**
|
||||
* The body
|
||||
*/
|
||||
DBody body;
|
||||
|
||||
/**
|
||||
* The collidable
|
||||
*/
|
||||
Collidable collidable;
|
||||
boolean applyImpulses = true;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param e The entity
|
||||
* @param collidable The collidable
|
||||
* @param body The body
|
||||
*/
|
||||
public ServerCollidableTree(Entity e, Collidable collidable, DBody body){
|
||||
parent = e;
|
||||
this.collidable = collidable;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public ServerCollidableTree(Entity e, Collidable collidable, DBody body, boolean applyImpulses){
|
||||
parent = e;
|
||||
this.collidable = collidable;
|
||||
this.body = body;
|
||||
this.applyImpulses = applyImpulses;
|
||||
}
|
||||
|
||||
static int incrementer = 0;
|
||||
|
||||
/**
|
||||
* Simulates the collidable tree
|
||||
* @param deltaTime The amount of time to simulate by
|
||||
*/
|
||||
public void simulate(float deltaTime){
|
||||
//have we hit a terrain impulse?
|
||||
//handle impulses
|
||||
@ -48,6 +74,94 @@ public class ServerCollidableTree implements BehaviorTree {
|
||||
this.resetGravityFall();
|
||||
}
|
||||
}
|
||||
|
||||
//capsule-specific block collision logic
|
||||
if(body.isEnabled() && body.getFirstGeom() != null && (body.getFirstGeom() instanceof DCapsule)){
|
||||
Realm realm = Globals.serverState.realmManager.getEntityRealm(parent);
|
||||
if(realm.getDataCellManager() instanceof VoxelCellManager){
|
||||
VoxelCellManager voxelCellManager = (VoxelCellManager)realm.getDataCellManager();
|
||||
|
||||
//get capsule params
|
||||
DCapsule capsuleGeom = (DCapsule)body.getFirstGeom();
|
||||
double length = capsuleGeom.getLength();
|
||||
double halfLength = length / 2.0;
|
||||
double radius = capsuleGeom.getRadius();
|
||||
Vector3d bodyOffset = PhysicsUtils.odeVecToJomlVec(body.getFirstGeom().getOffsetPosition());
|
||||
|
||||
//entity spatial transforms
|
||||
Vector3d entRealPos = EntityUtils.getPosition(parent);
|
||||
Quaterniond entRot = EntityUtils.getRotation(parent);
|
||||
|
||||
//start and end of capsule
|
||||
Vector3d realStart = new Vector3d(0,-halfLength,0).rotate(entRot).add(entRealPos).add(bodyOffset);
|
||||
Vector3d realEnd = new Vector3d(0,halfLength,0).rotate(entRot).add(entRealPos).add(bodyOffset);
|
||||
|
||||
|
||||
//block position of body
|
||||
Vector3d blockPos = ServerWorldData.clampRealToBlock(entRealPos);
|
||||
Vector3d currBlockPos = new Vector3d();
|
||||
|
||||
//get dims to scan along (ceil to overcompensate -- better to over scan than underscan)
|
||||
int halfRadBlockLen = (int)Math.ceil(halfLength / BlockChunkData.BLOCK_SIZE_MULTIPLIER);
|
||||
int radBlockLen = (int)Math.ceil(radius / BlockChunkData.BLOCK_SIZE_MULTIPLIER);
|
||||
|
||||
//final corrected position
|
||||
Vector3d corrected = new Vector3d(entRealPos);
|
||||
|
||||
//scan for all potential blocks
|
||||
for(int x = -radBlockLen; x <= radBlockLen; x++){
|
||||
for(int z = -radBlockLen; z <= radBlockLen; z++){
|
||||
for(int y = -halfRadBlockLen; y <= halfRadBlockLen; y++){
|
||||
currBlockPos.set(blockPos).add(
|
||||
x * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
y * BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
z * BlockChunkData.BLOCK_SIZE_MULTIPLIER
|
||||
);
|
||||
Vector3i chunkPos = ServerWorldData.convertRealToChunkSpace(currBlockPos);
|
||||
Vector3i entBlockPos = ServerWorldData.convertRealToLocalBlockSpace(currBlockPos);
|
||||
|
||||
//error check bounds
|
||||
if(chunkPos.x < 0 || chunkPos.y < 0 || chunkPos.z < 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
//get block data for block to check
|
||||
BlockChunkData data = voxelCellManager.getBlocksAtPosition(chunkPos);
|
||||
short type = data.getType(entBlockPos.x, entBlockPos.y, entBlockPos.z);
|
||||
|
||||
if(type != BlockChunkData.BLOCK_TYPE_EMPTY){
|
||||
|
||||
//AABB for the voxel
|
||||
AABBd voxelBox = new AABBd(
|
||||
currBlockPos.x,
|
||||
currBlockPos.y,
|
||||
currBlockPos.z,
|
||||
currBlockPos.x + BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
currBlockPos.y + BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
currBlockPos.z + BlockChunkData.BLOCK_SIZE_MULTIPLIER
|
||||
);
|
||||
|
||||
//actually collision check
|
||||
CollisionResult collisionResult = CollisionUtils.collideCapsuleAABB(realStart, realEnd, radius, voxelBox);
|
||||
if(collisionResult != null){
|
||||
double pen = collisionResult.getPenetration();
|
||||
double forceMul = pen * 0.3;
|
||||
Vector3d normal = collisionResult.getNormal().mul(forceMul);
|
||||
if(normal != null){
|
||||
// body.addForce(normal.x, normal.y, normal.z);
|
||||
//correct the position of the capsule
|
||||
corrected.add(normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//apply correction
|
||||
CollisionObjUtils.serverPositionCharacter(parent, corrected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -61,10 +175,20 @@ public class ServerCollidableTree implements BehaviorTree {
|
||||
this.collidable = collidable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity has a server collidable tree
|
||||
* @param e The entity
|
||||
* @return true if it has a collidable tree, false otherwise
|
||||
*/
|
||||
public static boolean hasServerCollidableTree(Entity e){
|
||||
return e.containsKey(EntityDataStrings.SERVER_COLLIDABLE_TREE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server collidable tree on an entity
|
||||
* @param e The entity
|
||||
* @return The tree if it exists, false otherwise
|
||||
*/
|
||||
public static ServerCollidableTree getServerCollidableTree(Entity e){
|
||||
return (ServerCollidableTree)e.getData(EntityDataStrings.SERVER_COLLIDABLE_TREE);
|
||||
}
|
||||
|
||||
@ -423,7 +423,7 @@ public class ServerWorldData {
|
||||
* @param realPos The real space position
|
||||
* @return The real space position that is clamped to the closest block space position
|
||||
*/
|
||||
public Vector3d clampRealToBlock(Vector3d realPos){
|
||||
public static Vector3d clampRealToBlock(Vector3d realPos){
|
||||
return new Vector3d(
|
||||
realPos.x - realPos.x % BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
realPos.y - realPos.y % BlockChunkData.BLOCK_SIZE_MULTIPLIER,
|
||||
|
||||
136
src/main/java/electrosphere/util/math/CollisionUtils.java
Normal file
136
src/main/java/electrosphere/util/math/CollisionUtils.java
Normal file
@ -0,0 +1,136 @@
|
||||
package electrosphere.util.math;
|
||||
|
||||
import org.joml.AABBd;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import electrosphere.collision.physics.CollisionResult;
|
||||
import io.github.studiorailgun.MathUtils;
|
||||
|
||||
/**
|
||||
* Utilities for performing collisions of geometry
|
||||
*/
|
||||
public class CollisionUtils {
|
||||
|
||||
/**
|
||||
* Checks if a capsule intersects an AABB
|
||||
* @param capsuleStart The start of the capsule
|
||||
* @param capsuleEnd The end of the capsule
|
||||
* @param radius The radius of the capsule
|
||||
* @param box The box
|
||||
* @return true if they intersect, false otherwise
|
||||
*/
|
||||
public static boolean capsuleIntersectsAABB(Vector3d start, Vector3d end, double radius, AABBd box){
|
||||
// Step 1: Find closest point on capsule segment to the AABB
|
||||
// Approximate by projecting the center of the AABB onto the segment
|
||||
double boxCenterX = (box.minX + box.maxX) * 0.5;
|
||||
double boxCenterY = (box.minY + box.maxY) * 0.5;
|
||||
double boxCenterZ = (box.minZ + box.maxZ) * 0.5;
|
||||
|
||||
double abX = end.x - start.x;
|
||||
double abY = end.y - start.y;
|
||||
double abZ = end.z - start.z;
|
||||
|
||||
double lenSquared = (boxCenterX - abX) * (boxCenterX - abX) + (boxCenterY - abY) * (boxCenterY - abY) + (boxCenterZ - abZ) * (boxCenterZ - abZ);
|
||||
|
||||
double t = MathUtils.dot(
|
||||
boxCenterX - start.x,
|
||||
boxCenterY - start.y,
|
||||
boxCenterZ - start.z,
|
||||
abX,
|
||||
abY,
|
||||
abZ
|
||||
) / lenSquared;
|
||||
t = Math.max(0f, Math.min(1f, t)); // clamp to [0,1]
|
||||
|
||||
double segClosesX = start.x + (abX * t);
|
||||
double segClosesY = start.y + (abY * t);
|
||||
double segClosesZ = start.z + (abZ * t);
|
||||
|
||||
// Step 2: Find closest point on AABB to that segment point
|
||||
double boxClosestX = MathUtils.clamp(segClosesX, box.minX, box.maxX);
|
||||
double boxClosestY = MathUtils.clamp(segClosesY, box.minY, box.maxY);
|
||||
double boxClosestZ = MathUtils.clamp(segClosesZ, box.minZ, box.maxZ);
|
||||
|
||||
// Step 3: Compute distance squared
|
||||
double diffX = segClosesX - boxClosestX;
|
||||
double diffY = segClosesY - boxClosestY;
|
||||
double diffZ = segClosesZ - boxClosestZ;
|
||||
|
||||
double distSq = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ);
|
||||
|
||||
return distSq <= radius * radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a capsule intersects an AABB
|
||||
* @param capsuleStart The start of the capsule
|
||||
* @param capsuleEnd The end of the capsule
|
||||
* @param radius The radius of the capsule
|
||||
* @param box The box
|
||||
* @return true if they intersect, false otherwise
|
||||
*/
|
||||
public static CollisionResult collideCapsuleAABB(Vector3d start, Vector3d end, double radius, AABBd box){
|
||||
CollisionResult rVal = new CollisionResult();
|
||||
// Step 1: Find closest point on capsule segment to the AABB
|
||||
// Approximate by projecting the center of the AABB onto the segment
|
||||
double boxCenterX = (box.minX + box.maxX) * 0.5;
|
||||
double boxCenterY = (box.minY + box.maxY) * 0.5;
|
||||
double boxCenterZ = (box.minZ + box.maxZ) * 0.5;
|
||||
|
||||
double abX = end.x - start.x;
|
||||
double abY = end.y - start.y;
|
||||
double abZ = end.z - start.z;
|
||||
|
||||
double lenSquared = (boxCenterX - abX) * (boxCenterX - abX) + (boxCenterY - abY) * (boxCenterY - abY) + (boxCenterZ - abZ) * (boxCenterZ - abZ);
|
||||
|
||||
double t = MathUtils.dot(
|
||||
boxCenterX - start.x,
|
||||
boxCenterY - start.y,
|
||||
boxCenterZ - start.z,
|
||||
abX,
|
||||
abY,
|
||||
abZ
|
||||
) / lenSquared;
|
||||
t = Math.max(0f, Math.min(1f, t)); // clamp to [0,1]
|
||||
|
||||
double segClosesX = start.x + (abX * t);
|
||||
double segClosesY = start.y + (abY * t);
|
||||
double segClosesZ = start.z + (abZ * t);
|
||||
|
||||
// Step 2: Find closest point on AABB to that segment point
|
||||
double boxClosestX = MathUtils.clamp(segClosesX, box.minX, box.maxX);
|
||||
double boxClosestY = MathUtils.clamp(segClosesY, box.minY, box.maxY);
|
||||
double boxClosestZ = MathUtils.clamp(segClosesZ, box.minZ, box.maxZ);
|
||||
|
||||
// Step 3: Compute distance squared
|
||||
double diffX = segClosesX - boxClosestX;
|
||||
double diffY = segClosesY - boxClosestY;
|
||||
double diffZ = segClosesZ - boxClosestZ;
|
||||
|
||||
//compute distance squared
|
||||
double distSq = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ);
|
||||
|
||||
//early return if no collision occurred
|
||||
if(distSq > radius * radius){
|
||||
return null;
|
||||
}
|
||||
|
||||
//compute distance
|
||||
double dist = Math.sqrt(distSq);
|
||||
|
||||
//compute penetration
|
||||
double penetration = radius - dist;
|
||||
rVal.setPenetration(penetration);
|
||||
|
||||
//compute normal
|
||||
Vector3d normal = null;
|
||||
if(dist > 1e-6){
|
||||
normal = new Vector3d(diffX,diffY,diffZ).mul(1 / dist);
|
||||
}
|
||||
rVal.setNormal(normal);
|
||||
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user