block colliders
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-22 14:10:38 -04:00
parent cbd5c98e0a
commit 4e4367f915
12 changed files with 547 additions and 207 deletions

View File

@ -1930,6 +1930,9 @@ Fix town placement structure-road intersection test
Kinematic collision templates now use geoms instead of setting kinematic (on client)
Geom-only collidables on server
(05/22/2025)
Block colliders leveraging spaces
Scene view debug window

View File

@ -13,6 +13,7 @@ import electrosphere.client.ui.menu.debug.perf.ImGuiMemory;
import electrosphere.client.ui.menu.debug.perf.ImGuiNetworkMonitor;
import electrosphere.client.ui.menu.debug.render.ImGuiRenderer;
import electrosphere.client.ui.menu.debug.render.ImGuiUIFramework;
import electrosphere.client.ui.menu.debug.scene.ImGuiSceneWindow;
import electrosphere.client.ui.menu.debug.server.ImGuiAI;
import electrosphere.client.ui.menu.debug.server.ImGuiFluidMonitor;
import electrosphere.client.ui.menu.debug.server.ImGuiGriddedManager;
@ -67,6 +68,7 @@ public class ImGuiWindowMacros {
ImGuiMemory.createMemoryDebugWindow();
ImGuiEditorWindows.initEditorWindows();
ImGuiClientServices.createClientServicesWindow();
ImGuiSceneWindow.createChunkMonitorWindow();
}
/**
@ -149,6 +151,9 @@ public class ImGuiWindowMacros {
if(ImGui.button("Client Entity Debug")){
ImGuiEntityMacros.clientEntityListWindow.setOpen(true);
}
if(ImGui.button("Client Scene")){
ImGuiSceneWindow.viewScene(Globals.clientState.clientScene, Globals.clientState.clientSceneWrapper.getCollisionEngine());
}
//controls state debug
if(ImGui.button("Control State Debug")){
ImGuiControls.controlsWindow.setOpen(true);
@ -187,6 +192,9 @@ public class ImGuiWindowMacros {
if(ImGui.button("Gridded Data Cell Monitor")){
ImGuiGriddedManager.griddedManagerWindow.setOpen(true);
}
if(ImGui.button("Server Scene")){
ImGuiSceneWindow.viewScene(null, Globals.serverState.realmManager.first().getCollisionEngine());
}
//memory usage
if(ImGui.button("Memory Usage")){
ImGuiMemory.memoryWindow.setOpen(!ImGuiMemory.memoryWindow.isOpen());

View File

@ -0,0 +1,59 @@
package electrosphere.client.ui.menu.debug.scene;
import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals;
import electrosphere.entity.scene.Scene;
import electrosphere.renderer.ui.imgui.ImGuiWindow;
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
import imgui.ImGui;
/**
* Views a scene
*/
public class ImGuiSceneWindow {
/**
* The target scene
*/
public static Scene targetScene;
/**
* The associated collision engine
*/
public static CollisionEngine targetCollisionEngine;
/**
* Window for scene data
*/
public static ImGuiWindow sceneWindow;
/**
* Scene view
*/
public static void createChunkMonitorWindow(){
sceneWindow = new ImGuiWindow("Scene Data");
sceneWindow.setCallback(new ImGuiWindowCallback() {
@Override
public void exec() {
if(targetCollisionEngine != null && ImGui.collapsingHeader("Collision Engine")){
ImGui.text(targetCollisionEngine.getStatus());
}
}
});
sceneWindow.setOpen(false);
Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(sceneWindow);
}
/**
* Views a scene
* @param scene The scene
* @param engine The collision engine associated with the scene (optional)
*/
public static void viewScene(Scene scene, CollisionEngine engine){
ImGuiSceneWindow.targetScene = scene;
ImGuiSceneWindow.targetCollisionEngine = engine;
sceneWindow.setOpen(true);
}
}

View File

