From 8bc8fb464b74a50bd9fc1d80b04ac6c5a05a02a8 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 5 Jun 2025 11:00:34 -0400 Subject: [PATCH] physics work --- docs/src/progress/renderertodo.md | 1 + .../collision/CollisionEngine.java | 17 +++- .../collision/PhysicsCallback.java | 51 +++++++---- .../collision/collidable/Collidable.java | 68 +++++++++++++-- .../entity/state/collidable/Impulse.java | 84 +++++++++++++++++++ .../collidable/ServerCollidableTree.java | 21 ++++- .../state/gravity/ClientGravityTree.java | 8 +- .../state/gravity/ServerGravityTree.java | 8 +- .../state/movement/fall/ClientFallTree.java | 5 +- 9 files changed, 233 insertions(+), 30 deletions(-) diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 783a724b..9dc19f22 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -2119,6 +2119,7 @@ Fix projection matrix being sent to light manager (06/05/2025) voxel tests +Physics work diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index 586a4c33..c54ac599 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -7,6 +7,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import org.joml.Matrix4d; import org.joml.Quaterniond; @@ -135,6 +136,12 @@ public class CollisionEngine { */ protected static final int MAX_CONTACTS = 64; + /** + *

Maximum number of contact points per geom-geom collision

+ *

Note:

+ */ + protected static final int MIN_CONTACTS = 4; + /** * The list of dbodies ode should be tracking */ @@ -265,13 +272,13 @@ public class CollisionEngine { case Collidable.TYPE_CREATURE: { switch(impactor.getType()){ case Collidable.TYPE_STATIC: { - receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude * 2, Collidable.TYPE_STATIC)); + receiver.addImpulse(normal, localPosition, worldPos, magnitude * 2, Collidable.TYPE_STATIC); } break; case Collidable.TYPE_CREATURE: { - receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_CREATURE)); + receiver.addImpulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_CREATURE); } break; case Collidable.TYPE_OBJECT: { - receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT)); + receiver.addImpulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT); } break; } } break; @@ -1608,12 +1615,15 @@ public class CollisionEngine { * @return The status of the collision engine */ public String getStatus(){ + CollisionEngine.lockOde(); String message = "" + "Name: " + this.name + "\n" + "Bodies: " + this.bodies.size() + "\n" + "Body Ptrs: " + this.bodyPointerMap.size() + "\n" + "Geom Ptrs: " + this.geomPointerMap.size() + "\n" + "Collidables: " + this.collidableList.size() + "\n" + + " (Static) Collidables: " + this.collidableList.stream().filter((Collidable collidable) -> collidable.getType().matches(Collidable.TYPE_STATIC)).collect(Collectors.toList()).size() + "\n" + + " (Creature) Collidables: " + this.collidableList.stream().filter((Collidable collidable) -> collidable.getType().matches(Collidable.TYPE_CREATURE)).collect(Collectors.toList()).size() + "\n" + "Space geom count: " + this.space.getNumGeoms() + "\n" + "Tracked geom count: " + this.geomCount + "\n" + "Floating origin: " + this.floatingOrigin.x + "," + this.floatingOrigin.y + "," + this.floatingOrigin.z + "\n" + @@ -1621,6 +1631,7 @@ public class CollisionEngine { "Final Collision Count: " + this.finalCollisionCount + "\n" + "" ; + CollisionEngine.unlockOde(); return message; } diff --git a/src/main/java/electrosphere/collision/PhysicsCallback.java b/src/main/java/electrosphere/collision/PhysicsCallback.java index 3d928815..2ce2f4bd 100644 --- a/src/main/java/electrosphere/collision/PhysicsCallback.java +++ b/src/main/java/electrosphere/collision/PhysicsCallback.java @@ -25,6 +25,11 @@ public class PhysicsCallback implements DNearCallback { */ protected CollisionEngine engine; + /** + * Enables geom-geom collisions between non-statics + */ + private boolean enableGeomGeom = false; + /** * Constructor */ @@ -43,7 +48,13 @@ public class PhysicsCallback implements DNearCallback { } //if neither are bodies - if(o1.getBody() == null && o2.getBody() == null){ + if( + o1.getBody() == null && o2.getBody() == null && + ( + !this.enableGeomGeom || + (o1.getCategoryBits() == Collidable.TYPE_STATIC_BIT && o2.getCategoryBits() == Collidable.TYPE_STATIC_BIT) + ) + ){ return; } @@ -91,6 +102,16 @@ public class PhysicsCallback implements DNearCallback { throw new Error(message); } + //Controls whether we should grab MAX_CONTACTS or MIN_CONTACTS + boolean isGeomGeomCollision = o1.getBody() == null && o2.getBody() == null; + + //Number of contacts to poll for + int contactCount = CollisionEngine.MAX_CONTACTS; + if(isGeomGeomCollision && this.enableGeomGeom){ + contactCount = CollisionEngine.MIN_CONTACTS; + } + + Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - Full collision phase"); try { Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - setup"); @@ -101,7 +122,7 @@ public class PhysicsCallback implements DNearCallback { if(c2 != null){ surfaceParams2 = c2.getSurfaceParams(); } - for (int i=0; i< CollisionEngine.MAX_CONTACTS; i++) { + for (int i=0; i< contactCount; i++) { DContact contact = engine.contacts.get(i); contact.surface.mode = surfaceParams1.getMode(); contact.surface.mu = surfaceParams1.getMu(); @@ -144,7 +165,7 @@ public class PhysicsCallback implements DNearCallback { Globals.profiler.endCpuSample(); //calculate collisions Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - OdeHelper.collide"); - int numc = OdeHelper.collide(o1,o2,CollisionEngine.MAX_CONTACTS,engine.contacts.getGeomBuffer()); + int numc = OdeHelper.collide(o1,o2,contactCount,engine.contacts.getGeomBuffer()); Globals.profiler.endCpuSample(); //create DContacts based on each collision that occurs Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - contact iterations"); @@ -160,18 +181,20 @@ public class PhysicsCallback implements DNearCallback { } // - //add contact to contact group - DJoint c = OdeHelper.createContactJoint(engine.world,engine.contactgroup,contact); - if(b1 == null){ - if(b2 == null){ + //add contact to contact group - don't create contacts for non-geom collisions + if(!isGeomGeomCollision){ + DJoint c = OdeHelper.createContactJoint(engine.world,engine.contactgroup,contact); + if(b1 == null){ + if(b2 == null){ + } else { + c.attach(null,b2); + } } else { - c.attach(null,b2); - } - } else { - if(b2 == null){ - c.attach(b1,null); - } else { - c.attach(b1,b2); + if(b2 == null){ + c.attach(b1,null); + } else { + c.attach(b1,b2); + } } } diff --git a/src/main/java/electrosphere/collision/collidable/Collidable.java b/src/main/java/electrosphere/collision/collidable/Collidable.java index e7bf9d7f..8f782f1e 100644 --- a/src/main/java/electrosphere/collision/collidable/Collidable.java +++ b/src/main/java/electrosphere/collision/collidable/Collidable.java @@ -4,14 +4,20 @@ import electrosphere.entity.Entity; import electrosphere.entity.state.collidable.Impulse; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; +import org.joml.Vector3d; + /** * Stores the type of the collidable object as well as the impulses currently applied to it */ public class Collidable { + + /** + * Max impulses that can be applied to a collidable + */ + public static final int MAX_IMPULSES = 5; /** * The entity this collidable is attached to @@ -31,7 +37,12 @@ public class Collidable { /** * The impulses to be applied to this collidable */ - private List impulses = new LinkedList(); + private Impulse[] impulses = new Impulse[MAX_IMPULSES]; + + /** + * The number of impulses stored in the collidable + */ + private int impulseCount = 0; /** * The params for the surface of this collidable when a collision occurs @@ -78,6 +89,9 @@ public class Collidable { this.type = type; this.parentTracksCollidable = parentTracksCollidable; this.surfaceParams = new SurfaceParams(); + for(int i = 0; i < MAX_IMPULSES; i++){ + this.impulses[i] = new Impulse(); + } } /** @@ -96,12 +110,42 @@ public class Collidable { return this.surfaceParams; } - public List getImpulses() { + /** + * Gets the array of impulses + * @return The array of impulses + */ + public Impulse[] getImpulses() { return impulses; } + /** + * Adds an impulse the collidable + * @param impulse The impulse + */ public void addImpulse(Impulse impulse) { - impulses.add(impulse); + if(this.impulseCount < MAX_IMPULSES){ + impulses[this.impulseCount].setCollisionPoint(impulse.getCollisionPoint()); + impulses[this.impulseCount].setDirection(impulse.getDirection()); + impulses[this.impulseCount].setWorldPoint(impulse.getWorldPoint()); + impulses[this.impulseCount].setType(impulse.getType()); + impulses[this.impulseCount].setForce(impulse.getForce()); + this.impulseCount++; + } + } + + /** + * Adds an impulse the collidable + * @param impulse The impulse + */ + public void addImpulse(Vector3d direction, Vector3d collisionPoint, Vector3d worldPoint, double force, String type){ + if(this.impulseCount < MAX_IMPULSES){ + impulses[this.impulseCount].setCollisionPoint(collisionPoint); + impulses[this.impulseCount].setDirection(direction); + impulses[this.impulseCount].setWorldPoint(worldPoint); + impulses[this.impulseCount].setType(type); + impulses[this.impulseCount].setForce(force); + this.impulseCount++; + } } public Entity getParent() { @@ -124,8 +168,14 @@ public class Collidable { this.type = type; } + /** + * Clears the impulses + */ public void clear(){ - impulses.clear(); + for(int i = 0; i < MAX_IMPULSES; i++){ + impulses[i].clear(); + } + this.impulseCount = 0; } /** @@ -144,6 +194,14 @@ public class Collidable { this.ready = ready; } + /** + * Gets the number of impulses stored in the collidable + * @return The number of impulses + */ + public int getImpulseCount(){ + return this.impulseCount; + } + diff --git a/src/main/java/electrosphere/entity/state/collidable/Impulse.java b/src/main/java/electrosphere/entity/state/collidable/Impulse.java index 7812d618..192cc7f3 100644 --- a/src/main/java/electrosphere/entity/state/collidable/Impulse.java +++ b/src/main/java/electrosphere/entity/state/collidable/Impulse.java @@ -21,25 +21,109 @@ public class Impulse { this.worldPoint = worldPoint; } + /** + * Constructor for collidable work + */ + public Impulse(){ + this.direction = new Vector3d(); + this.collisionPoint = new Vector3d(); + this.worldPoint = new Vector3d(); + this.force = 0; + this.type = null; + } + + /** + * Gets the direction of the impulse + * @return The direction of the impulse + */ public Vector3d getDirection() { return direction; } + /** + * Gets the force of the impulse + * @return The force of the impulse + */ public double getForce() { return force; } + /** + * Gets the type of the impulse + * @return The type of the impulse + */ public String getType() { return type; } + /** + * Gets the collision point of the impulse + * @return The collision point of the impulse + */ public Vector3d getCollisionPoint() { return collisionPoint; } + /** + * Gets the world point of the impulse + * @return The world point of the impulse + */ public Vector3d getWorldPoint(){ return worldPoint; } + + /** + * Sets the direction of the impulse + * @param direction The direction of the impulse + */ + public void setDirection(Vector3d direction) { + this.direction = direction; + } + + /** + * Sets the collision point of the impulse + * @param collisionPoint The collision point of the impulse + */ + public void setCollisionPoint(Vector3d collisionPoint) { + this.collisionPoint = collisionPoint; + } + + /** + * Sets the world point of the impulse + * @param worldPoint The world point of the impulse + */ + public void setWorldPoint(Vector3d worldPoint) { + this.worldPoint = worldPoint; + } + + /** + * Sets the force of the impulse + * @param force The force of the impulse + */ + public void setForce(double force) { + this.force = force; + } + + /** + * Sets the type of the impulse + * @param type The type of the impulse + */ + public void setType(String type) { + this.type = type; + } + + /** + * Clears the data in the impulse + */ + public void clear(){ + this.direction.set(0,0,0); + this.collisionPoint.set(0,0,0); + this.worldPoint.set(0,0,0); + this.force = 0; + this.type = null; + } + + } diff --git a/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java b/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java index 947552a7..6f75d674 100644 --- a/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java +++ b/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java @@ -3,10 +3,13 @@ package electrosphere.entity.state.collidable; import electrosphere.collision.collidable.Collidable; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.gravity.ServerGravityTree; import electrosphere.entity.state.movement.fall.ServerFallTree; +import org.joml.Vector3d; import org.ode4j.ode.DBody; import org.ode4j.ode.DGeom; @@ -25,6 +28,11 @@ public class ServerCollidableTree implements BehaviorTree { */ protected DBody body; + /** + * The geom + */ + protected DGeom geom; + /** * The collidable */ @@ -52,6 +60,7 @@ public class ServerCollidableTree implements BehaviorTree { public ServerCollidableTree(Entity e, Collidable collidable, DGeom geom){ parent = e; this.collidable = collidable; + this.geom = geom; } /** @@ -61,16 +70,22 @@ public class ServerCollidableTree implements BehaviorTree { public void simulate(float deltaTime){ //have we hit a terrain impulse? //handle impulses - for(Impulse impulse : collidable.getImpulses()){ - if(impulse.type.matches(Collidable.TYPE_CREATURE)){ + Impulse[] impulses = collidable.getImpulses(); + Vector3d pos = EntityUtils.getPosition(parent); + for(int i = 0; i < collidable.getImpulseCount(); i++){ + if(impulses[i].type.matches(Collidable.TYPE_CREATURE)){ if(ServerGravityTree.getServerGravityTree(parent)!=null){ ServerGravityTree.getServerGravityTree(parent).start(); } } - if(impulse.type.matches(Collidable.TYPE_WORLD_BOUND) || impulse.type.matches(Collidable.TYPE_STATIC)){ + if(impulses[i].type.matches(Collidable.TYPE_WORLD_BOUND) || impulses[i].type.matches(Collidable.TYPE_STATIC)){ this.resetGravityFall(); + pos.add(impulses[i].getDirection().mul(impulses[i].getForce())); } } + if(geom != null){ + ServerEntityUtils.repositionEntity(parent, pos); + } collidable.setReady(true); diff --git a/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java b/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java index 48a4341b..6af877de 100644 --- a/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java +++ b/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java @@ -119,7 +119,9 @@ public class ClientGravityTree implements BehaviorTree { public boolean hadGroundCollision(){ boolean rVal = false; - for(Impulse impulse : collidable.getImpulses()){ + Impulse[] impulses = collidable.getImpulses(); + for(int i = 0; i < collidable.getImpulseCount(); i++){ + Impulse impulse = impulses[i]; if(impulse.getType().equals(Collidable.TYPE_STATIC)){ rVal = true; break; @@ -142,7 +144,9 @@ public class ClientGravityTree implements BehaviorTree { public boolean hadEntityCollision(){ boolean rVal = false; - for(Impulse impulse : collidable.getImpulses()){ + Impulse[] impulses = collidable.getImpulses(); + for(int i = 0; i < collidable.getImpulseCount(); i++){ + Impulse impulse = impulses[i]; if(impulse.getType().equals(Collidable.TYPE_CREATURE)){ rVal = true; break; diff --git a/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java b/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java index 4cd07c26..af0a4c15 100644 --- a/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java +++ b/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java @@ -138,7 +138,9 @@ public class ServerGravityTree implements BehaviorTree { */ public boolean hadGroundCollision(){ boolean rVal = false; - for(Impulse impulse : collidable.getImpulses()){ + Impulse[] impulses = collidable.getImpulses(); + for(int i = 0; i < collidable.getImpulseCount(); i++){ + Impulse impulse = impulses[i]; if(impulse.getType().equals(Collidable.TYPE_STATIC)){ rVal = true; break; @@ -165,7 +167,9 @@ public class ServerGravityTree implements BehaviorTree { */ public boolean hadEntityCollision(){ boolean rVal = false; - for(Impulse impulse : collidable.getImpulses()){ + Impulse[] impulses = collidable.getImpulses(); + for(int i = 0; i < collidable.getImpulseCount(); i++){ + Impulse impulse = impulses[i]; if(impulse.getType().equals(Collidable.TYPE_CREATURE)){ rVal = true; break; diff --git a/src/main/java/electrosphere/entity/state/movement/fall/ClientFallTree.java b/src/main/java/electrosphere/entity/state/movement/fall/ClientFallTree.java index c44351a8..1c5f5d13 100644 --- a/src/main/java/electrosphere/entity/state/movement/fall/ClientFallTree.java +++ b/src/main/java/electrosphere/entity/state/movement/fall/ClientFallTree.java @@ -141,7 +141,10 @@ public class ClientFallTree implements BehaviorTree { public boolean hadGroundCollision(){ boolean rVal = false; if(PhysicsEntityUtils.getCollidable(parent) != null){ - for(Impulse impulse : PhysicsEntityUtils.getCollidable(parent).getImpulses()){ + Collidable collidable = PhysicsEntityUtils.getCollidable(parent); + Impulse[] impulses = collidable.getImpulses(); + for(int i = 0; i < collidable.getImpulseCount(); i++){ + Impulse impulse = impulses[i]; if(impulse.getType().equals(Collidable.TYPE_STATIC)){ rVal = true; break;