physics work
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-06-05 11:00:34 -04:00
parent e57658467b
commit 8bc8fb464b
9 changed files with 233 additions and 30 deletions

View File

@ -2119,6 +2119,7 @@ Fix projection matrix being sent to light manager
(06/05/2025) (06/05/2025)
voxel tests voxel tests
Physics work

View File

@ -7,6 +7,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.joml.Matrix4d; import org.joml.Matrix4d;
import org.joml.Quaterniond; import org.joml.Quaterniond;
@ -135,6 +136,12 @@ public class CollisionEngine {
*/ */
protected static final int MAX_CONTACTS = 64; protected static final int MAX_CONTACTS = 64;
/**
* <p> Maximum number of contact points per geom-geom collision </p>
* <p><b> Note: </b></p>
*/
protected static final int MIN_CONTACTS = 4;
/** /**
* The list of dbodies ode should be tracking * The list of dbodies ode should be tracking
*/ */
@ -265,13 +272,13 @@ public class CollisionEngine {
case Collidable.TYPE_CREATURE: { case Collidable.TYPE_CREATURE: {
switch(impactor.getType()){ switch(impactor.getType()){
case Collidable.TYPE_STATIC: { 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; } break;
case Collidable.TYPE_CREATURE: { 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; } break;
case Collidable.TYPE_OBJECT: { 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;
} }
} break; } break;
@ -1608,12 +1615,15 @@ public class CollisionEngine {
* @return The status of the collision engine * @return The status of the collision engine
*/ */
public String getStatus(){ public String getStatus(){
CollisionEngine.lockOde();
String message = "" + String message = "" +
"Name: " + this.name + "\n" + "Name: " + this.name + "\n" +
"Bodies: " + this.bodies.size() + "\n" + "Bodies: " + this.bodies.size() + "\n" +
"Body Ptrs: " + this.bodyPointerMap.size() + "\n" + "Body Ptrs: " + this.bodyPointerMap.size() + "\n" +
"Geom Ptrs: " + this.geomPointerMap.size() + "\n" + "Geom Ptrs: " + this.geomPointerMap.size() + "\n" +
"Collidables: " + this.collidableList.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" + "Space geom count: " + this.space.getNumGeoms() + "\n" +
"Tracked geom count: " + this.geomCount + "\n" + "Tracked geom count: " + this.geomCount + "\n" +
"Floating origin: " + this.floatingOrigin.x + "," + this.floatingOrigin.y + "," + this.floatingOrigin.z + "\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" + "Final Collision Count: " + this.finalCollisionCount + "\n" +
"" ""
; ;
CollisionEngine.unlockOde();
return message; return message;
} }

View File

@ -25,6 +25,11 @@ public class PhysicsCallback implements DNearCallback {
*/ */
protected CollisionEngine engine; protected CollisionEngine engine;
/**
* Enables geom-geom collisions between non-statics
*/
private boolean enableGeomGeom = false;
/** /**
* Constructor * Constructor
*/ */
@ -43,7 +48,13 @@ public class PhysicsCallback implements DNearCallback {
} }
//if neither are bodies //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; return;
} }
@ -91,6 +102,16 @@ public class PhysicsCallback implements DNearCallback {
throw new Error(message); 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"); Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - Full collision phase");
try { try {
Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - setup"); Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - setup");
@ -101,7 +122,7 @@ public class PhysicsCallback implements DNearCallback {
if(c2 != null){ if(c2 != null){
surfaceParams2 = c2.getSurfaceParams(); 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); DContact contact = engine.contacts.get(i);
contact.surface.mode = surfaceParams1.getMode(); contact.surface.mode = surfaceParams1.getMode();
contact.surface.mu = surfaceParams1.getMu(); contact.surface.mu = surfaceParams1.getMu();
@ -144,7 +165,7 @@ public class PhysicsCallback implements DNearCallback {
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
//calculate collisions //calculate collisions
Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - OdeHelper.collide"); 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(); Globals.profiler.endCpuSample();
//create DContacts based on each collision that occurs //create DContacts based on each collision that occurs
Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - contact iterations"); Globals.profiler.beginAggregateCpuSample("CollisionEngine.nearCallback - contact iterations");
@ -160,18 +181,20 @@ public class PhysicsCallback implements DNearCallback {
} }
// //
//add contact to contact group //add contact to contact group - don't create contacts for non-geom collisions
DJoint c = OdeHelper.createContactJoint(engine.world,engine.contactgroup,contact); if(!isGeomGeomCollision){
if(b1 == null){ DJoint c = OdeHelper.createContactJoint(engine.world,engine.contactgroup,contact);
if(b2 == null){ if(b1 == null){
if(b2 == null){
} else {
c.attach(null,b2);
}
} else { } else {
c.attach(null,b2); if(b2 == null){
} c.attach(b1,null);
} else { } else {
if(b2 == null){ c.attach(b1,b2);
c.attach(b1,null); }
} else {
c.attach(b1,b2);
} }
} }

View File

@ -4,15 +4,21 @@ import electrosphere.entity.Entity;
import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.state.collidable.Impulse;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.joml.Vector3d;
/** /**
* Stores the type of the collidable object as well as the impulses currently applied to it * Stores the type of the collidable object as well as the impulses currently applied to it
*/ */
public class Collidable { 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 * The entity this collidable is attached to
*/ */
@ -31,7 +37,12 @@ public class Collidable {
/** /**
* The impulses to be applied to this collidable * The impulses to be applied to this collidable
*/ */
private List<Impulse> impulses = new LinkedList<Impulse>(); 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 * The params for the surface of this collidable when a collision occurs
@ -78,6 +89,9 @@ public class Collidable {
this.type = type; this.type = type;
this.parentTracksCollidable = parentTracksCollidable; this.parentTracksCollidable = parentTracksCollidable;
this.surfaceParams = new SurfaceParams(); 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; return this.surfaceParams;
} }
public List<Impulse> getImpulses() { /**
* Gets the array of impulses
* @return The array of impulses
*/
public Impulse[] getImpulses() {
return impulses; return impulses;
} }
/**
* Adds an impulse the collidable
* @param impulse The impulse
*/
public void addImpulse(Impulse 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() { public Entity getParent() {
@ -124,8 +168,14 @@ public class Collidable {
this.type = type; this.type = type;
} }
/**
* Clears the impulses
*/
public void clear(){ 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; this.ready = ready;
} }
/**
* Gets the number of impulses stored in the collidable
* @return The number of impulses
*/
public int getImpulseCount(){
return this.impulseCount;
}

View File

@ -21,25 +21,109 @@ public class Impulse {
this.worldPoint = worldPoint; 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() { public Vector3d getDirection() {
return direction; return direction;
} }
/**
* Gets the force of the impulse
* @return The force of the impulse
*/
public double getForce() { public double getForce() {
return force; return force;
} }
/**
* Gets the type of the impulse
* @return The type of the impulse
*/
public String getType() { public String getType() {
return type; return type;
} }
/**
* Gets the collision point of the impulse
* @return The collision point of the impulse
*/
public Vector3d getCollisionPoint() { public Vector3d getCollisionPoint() {
return collisionPoint; return collisionPoint;
} }
/**
* Gets the world point of the impulse
* @return The world point of the impulse
*/
public Vector3d getWorldPoint(){ public Vector3d getWorldPoint(){
return worldPoint; 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;
}
} }

View File

@ -3,10 +3,13 @@ package electrosphere.entity.state.collidable;
import electrosphere.collision.collidable.Collidable; import electrosphere.collision.collidable.Collidable;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.gravity.ServerGravityTree; import electrosphere.entity.state.gravity.ServerGravityTree;
import electrosphere.entity.state.movement.fall.ServerFallTree; import electrosphere.entity.state.movement.fall.ServerFallTree;
import org.joml.Vector3d;
import org.ode4j.ode.DBody; import org.ode4j.ode.DBody;
import org.ode4j.ode.DGeom; import org.ode4j.ode.DGeom;
@ -25,6 +28,11 @@ public class ServerCollidableTree implements BehaviorTree {
*/ */
protected DBody body; protected DBody body;
/**
* The geom
*/
protected DGeom geom;
/** /**
* The collidable * The collidable
*/ */
@ -52,6 +60,7 @@ public class ServerCollidableTree implements BehaviorTree {
public ServerCollidableTree(Entity e, Collidable collidable, DGeom geom){ public ServerCollidableTree(Entity e, Collidable collidable, DGeom geom){
parent = e; parent = e;
this.collidable = collidable; this.collidable = collidable;
this.geom = geom;
} }
/** /**
@ -61,16 +70,22 @@ public class ServerCollidableTree implements BehaviorTree {
public void simulate(float deltaTime){ public void simulate(float deltaTime){
//have we hit a terrain impulse? //have we hit a terrain impulse?
//handle impulses //handle impulses
for(Impulse impulse : collidable.getImpulses()){ Impulse[] impulses = collidable.getImpulses();
if(impulse.type.matches(Collidable.TYPE_CREATURE)){ 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){ if(ServerGravityTree.getServerGravityTree(parent)!=null){
ServerGravityTree.getServerGravityTree(parent).start(); 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(); this.resetGravityFall();
pos.add(impulses[i].getDirection().mul(impulses[i].getForce()));
} }
} }
if(geom != null){
ServerEntityUtils.repositionEntity(parent, pos);
}
collidable.setReady(true); collidable.setReady(true);

View File

@ -119,7 +119,9 @@ public class ClientGravityTree implements BehaviorTree {
public boolean hadGroundCollision(){ public boolean hadGroundCollision(){
boolean rVal = false; 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)){ if(impulse.getType().equals(Collidable.TYPE_STATIC)){
rVal = true; rVal = true;
break; break;
@ -142,7 +144,9 @@ public class ClientGravityTree implements BehaviorTree {
public boolean hadEntityCollision(){ public boolean hadEntityCollision(){
boolean rVal = false; 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)){ if(impulse.getType().equals(Collidable.TYPE_CREATURE)){
rVal = true; rVal = true;
break; break;

View File

@ -138,7 +138,9 @@ public class ServerGravityTree implements BehaviorTree {
*/ */
public boolean hadGroundCollision(){ public boolean hadGroundCollision(){
boolean rVal = false; 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)){ if(impulse.getType().equals(Collidable.TYPE_STATIC)){
rVal = true; rVal = true;
break; break;
@ -165,7 +167,9 @@ public class ServerGravityTree implements BehaviorTree {
*/ */
public boolean hadEntityCollision(){ public boolean hadEntityCollision(){
boolean rVal = false; 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)){ if(impulse.getType().equals(Collidable.TYPE_CREATURE)){
rVal = true; rVal = true;
break; break;

View File

@ -141,7 +141,10 @@ public class ClientFallTree implements BehaviorTree {
public boolean hadGroundCollision(){ public boolean hadGroundCollision(){
boolean rVal = false; boolean rVal = false;
if(PhysicsEntityUtils.getCollidable(parent) != null){ 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)){ if(impulse.getType().equals(Collidable.TYPE_STATIC)){
rVal = true; rVal = true;
break; break;