@ -16,6 +16,7 @@ import org.ode4j.ode.DCapsule;
import org.ode4j.ode.DCylinder;
import org.ode4j.ode.DGeom;
import org.ode4j.ode.DMass;
import org.ode4j.ode.DSpace;
import org.ode4j.ode.DSphere;
import org.ode4j.ode.DTriMesh;
@ -296,6 +297,22 @@ public class CollisionBodyCreation {
return body;
}
/**
* Creates an ode collider from a terrain chunk data object
* @param data The terrain data
* @return The geom
*/
public static DGeom generateGeomFromTerrainData(CollisionEngine collisionEngine, TriGeomData data, long categoryBits){
DGeom geom = null;
//create trimesh
if(data.getFaceElements().length > 0){
geom = collisionEngine.createTrimeshGeom(data.getVertices(),data.getFaceElements(),categoryBits);
}
return geom;
}
/**
* Creates an ode DBody from a mesh data set containing multiple shapes
* @param data The mesh data data
@ -325,6 +342,24 @@ public class CollisionBodyCreation {
return body;
}
/**
* Creates an ode DBody from a mesh data set containing multiple shapes
* @param data The mesh data data
* @return The DBody
*/
public static DGeom generateColliderFromMultiShapeMeshData(CollisionEngine collisionEngine, MultiShapeTriGeomData data, long categoryBits){
DSpace multishape = collisionEngine.createSpace();
//create trimeshes
for(TriGeomData shapeData : data.getData()){
if(shapeData.getFaceElements().length > 0){
collisionEngine.createTrimeshGeom(shapeData.getVertices(),shapeData.getFaceElements(),categoryBits,multishape);
}
}
return multishape;
}
/**
* Generates a body from an AIScene
* @param scene The AIScene to generate a rigid body off of

View File

@ -404,10 +404,13 @@ public class CollisionEngine {
//make sure we have collidables for both
if(c1 == null || c2 == null){
String message = "Collidable is undefined!\n" +
"Geoms:\n" +
o1 + " \n" +
o2 + " \n" +
"Bodies:\n" +
b1 + " \n" +
b2 + " \n" +
"Colliders:\n" +
c1 + " \n" +
c2 + " \n" +
"Obj 1 pointers:\n" +
@ -621,18 +624,21 @@ public class CollisionEngine {
public void updateDynamicObjectTransforms(){
Globals.profiler.beginCpuSample("updateDynamicObjectTransforms");
spaceLock.lock();
Matrix4d inverseTransform = new Matrix4d();
if(this.collisionWorldData != null){
for(Collidable collidable : collidableList){
if(collidable.getParentTracksCollidable()){
Entity physicsEntity = collidable.getParent();
DBody rigidBody = PhysicsEntityUtils.getDBody(physicsEntity);
DGeom geom = PhysicsEntityUtils.getDGeom(physicsEntity);
Matrix4d inverseTransform = new Matrix4d();
Vector4d rawPos = null;
inverseTransform.identity();
if(rigidBody != null){
rawPos = inverseTransform.transform(new Vector4d(PhysicsUtils.odeVecToJomlVec(rigidBody.getPosition()).add(this.floatingOrigin),1));
} else if(geom != null){
rawPos = inverseTransform.transform(new Vector4d(PhysicsUtils.odeVecToJomlVec(geom.getPosition()).add(this.floatingOrigin),1));
} else {
continue;
}
Vector3d calculatedPosition = new Vector3d(rawPos.x,rawPos.y,rawPos.z);
Vector3d suggestedPosition = this.suggestMovementPosition(collisionWorldData, calculatedPosition);
@ -650,8 +656,10 @@ public class CollisionEngine {
}
if(geom != null){
CollidableTemplate template = PhysicsEntityUtils.getPhysicsTemplate(physicsEntity);
suggestedPosition.sub(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ());
newRotation.mul(new Quaterniond(template.getRotX(),template.getRotY(),template.getRotZ(),template.getRotW()).invert());
if(template != null){
suggestedPosition.sub(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ());
newRotation.mul(new Quaterniond(template.getRotX(),template.getRotY(),template.getRotZ(),template.getRotW()).invert());
}
}
EntityUtils.getPosition(physicsEntity).set(suggestedPosition);
EntityUtils.getRotation(physicsEntity).set(newRotation);
@ -668,7 +676,7 @@ public class CollisionEngine {
public void rebaseWorldOrigin(){
Globals.profiler.beginCpuSample("rebaseWorldOrigin");
spaceLock.lock();
if(this.collisionWorldData != null && this.floatingOrigin.x == 0 && this.floatingOrigin.z == 0){
if(this.collisionWorldData != null){
int collected = 0;
Vector3d newOrigin = new Vector3d();
//calculate new origin
@ -686,6 +694,32 @@ public class CollisionEngine {
}
collected++;
}
DGeom geom = PhysicsEntityUtils.getDGeom(physicsEntity);
if(geom != null){
if(geom instanceof DSpace space){
for(DGeom child : space.getGeoms()){
Vector3d currentBodyOffset = PhysicsUtils.odeVecToJomlVec(child.getPosition());
if(collected == 0){
newOrigin.set(currentBodyOffset);
} else {
float percentExisting = collected / (float)(collected + 1);
float percentNew = 1.0f - percentExisting;
newOrigin = newOrigin.mul(percentExisting).add(currentBodyOffset.mul(percentNew));
}
collected++;
}
} else {
Vector3d currentBodyOffset = PhysicsUtils.odeVecToJomlVec(geom.getPosition());
if(collected == 0){
newOrigin.set(currentBodyOffset);
} else {
float percentExisting = collected / (float)(collected + 1);
float percentNew = 1.0f - percentExisting;
newOrigin = newOrigin.mul(percentExisting).add(currentBodyOffset.mul(percentNew));
}
collected++;
}
}
}
Vector3d delta = new Vector3d(this.floatingOrigin).sub(newOrigin);
this.floatingOrigin = this.floatingOrigin.set(newOrigin);
@ -700,8 +734,15 @@ public class CollisionEngine {
}
DGeom geom = PhysicsEntityUtils.getDGeom(physicsEntity);
if(geom != null){
Vector3d existingPosition = PhysicsUtils.odeVecToJomlVec(geom.getPosition());
geom.setPosition(PhysicsUtils.jomlVecToOdeVec(existingPosition.add(delta)));
if(geom instanceof DSpace space){
for(DGeom child : space.getGeoms()){
Vector3d existingPosition = PhysicsUtils.odeVecToJomlVec(child.getPosition());
child.setPosition(PhysicsUtils.jomlVecToOdeVec(existingPosition.add(delta)));
}
} else {
Vector3d existingPosition = PhysicsUtils.odeVecToJomlVec(geom.getPosition());
geom.setPosition(PhysicsUtils.jomlVecToOdeVec(existingPosition.add(delta)));
}
}
}
}
@ -740,6 +781,23 @@ public class CollisionEngine {
spaceLock.unlock();
}
/**
* Registers a collision object with the server
* @param geoms The list of geoms
* @param collidable The corresponding collidable
*/
public void registerCollisionObject(List<DGeom> geoms, Collidable collidable){
if(collidable == null){
throw new Error("Collidable is null!");
}
spaceLock.lock();
for(DGeom geom : geoms){
geomPointerMap.put(geom,collidable);
}
collidableList.add(collidable);
spaceLock.unlock();
}
/**
* Deregisters a collidable from the physics engine
* @param collidable The collidable
@ -751,6 +809,17 @@ public class CollisionEngine {
collidableList.remove(collidable);
spaceLock.unlock();
}
/**
* Deregisters a collidable from the physics engine
* @param collidable The collidable
*/
public void deregisterCollisionObject(DGeom geom, Collidable collidable){
spaceLock.lock();
geomPointerMap.remove(geom);
collidableList.remove(collidable);
spaceLock.unlock();
}
public void listBodyPositions(){
for(DBody body : bodies){
@ -890,6 +959,21 @@ public class CollisionEngine {
}
spaceLock.unlock();
}
/**
* Destroys a geom
* @param body The DGeom to destroy
*/
protected void destroyDGeom(DGeom geom){
spaceLock.lock();
try {
geom.destroy();
} catch (NullPointerException ex){
LoggerInterface.loggerEngine.ERROR(ex);
spaceLock.unlock();
}
spaceLock.unlock();
}
/**
* Destroys the physics on an entity
@ -906,6 +990,20 @@ public class CollisionEngine {
e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY);
this.destroyDBody(rigidBody);
}
if(PhysicsEntityUtils.containsDGeom(e)){
DGeom geom = PhysicsEntityUtils.getDGeom(e);
if(geom == null){
throw new Error("DGeom key set to null rigid body! " + geom);
}
if(geom instanceof DSpace space){
for(DGeom child : space.getGeoms()){
this.destroyDGeom(child);
}
}
this.deregisterCollisionObject(geom,PhysicsEntityUtils.getCollidable(e));
e.removeData(EntityDataStrings.PHYSICS_GEOM);
this.destroyDGeom(geom);
}
if(ServerPhysicsSyncTree.hasTree(e)){
ServerPhysicsSyncTree.detachTree(e, ServerPhysicsSyncTree.getTree(e));
}
@ -954,7 +1052,7 @@ public class CollisionEngine {
spaceLock.unlock();
return rVal;
}
/**
* Creates a trimesh from a given set of vertices and indices
* @param verts The vertices
@ -962,6 +1060,30 @@ public class CollisionEngine {
* @return The DTriMesh
*/
protected DTriMesh createTrimeshGeom(float[] verts, int[] indices, long categoryBits){
spaceLock.lock();
DTriMeshData data = OdeHelper.createTriMeshData();
data.build(verts, indices);
final int preprocessFlags =
(1 << DTriMeshData.dTRIDATAPREPROCESS_BUILD.CONCAVE_EDGES) |
(1 << DTriMeshData.dTRIDATAPREPROCESS_BUILD.FACE_ANGLES) |
(1 << DTriMeshData.dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MAX)
;
data.preprocess2(preprocessFlags,null);
DTriMesh rVal = OdeHelper.createTriMesh(this.space, data);
rVal.setTrimeshData(data);
rVal.setCategoryBits(categoryBits);
spaceLock.unlock();
return rVal;
}
/**
* Creates a trimesh from a given set of vertices and indices
* @param verts The vertices
* @param indices The indices
* @param space The space to create it within
* @return The DTriMesh
*/
protected DTriMesh createTrimeshGeom(float[] verts, int[] indices, long categoryBits, DSpace space){
spaceLock.lock();
DTriMeshData data = OdeHelper.createTriMeshData();
data.build(verts, indices);
@ -1031,6 +1153,18 @@ public class CollisionEngine {
return capsuleGeom;
}
/**
* Creates a space
* @return The space
*/
protected DSpace createSpace(){
spaceLock.lock();
DSpace rVal = OdeHelper.createSimpleSpace();
this.space.add(rVal);
spaceLock.unlock();
return rVal;
}
/**
* Creates a DBody. Can optionally be passed DGeom objects to be attached to the body when it is created.
* @param geom The geometry objects to attach to the body on creation
@ -1210,11 +1344,17 @@ public class CollisionEngine {
*/
protected void setGeomTransform(DGeom geom, Vector3d position, Quaterniond rotation){
spaceLock.lock();
geom.setPosition(position.x - this.floatingOrigin.x, position.y - this.floatingOrigin.y, position.z - this.floatingOrigin.z);
if(geom instanceof DCylinder || geom instanceof DCapsule){
geom.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(new Quaterniond(rotation).rotateX(Math.PI/2.0)));
if(geom instanceof DSpace space){
for(DGeom child : space.getGeoms()){
this.setGeomTransform(child, position, rotation);
}
} else {
geom.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
geom.setPosition(position.x - this.floatingOrigin.x, position.y - this.floatingOrigin.y, position.z - this.floatingOrigin.z);
if(geom instanceof DCylinder || geom instanceof DCapsule){
geom.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(new Quaterniond(rotation).rotateX(Math.PI/2.0)));
} else {
geom.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
}
}
spaceLock.unlock();
}
@ -1331,6 +1471,22 @@ public class CollisionEngine {
protected void attachGeomToBody(DBody body, DGeom geom){
geom.setBody(body);
}
/**
* Gets the status of the collision engine
* @return The status of the collision engine
*/
public String getStatus(){
String message = "" +
"Bodies: " + this.bodies.size() + "\n" +
"Body Ptrs: " + this.bodyPointerMap.size() + "\n" +
"Geom Ptrs: " + this.geomPointerMap.size() + "\n" +
"Collidables: " + this.collidableList.size() + "\n" +
"Space geom count: " + this.space.getNumGeoms() + "\n" +
""
;
return message;
}
/**

View File

@ -704,6 +704,20 @@ public class PhysicsEntityUtils {
terrain.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
}
/**
* [CLIENT ONLY] Given an entity and a terrain chunk description, create physics for the chunk and attach it to the entity
* @param terrain The entity
* @param data The terrain description
* @return The rigid body created (note, attachment has already been performed)
*/
public static void clientAttachTriGeomCollider(Entity terrain, TriGeomData data){
DGeom terrainGeom = CollisionBodyCreation.generateGeomFromTerrainData(Globals.clientState.clientSceneWrapper.getCollisionEngine(), data, Collidable.TYPE_STATIC_BIT);
Collidable collidable = new Collidable(terrain,Collidable.TYPE_STATIC, false);
PhysicsEntityUtils.setCollidable(terrain, collidable);
Globals.clientState.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainGeom, collidable);
PhysicsEntityUtils.setDGeom(terrain,terrainGeom);
}
/**
* [CLIENT ONLY] Given an entity and a multi-shape trimesh description, create physics for the shapes and attach it to the entity
* @param terrain The entity
@ -719,6 +733,20 @@ public class PhysicsEntityUtils {
terrain.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
}
/**
* [CLIENT ONLY] Given an entity and a multi-shape trimesh description, create physics for the shapes and attach it to the entity
* @param terrain The entity
* @param data The trimesh description
* @return The collider created (note, attachment has already been performed)
*/
public static void clientAttachMultiShapeTriGeomCollider(Entity terrain, MultiShapeTriGeomData data){
DGeom terrainBody = CollisionBodyCreation.generateColliderFromMultiShapeMeshData(Globals.clientState.clientSceneWrapper.getCollisionEngine(), data, Collidable.TYPE_STATIC_BIT);
Collidable collidable = new Collidable(terrain,Collidable.TYPE_STATIC, false);
PhysicsEntityUtils.setCollidable(terrain, collidable);
Globals.clientState.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, collidable);
PhysicsEntityUtils.setDGeom(terrain,terrainBody);
}
/**
* [SERVER ONLY] Given an entity and a terrain chunk description, create physics for the chunk and attach it to the entity
@ -737,6 +765,24 @@ public class PhysicsEntityUtils {
return terrainBody;
}
/**
* [SERVER ONLY] Given an entity and a terrain chunk description, create physics for the chunk and attach it to the entity
* @param terrain The entity
* @param data The terrain description
* @return The rigid body created (note, attachment has already been performed)
*/
public static DGeom serverAttachTriGeomCollider(Entity terrain, TriGeomData data){
Realm realm = Globals.serverState.realmManager.getEntityRealm(terrain);
DGeom terrainCollider = CollisionBodyCreation.generateGeomFromTerrainData(realm.getCollisionEngine(),data,Collidable.TYPE_STATIC_BIT);
Collidable collidable = new Collidable(terrain,Collidable.TYPE_STATIC, false);
PhysicsEntityUtils.setCollidable(terrain, collidable);
realm.getCollisionEngine().registerCollisionObject(terrainCollider, collidable);
PhysicsEntityUtils.setDGeom(terrain,terrainCollider);
return terrainCollider;
}
/**
* [SERVER ONLY] Given an entity and a multi-shape trimesh description, create physics for the shapes and attach it to the entity
* @param terrain The entity
@ -754,6 +800,24 @@ public class PhysicsEntityUtils {
return terrainBody;
}
/**
* [SERVER ONLY] Given an entity and a multi-shape trimesh description, create physics for the shapes and attach it to the entity
* @param terrain The entity
* @param data The trimesh description
* @return The rigid body created (note, attachment has already been performed)
*/
public static DGeom serverAttachMultiShapeTriGeomCollider(Entity terrain, MultiShapeTriGeomData data){
Realm realm = Globals.serverState.realmManager.getEntityRealm(terrain);
DGeom terrainBody = CollisionBodyCreation.generateColliderFromMultiShapeMeshData(realm.getCollisionEngine(),data,Collidable.TYPE_STATIC_BIT);
Collidable collidable = new Collidable(terrain,Collidable.TYPE_STATIC, false);
PhysicsEntityUtils.setCollidable(terrain, collidable);
realm.getCollisionEngine().registerCollisionObject(terrainBody, collidable);
PhysicsEntityUtils.setDGeom(terrain,terrainBody);
return terrainBody;
}
/**
* Repositions all active physics-scoped entities on a given realm
* @param collisionEngine The realm's collision engine
@ -828,6 +892,15 @@ public class PhysicsEntityUtils {
return (DBody)entity.getData(EntityDataStrings.PHYSICS_COLLISION_BODY);
}
/**
* Checks if the entity contains a dgeom
* @param entity the entity
* @return true if contains, false otherwise
*/
public static boolean containsDGeom(Entity entity){
return entity.containsKey(EntityDataStrings.PHYSICS_GEOM);
}
/**
* Sets the static geom on the entity
* @param entity The entity
@ -867,6 +940,15 @@ public class PhysicsEntityUtils {
return (Collidable)entity.getData(EntityDataStrings.PHYSICS_COLLIDABLE);
}
/**
* Gets the collidable attached to the entity
* @param entity The entity
* @param collidable The collidable
*/
public static void setCollidable(Entity entity, Collidable collidable){
entity.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
}
/**
* Enables a body
* @param collisionEngine The collision engine

View File

@ -1,24 +1,16 @@
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
@ -28,17 +20,17 @@ public class ClientCollidableTree implements BehaviorTree {
/**
* The parent entity of this tree
*/
Entity parent;
private Entity parent;
/**
* The body for this tree
*/
DBody body;
protected DBody body;
/**
* The collidable for this tree
*/
Collidable collidable;
protected Collidable collidable;
/**
* Constructor
@ -75,89 +67,89 @@ 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());
// 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);
// //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);
// //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();
// //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);
// //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);
// //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);
// //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;
}
// //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){
// //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
);
// //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);
}
}
}
}
}
}
}
// //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);
}
// //apply correction
// CollisionObjUtils.clientPositionCharacter(parent, newPosition, entRot);
// }
}
/**

View File

@ -1,28 +1,13 @@
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
@ -32,17 +17,17 @@ public class ServerCollidableTree implements BehaviorTree {
/**
* The parent of the collidable
*/
Entity parent;
private Entity parent;
/**
* The body
*/
DBody body;
protected DBody body;
/**
* The collidable
*/
Collidable collidable;
private Collidable collidable;
/**
@ -76,92 +61,92 @@ public class ServerCollidableTree implements BehaviorTree {
}
//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();
// 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());
// //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);
// //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);
// //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();
// //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);
// //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);
// //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);
// //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;
}
// //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);
// //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){
// 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
);
// //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);
}
}
}
}
}
}
// //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);
}
}
// //apply correction
// CollisionObjUtils.serverPositionCharacter(parent, corrected);
// }
// }
}

View File

@ -83,12 +83,21 @@ public class CollisionObjUtils {
}
if(geom != null){
CollidableTemplate template = PhysicsEntityUtils.getPhysicsTemplate(e);
PhysicsUtils.setGeomTransform(
collisionEngine,
new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()),
rotation,
geom
);
if(template == null){
PhysicsUtils.setGeomTransform(
collisionEngine,
position,
rotation,
geom
);
} else {
PhysicsUtils.setGeomTransform(
collisionEngine,
new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()),
rotation,
geom
);
}
}
}
@ -107,12 +116,21 @@ public class CollisionObjUtils {
DGeom geom = PhysicsEntityUtils.getDGeom(e);
if(geom != null){
CollidableTemplate template = PhysicsEntityUtils.getPhysicsTemplate(e);
PhysicsUtils.setGeomTransform(
Globals.clientState.clientSceneWrapper.getCollisionEngine(),
new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()),
rotation,
geom
);
if(template == null){
PhysicsUtils.setGeomTransform(
Globals.clientState.clientSceneWrapper.getCollisionEngine(),
new Vector3d(position),
rotation,
geom
);
} else {
PhysicsUtils.setGeomTransform(
Globals.clientState.clientSceneWrapper.getCollisionEngine(),
new Vector3d(position).add(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()),
rotation,
geom
);
}
}
}

View File

@ -8,7 +8,7 @@ import java.util.concurrent.Executors;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.ode4j.ode.DBody;
import org.ode4j.ode.DGeom;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.block.cells.BlockDrawCell;
@ -78,7 +78,7 @@ public class BlockChunkEntity {
}));
EntityCreationUtils.makeEntityDrawablePreexistingModel(solidsEnt, modelPath);
if(levelOfDetail == BlockChunkData.LOD_FULL_RES){
PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(solidsEnt, data);
PhysicsEntityUtils.clientAttachMultiShapeTriGeomCollider(solidsEnt, data);
CollisionObjUtils.clientPositionCharacter(solidsEnt, new Vector3d(EntityUtils.getPosition(solidsEnt)), new Quaterniond());
} else {
EntityCreationUtils.bypassShadowPass(solidsEnt);
@ -129,7 +129,7 @@ public class BlockChunkEntity {
}));
EntityCreationUtils.makeEntityDrawablePreexistingModel(transparentEnt, modelPath);
if(levelOfDetail == BlockChunkData.LOD_FULL_RES){
PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(transparentEnt, data);
PhysicsEntityUtils.clientAttachMultiShapeTriGeomCollider(transparentEnt, data);
CollisionObjUtils.clientPositionCharacter(transparentEnt, new Vector3d(EntityUtils.getPosition(transparentEnt)), new Quaterniond());
} else {
EntityCreationUtils.bypassShadowPass(transparentEnt);
@ -182,12 +182,12 @@ public class BlockChunkEntity {
*/
public static void serverCreateBlockChunkEntity(Entity entity, BlockMeshData blockChunkData){
if(blockChunkData.getVertices().length > 0){
PhysicsEntityUtils.serverAttachMultiShapeTriGeomRigidBody(entity, blockChunkData);
PhysicsEntityUtils.serverAttachMultiShapeTriGeomCollider(entity, blockChunkData);
Realm realm = Globals.serverState.realmManager.getEntityRealm(entity);
DBody terrainBody = PhysicsEntityUtils.getDBody(entity);
DGeom terrainCollider = PhysicsEntityUtils.getDGeom(entity);
Vector3d entityPos = EntityUtils.getPosition(entity);
Quaterniond entityRot = EntityUtils.getRotation(entity);
PhysicsUtils.setRigidBodyTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainBody);
PhysicsUtils.setGeomTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainCollider);
entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
CommonEntityUtils.setEntityType(entity, EntityType.ENGINE);
}

