Renderer/src/main/java/electrosphere/collision/RayCastCallback.java
austin 64a3628316
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
Add interaction usage data and functionality
2024-11-14 19:49:31 -05:00

169 lines
7.1 KiB
Java

package electrosphere.collision;
import static org.ode4j.ode.OdeConstants.dContactBounce;
import static org.ode4j.ode.OdeConstants.dContactSoftCFM;
import static org.ode4j.ode.OdeConstants.dInfinity;
import static org.ode4j.ode.OdeHelper.areConnectedExcluding;
import java.util.List;
import java.util.Map;
import org.joml.Vector3d;
import org.ode4j.ode.DBody;
import org.ode4j.ode.DContact;
import org.ode4j.ode.DContactBuffer;
import org.ode4j.ode.DContactJoint;
import org.ode4j.ode.DGeom;
import org.ode4j.ode.DGeom.DNearCallback;
import org.ode4j.ode.DRay;
import org.ode4j.ode.OdeHelper;
import electrosphere.collision.collidable.Collidable;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.logger.LoggerInterface;
public class RayCastCallback implements DNearCallback {
/**
* Maximum number of contacts allowed
*/
static final int MAX_CONTACTS = 5;
/**
* Really far away from the ray origin point
*/
static final double REALLY_LONG_DISTANCE = 1000000;
/**
* // Check ray collision against a space
void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) {
dReal *HitPosition = (dReal *)Data;
// Check collisions
dContact Contacts[MAX_CONTACTS];
int Count = dCollide(Geometry1, Geometry2, MAX_CONTACTS, &Contacts[0].geom, sizeof(dContact));
for(int i = 0; i < Count; i++) {
// Check depth against current closest hit
if(Contacts[i].geom.depth < HitPosition[3]) {
dCopyVector3(HitPosition, Contacts[i].geom.pos);
HitPosition[3] = Contacts[i].geom.depth;
}
}
}
*/
//For the current execution, this stores the shortest length that has currently been encountered.
//This is used to keep track of the closest body so that there doesn't need to be contact join creation.
//This should be reset every time a ray cast is called in collision engine by calling setLength in this object.
double shortestLength = REALLY_LONG_DISTANCE;
@Override
public void call(Object data, DGeom o1, DGeom o2) {
// if (o1->body && o2->body) return;
RayCastCallbackData rayCastData = (RayCastCallbackData)data;
//null out potentially previous results
// rayCastData.collisionPosition = null;
// rayCastData.collidedEntity = null;
// exit without doing anything if the two bodies are connected by a joint
DBody b1 = o1.getBody();
DBody b2 = o2.getBody();
if (b1!=null && b2!=null && areConnectedExcluding (b1,b2,DContactJoint.class)) return;
//creates a buffer to store potential collisions
DContactBuffer contacts = new DContactBuffer(MAX_CONTACTS); // up to MAX_CONTACTS contacts per box-box
for (int i=0; i<MAX_CONTACTS; i++) {
DContact contact = contacts.get(i);
contact.surface.mode = dContactBounce | dContactSoftCFM;
contact.surface.mu = dInfinity;
contact.surface.mu2 = 0;
contact.surface.bounce = 0.1;
contact.surface.bounce_vel = 0.1;
contact.surface.soft_cfm = 0.01;
}
Collidable collidable1 = rayCastData.bodyEntityMap.get(b1);
Collidable collidable2 = rayCastData.bodyEntityMap.get(b2);
if(
(
rayCastData.collidableTypeMask == null ||
(
rayCastData.collidableTypeMask != null &&
(
(o1 instanceof DRay && rayCastData.collidableTypeMask.contains(collidable2.getType())) ||
(o2 instanceof DRay && rayCastData.collidableTypeMask.contains(collidable1.getType()))
)
)
) &&
collidable1 != null && collidable1.getParent() != Globals.playerEntity && //don't self cast -- should work on both server and client
collidable2 != null && collidable2.getParent() != Globals.playerEntity //don't self cast -- should work on both server and client
){
//calculate collisions
int numc = OdeHelper.collide(o1,o2,MAX_CONTACTS,contacts.getGeomBuffer());
//create DContacts based on each collision that occurs
if (numc != 0) {
for (int i=0; i<numc; i++) {
DContact contact = contacts.get(i);
double depth = contact.geom.depth;
//check if should be stored in ray cast data return
if(depth < shortestLength){
shortestLength = depth;
if(collidable1 != null){
rayCastData.collidedEntity = collidable1.getParent();
rayCastData.collisionPosition = new Vector3d(contact.geom.pos.get0(),contact.geom.pos.get1(),contact.geom.pos.get2());
} else if(collidable2 != null) {
rayCastData.collidedEntity = collidable2.getParent();
rayCastData.collisionPosition = new Vector3d(contact.geom.pos.get0(),contact.geom.pos.get1(),contact.geom.pos.get2());
} else if(rayCastData.collidableTypeMask == null){
rayCastData.collisionPosition = new Vector3d(contact.geom.pos.get0(),contact.geom.pos.get1(),contact.geom.pos.get2());
} else {
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Collided with entity that is not defined in the rayCastData.bodyEntityMap! \"" + collidable1 + "\",\"" + collidable2 + "\""));
}
}
}
if(rayCastData.collisionPosition == null){
String errorMessage = "Collision error!\n" +
"body1: " + b1 + "\n" +
"body2: " + b2 + "\n" +
"collidable1: " + collidable1 + "\n" +
"collidable2: " + collidable2 + "\n";
LoggerInterface.loggerEngine.ERROR(new IllegalStateException(errorMessage));
}
}
}
}
/**
* Sets the length that the ray should travel
* @param length The length
*/
protected void setLength(double length){
this.shortestLength = length + 0.1;
}
/**
* Data object that contains the information for a ray cast check
*/
static class RayCastCallbackData {
//The map of ode DBody -> collidable
Map<DBody,Collidable> bodyEntityMap;
//The mask of collidable types to filter collisions by. Can be null.
List<String> collidableTypeMask;
//The entity that the ray cast collided with. If null, no entity was collided with.
Entity collidedEntity = null;
//The position in world space that the collision happened
Vector3d collisionPosition = null;
/**
* Constructor
* @param bodyEntityMap The map of ode DBody -> collidable
* @param collidableTypeMask The mask of collidable types to filter collisions by. Can be null.
*/
public RayCastCallbackData(Map<DBody,Collidable> bodyEntityMap,List<String> collidableTypeMask){
this.bodyEntityMap = bodyEntityMap;
this.collidableTypeMask = collidableTypeMask;
}
}
}