View File

@ -6,7 +6,7 @@ import java.util.concurrent.Executors;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3i;
import org.ode4j.ode.DBody;
import org.ode4j.ode.DGeom;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.terrain.cells.ClientDrawCellManager;
@ -73,7 +73,7 @@ public class TerrainChunk {
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas, notifyTarget, toDelete);
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
if(levelOfDetail == BlockChunkData.LOD_FULL_RES && data.getFaceElements().length > 0){
PhysicsEntityUtils.clientAttachTriGeomRigidBody(rVal, data);
PhysicsEntityUtils.clientAttachTriGeomCollider(rVal, data);
Vector3d finalPos = new Vector3d(EntityUtils.getPosition(rVal));
CollisionObjUtils.clientPositionCharacter(rVal, finalPos, new Quaterniond());
} else {
@ -131,11 +131,11 @@ public class TerrainChunk {
if(data.getVertices().length > 0){
Realm realm = Globals.serverState.realmManager.getEntityRealm(entity);
if(realm != null){
PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, data);
DBody terrainBody = PhysicsEntityUtils.getDBody(entity);
PhysicsEntityUtils.serverAttachTriGeomCollider(entity, data);
DGeom terrainGeom = PhysicsEntityUtils.getDGeom(entity);
Vector3d entityPos = EntityUtils.getPosition(entity);
Quaterniond entityRot = EntityUtils.getRotation(entity);
PhysicsUtils.setRigidBodyTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainBody);
PhysicsUtils.setGeomTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainGeom);
entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
CommonEntityUtils.setEntityType(entity, EntityType.ENGINE);
}

View File

@ -94,10 +94,12 @@ public class PhysicsDataCell {
return;
}
//generate
//generate terrain
this.terrainChunkData = TerrainChunk.serverGenerateTerrainChunkData(weights, types);
TerrainChunk.serverCreateTerrainChunkEntity(localPhysicsEnt, this.terrainChunkData);
localPhysicsEnt.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
//generate blocks
this.blockData = BlockMeshgen.rasterize(this.blockChunk);
BlockChunkEntity.serverCreateBlockChunkEntity(localBlockPhysicsEntity, this.blockData);
localBlockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);