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 static org.ode4j.ode.OdeMath.dCalcVectorLengthSquare3; // import static org.ode4j.ode.OdeMath.dSubtractVectors3; // import static org.ode4j.ode.internal.Common.dRecip; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; import org.joml.Matrix4d; import org.joml.Quaterniond; import org.joml.Vector3d; import org.joml.Vector4d; import org.ode4j.math.DMatrix3; import org.ode4j.math.DVector3; import org.ode4j.ode.DBody; import org.ode4j.ode.DBox; import org.ode4j.ode.DCapsule; import org.ode4j.ode.DContact; import org.ode4j.ode.DContactBuffer; import org.ode4j.ode.DContactGeom; import org.ode4j.ode.DContactJoint; import org.ode4j.ode.DCylinder; import org.ode4j.ode.DGeom; import org.ode4j.ode.DGeom.DNearCallback; import org.ode4j.ode.DJoint; import org.ode4j.ode.DJointGroup; import org.ode4j.ode.DMass; import org.ode4j.ode.DRay; import org.ode4j.ode.DSpace; import org.ode4j.ode.DSphere; import org.ode4j.ode.DTriMesh; import org.ode4j.ode.DTriMeshData; import org.ode4j.ode.DWorld; import org.ode4j.ode.OdeConstants; import org.ode4j.ode.OdeHelper; import electrosphere.collision.RayCastCallback.RayCastCallbackData; import electrosphere.collision.collidable.Collidable; import electrosphere.engine.Globals; import electrosphere.engine.time.Timekeeper; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.state.physicssync.ServerPhysicsSyncTree; import electrosphere.game.data.collidable.CollidableTemplate; import electrosphere.game.data.collidable.HitboxData; import electrosphere.logger.LoggerInterface; /** * The main collision engine class. Tracks all entities that collide in its system and fires callbacks when they do. */ public class CollisionEngine { //gravity constant public static final float GRAVITY_MAGNITUDE = 9.8f * 2; //world data that the collision engine leverages for position correction and the like CollisionWorldData collisionWorldData; //Ode-specific stuff private DWorld world; private DSpace space; private static Semaphore spaceLock = new Semaphore(1); private DJointGroup contactgroup; private static final int MAX_CONTACTS = 10; // maximum number of contact points per body //The list of dbodies ode should be tracking List bodies = new ArrayList(); //This is used to relate DBody's back to their collidables so that when the library detects a collision, the callback can know which collidables are involved. Map bodyPointerMap = new HashMap(); //The list of all collidables the engine is currently tracking List collidableList = new ArrayList(); //callbacks for collision check RayCastCallback rayCastCallback = new RayCastCallback(); //the collision resolution callback CollisionResolutionCallback collisionResolutionCallback; //buffer for collisions DContactBuffer contacts = null; // Callback for any near collisions in the broadphase of the collision check private DNearCallback nearCallback; /** * Constructor */ public CollisionEngine(){ world = OdeHelper.createWorld(); space = OdeHelper.createBHVSpace(Collidable.TYPE_STATIC_BIT); world.setGravity(0,-GRAVITY_MAGNITUDE,0); contactgroup = OdeHelper.createJointGroup(); this.nearCallback = new DNearCallback() { @Override public void call(Object data, DGeom o1, DGeom o2) { nearCallback( data, o1, o2); } }; } /** * Resolves collisions in the engine * @param contactGeom the ode4j contact geometry * @param impactor the instigator of the collision * @param receiver the receiver of the collision * @param normal the normal to the collision surface * @param localPosition the local position of the collision * @param worldPos the world position of the collision * @param magnitude the magnitude of the collision */ public static void resolveCollision(DContactGeom contactGeom, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude){ switch(receiver.getType()){ case Collidable.TYPE_CREATURE: switch(impactor.getType()){ case Collidable.TYPE_TERRAIN: // System.out.println(EntityUtils.getPosition(impactor.getParent()) + " " + EntityUtils.getPosition(receiver.getParent())); // System.out.println(); // System.out.println("Terrain-creature collision: " + normal + " mag:" + magnitude); // if(normal.y > normal.x + normal.z){ // normal.x = 0; // normal.z = 0; // } receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude * 2, Collidable.TYPE_TERRAIN)); break; case Collidable.TYPE_CREATURE: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_CREATURE)); break; case Collidable.TYPE_STRUCTURE: // float realMag = 1f/(float)Math.pow(0.1, magnitude); // System.out.println(normal + " - " + realMag); receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_STRUCTURE)); // System.out.println("Structure-creature collision"); break; case Collidable.TYPE_ITEM: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_ITEM)); break; case Collidable.TYPE_OBJECT: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT)); break; case Collidable.TYPE_FOLIAGE_STATIC: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT)); break; } break; case Collidable.TYPE_ITEM: switch(impactor.getType()){ case Collidable.TYPE_TERRAIN: // System.out.println(EntityUtils.getPosition(impactor.getParent()) + " " + EntityUtils.getPosition(receiver.getParent())); // System.out.println(); // System.out.println("Terrain-item collision: " + normal + " mag:" + magnitude); // if(normal.y > normal.x + normal.z){ // normal.x = 0; // normal.z = 0; // } receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude * 2, Collidable.TYPE_TERRAIN)); break; case Collidable.TYPE_CREATURE: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_CREATURE)); break; case Collidable.TYPE_STRUCTURE: // float realMag = 1f/(float)Math.pow(0.1, magnitude); // System.out.println(normal + " - " + realMag); receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_STRUCTURE)); // System.out.println("Structure-creature collision"); break; case Collidable.TYPE_ITEM: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_ITEM)); break; case Collidable.TYPE_OBJECT: receiver.addImpulse(new Impulse(normal, localPosition, worldPos, magnitude, Collidable.TYPE_OBJECT)); break; } break; } } public void clearCollidableImpulseLists(){ spaceLock.acquireUninterruptibly(); for(Collidable collidable : collidableList){ collidable.clear(); } spaceLock.release(); } public List getCollidables(){ return collidableList; } /** * * @param e the entity that wants to move * @param positionToCheck the position that it wants to move to * @return true if it can occupy that position, false otherwise */ public boolean checkCanOccupyPosition(CollisionWorldData w, Entity e, Vector3d positionToCheck){ boolean rVal = true; // // check world bounds // if( positionToCheck.x < collisionWorldData.getWorldBoundMin().x || positionToCheck.y < collisionWorldData.getWorldBoundMin().y || positionToCheck.z < collisionWorldData.getWorldBoundMin().z || positionToCheck.x > collisionWorldData.getWorldBoundMax().x || positionToCheck.y > collisionWorldData.getWorldBoundMax().y || positionToCheck.z > collisionWorldData.getWorldBoundMax().z ){ return false; } // // // // are we below the terrain? // // // if(w.getElevationAtPoint(positionToCheck) > positionToCheck.y){ // return false; // } return rVal; } /** * Performs the collision and simulation phases for this collision engine * @param time The time to increment the physics simulation by */ public void simulatePhysics(float time){ Globals.profiler.beginCpuSample("physics"); spaceLock.acquireUninterruptibly(); Globals.profiler.beginCpuSample("collide"); OdeHelper.spaceCollide(space, 0, nearCallback); Globals.profiler.endCpuSample(); // space.collide2(space, collisionWorldData, nearCallback); //simulate physics Globals.profiler.beginCpuSample("step physics"); world.quickStep(Timekeeper.ENGINE_STEP_SIZE); Globals.profiler.endCpuSample(); // remove all contact joints contactgroup.empty(); spaceLock.release(); Globals.profiler.endCpuSample(); } /** * Runs a collision cycle on all bodies in the collision engine */ public void collide(){ Globals.profiler.beginCpuSample("physics"); spaceLock.acquireUninterruptibly(); Globals.profiler.beginCpuSample("collide"); OdeHelper.spaceCollide(space, 0, nearCallback); Globals.profiler.endCpuSample(); // remove all contact joints contactgroup.empty(); spaceLock.release(); Globals.profiler.endCpuSample(); } /** * Sets the near callback function for all collision calls. * !!YOU LIKELY WANT TO INSTEAD OVERWRITE THE CollisionResolutionCallback!! * @param callback the callback */ public void setNearCallback(DNearCallback callback){ this.nearCallback = callback; } /** * This is called by dSpaceCollide when two objects in space are potentially colliding. * @param data The data * @param o1 the first collision body * @param o2 the second collision body */ private void nearCallback (Object data, DGeom o1, DGeom o2) { // if (o1->body && o2->body) return; // 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 collisionWorldData.getWorldBoundMax().x){ suggestedPosition.x = collisionWorldData.getWorldBoundMax().x; } if(suggestedPosition.y > collisionWorldData.getWorldBoundMax().y){ suggestedPosition.y = collisionWorldData.getWorldBoundMax().y; } if(suggestedPosition.z > collisionWorldData.getWorldBoundMax().z){ suggestedPosition.z = collisionWorldData.getWorldBoundMax().z; } return suggestedPosition; } /** * Sets the collision world data * @param collisionWorldData The collision world data */ public void setCollisionWorldData(CollisionWorldData collisionWorldData){ this.collisionWorldData = collisionWorldData; } public boolean collisionSphereCheck(Entity hitbox1, HitboxData hitbox1data, Entity hitbox2, HitboxData hitbox2data){ Vector3d position1 = EntityUtils.getPosition(hitbox1); Vector3d position2 = EntityUtils.getPosition(hitbox2); float radius1 = hitbox1data.getRadius(); float radius2 = hitbox2data.getRadius(); double distance = position1.distance(position2); if(distance < radius1 + radius2){ return true; } else { return false; } } /** * Main function to resynchronize entity positions with physics object positions after impulses are applied. */ public void updateDynamicObjectTransforms(){ Globals.profiler.beginCpuSample("updateDynamicObjectTransforms"); spaceLock.acquireUninterruptibly(); if(this.collisionWorldData != null){ for(Collidable collidable : collidableList){ if(collidable.getParentTracksCollidable()){ Entity physicsEntity = collidable.getParent(); DBody rigidBody = PhysicsEntityUtils.getDBody(physicsEntity); Matrix4d inverseTransform = new Matrix4d(); Vector4d rawPos = inverseTransform.transform(new Vector4d(PhysicsUtils.getRigidBodyPosition(rigidBody),1)); Vector3d calculatedPosition = new Vector3d(rawPos.x,rawPos.y,rawPos.z); Vector3d suggestedPosition = this.suggestMovementPosition(collisionWorldData, calculatedPosition); if(calculatedPosition.distance(suggestedPosition) > 0){ collidable.addImpulse(new Impulse(new Vector3d(), new Vector3d(), new Vector3d(), 0, Collidable.TYPE_WORLD_BOUND)); } Quaterniond newRotation = PhysicsUtils.getRigidBodyRotation(rigidBody); EntityUtils.getPosition(physicsEntity).set(suggestedPosition); EntityUtils.getRotation(physicsEntity).set(newRotation); } } } spaceLock.release(); Globals.profiler.endCpuSample(); } public void registerCollisionObject(DBody body, Collidable collidable){ spaceLock.acquireUninterruptibly(); registerPhysicsObject(body); bodyPointerMap.put(body,collidable); collidableList.add(collidable); spaceLock.release(); } /** * Deregisters a collidable from the physics engine * @param collidable The collidable */ public void deregisterCollisionObject(DBody body, Collidable collidable){ spaceLock.acquireUninterruptibly(); bodyPointerMap.remove(body); collidableList.remove(collidable); spaceLock.release(); } public void listBodyPositions(){ for(DBody body : bodies){ LoggerInterface.loggerEngine.INFO("" + body); LoggerInterface.loggerEngine.INFO("" + PhysicsUtils.getRigidBodyPosition(body)); } } /** * Casts a ray into the scene and returns the first entity that the ray collides with. * This will collide with any type of collidable object. * @param start THe start position of the ray * @param direction The direction the ray will travel in * @param length The length of the ray to cast * @return The entity that the ray collides with if successful, null otherwise */ public Entity rayCast(Vector3d start, Vector3d direction, double length){ return rayCast(start,direction,length,null); } /** * Casts a ray into the collision space and returns the first entity that the ray collides with. * The type mask is a list of collidable types that are valid collisions. * For instance, if the typeMask only contains Collidable.TYPE_TERRAIN, only entities with the type terrain will * be returned from the raycast. * @param start The start position of the way * @param length The length to cast the ray out to * @param typeMask The mask of types to collide the ray with * @return The entity that the ray cast collided with. Will be null if no entity was collided with. */ public Entity rayCast(Vector3d start, Vector3d direction, double length, List typeMask){ spaceLock.acquireUninterruptibly(); //create the ray DRay ray = OdeHelper.createRay(space, length); ray.set(start.x, start.y, start.z, direction.x, direction.y, direction.z); //collide RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, typeMask); rayCastCallback.setLength(length); space.collide2(space, data, rayCastCallback); //destroy ray ray.destroy(); spaceLock.release(); return data.collidedEntity; } /** * Ray casts into the scene and gets the position of the closest collision's position in world space. * Will collide with any collidable types including characters and items. * @param start The start position of the ray to cast * @param direction The direction of the ray to cast * @param length The length of the ray to cast * @return The position, in world coordinates, of the closest collision of the way, or null if it did not collide with anything. */ public Vector3d rayCastPosition(Vector3d start, Vector3d direction, double length){ spaceLock.acquireUninterruptibly(); //create the ray DRay ray = OdeHelper.createRay(space, length); ray.set(start.x, start.y, start.z, direction.x, direction.y, direction.z); //collide RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, null); rayCastCallback.setLength(length); space.collide2(ray, data, rayCastCallback); //destroy ray ray.destroy(); spaceLock.release(); return data.collisionPosition; } /** * Ray casts into the scene and gets the position of the closest collision's position in world space. * @param start The start position of the ray to cast * @param direction The direction of the ray to cast * @param length The length of the ray to cast * @param typeMask The mask of types to collide the ray with * @return The position, in world coordinates, of the closest collision of the way, or null if it did not collide with anything. */ public Vector3d rayCastPositionMasked(Vector3d start, Vector3d direction, double length, List typeMask){ spaceLock.acquireUninterruptibly(); //create the ray DRay ray = OdeHelper.createRay(space, length); ray.set(start.x, start.y, start.z, direction.x, direction.y, direction.z); //collide RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, typeMask); rayCastCallback.setLength(length); space.collide2(ray, data, rayCastCallback); //destroy ray ray.destroy(); spaceLock.release(); return data.collisionPosition; } public void registerPhysicsObject(DBody body){ if(!bodies.contains(body)){ bodies.add(body); // OdeHelper.createBody(world); } } /** * Destroys a body and all geometry under the body * @param body The DBody to destroy */ private void deregisterPhysicsObject(DBody body){ spaceLock.acquireUninterruptibly(); if(bodies.contains(body)){ bodies.remove(body); } Iterator geomIterator = body.getGeomIterator(); while(geomIterator.hasNext()){ DGeom geom = geomIterator.next(); space.remove(geom); geom.destroy(); } body.destroy(); spaceLock.release(); } /** * Destroys the physics on an entity * @param e The entity */ public void destroyPhysics(Entity e){ //make uncollidable if(e.containsKey(EntityDataStrings.PHYSICS_COLLISION_BODY)){ DBody rigidBody = (DBody)e.getData(EntityDataStrings.PHYSICS_COLLISION_BODY); deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e)); e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY); deregisterPhysicsObject(rigidBody); } if(ServerPhysicsSyncTree.hasTree(e)){ ServerPhysicsSyncTree.detachTree(e, ServerPhysicsSyncTree.getTree(e)); } } /** * Returns the Ode DSpace * @return The DSpace */ public DSpace getSpace(){ return space; } /** * Gets the DWorld for this collision engine * @return The DWorld */ public DWorld getDWorld(){ return world; } /** * Creates a trimesh from a given set of vertices and indices * @param verts The vertices * @param indices The indices * @return The DTriMesh */ protected DTriMesh createTrimeshGeom(float[] verts, int[] indices, long categoryBits){ spaceLock.acquireUninterruptibly(); DTriMeshData data = OdeHelper.createTriMeshData(); data.build(verts, indices); DTriMesh rVal = OdeHelper.createTriMesh(getSpace(), data); rVal.setCategoryBits(categoryBits); spaceLock.release(); return rVal; } /** * Creates a cube geometry. Dimensions vector control the total length of the x, y, and z dimensions respectively. * @param dimensions The dimensions of the box * @return The DBox */ protected DBox createCubeGeom(Vector3d dimensions, long categoryBits){ spaceLock.acquireUninterruptibly(); DBox boxGeom = OdeHelper.createBox(space, dimensions.x, dimensions.y, dimensions.z); boxGeom.setCategoryBits(categoryBits); spaceLock.release(); return boxGeom; } /** * Creates a cylinder geometry in the physics space * @param dimensions The dimensions of the cylinder. X is the radius, y is the total height. * @return The cylinder geometry */ protected DCylinder createCylinderGeom(double radius, double length, long categoryBits){ spaceLock.acquireUninterruptibly(); DCylinder cylinderGeom = OdeHelper.createCylinder(space, radius, length); cylinderGeom.setCategoryBits(categoryBits); spaceLock.release(); return cylinderGeom; } /** * Creates a sphere geometry in the physics space * @param radius The radius of the sphere * @return The sphere geometry */ protected DSphere createSphereGeom(double radius, long categoryBits){ spaceLock.acquireUninterruptibly(); DSphere sphereGeom = OdeHelper.createSphere(space, radius); sphereGeom.setCategoryBits(categoryBits); spaceLock.release(); return sphereGeom; } /** * Creates a capsule geometry in the physics space * @param radius The radius of the capsule * @param length The length of the capsule * @return The capsule geometry */ protected DCapsule createCapsuleGeom(double radius, double length, long categoryBits){ spaceLock.acquireUninterruptibly(); DCapsule capsuleGeom = OdeHelper.createCapsule(space, radius, length); capsuleGeom.setCategoryBits(categoryBits); spaceLock.release(); return capsuleGeom; } /** * 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 * @return The DBody */ protected DBody createDBody(DGeom ...geom){ spaceLock.acquireUninterruptibly(); DBody body = OdeHelper.createBody(world); if(geom != null){ for(int i = 0; i < geom.length; i++){ if(geom != null){ geom[i].setBody(body); } } } spaceLock.release(); return body; } /** * Creates a DMass and attaches a body to it * @param massValue The amount of mass for the object * @param body The DBody to attach the mass to * @return The DMass */ protected DMass createDMass(double massValue, DBody body){ spaceLock.acquireUninterruptibly(); DMass mass = OdeHelper.createMass(); mass.setMass(massValue); spaceLock.release(); return mass; } /** * Sets the transform on a body * @param body The body * @param position The position * @param rotation The rotation */ protected void setBodyTransform(DBody body, Vector3d position, Quaterniond rotation){ spaceLock.acquireUninterruptibly(); body.setPosition(position.x, position.y, position.z); body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); spaceLock.release(); } /** * Synchronizes the data on a body * @param body The body * @param position The position * @param rotation The rotation * @param linearVel The linear velocity * @param angularVel The angular velocity * @param linearForce The linear force * @param angularForce The angular force */ protected void synchronizeData(DBody body, Vector3d position, Quaterniond rotation, Vector3d linearVel, Vector3d angularVel, Vector3d linearForce, Vector3d angularForce){ spaceLock.acquireUninterruptibly(); body.setPosition(position.x, position.y, position.z); body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); body.setLinearVel(PhysicsUtils.jomlVecToOdeVec(linearVel)); body.setAngularVel(PhysicsUtils.jomlVecToOdeVec(angularVel)); body.setForce(PhysicsUtils.jomlVecToOdeVec(linearForce)); body.setTorque(PhysicsUtils.jomlVecToOdeVec(angularForce)); spaceLock.release(); } /** * Sets the transform on a body * @param body The body * @param position The position * @param rotation The rotation * @param scale The scale */ protected void setBodyTransform(DBody body, CollidableTemplate template, Vector3d position, Quaterniond rotation, Vector3d scale){ spaceLock.acquireUninterruptibly(); body.setPosition(position.x, position.y, position.z); body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); DGeom firstGeom = body.getFirstGeom(); if(firstGeom instanceof DCylinder){ ((DCylinder)firstGeom).setParams(template.getDimension1() * scale.x,template.getDimension2() * scale.y); } else if(firstGeom instanceof DBox){ ((DBox)firstGeom).setLengths(template.getDimension1() * scale.x,template.getDimension2() * scale.y,template.getDimension3() * scale.z); } else if(firstGeom instanceof DCapsule){ ((DCapsule)firstGeom).setParams(template.getDimension1() * scale.x,template.getDimension2() * scale.y); } spaceLock.release(); } /** * Sets the transform of a geometry * @param geom The geometry * @param position The position * @param rotation The rotation */ protected void setGeomTransform(DGeom geom, Vector3d position, Quaterniond rotation){ spaceLock.acquireUninterruptibly(); geom.setOffsetWorldPosition(position.x, position.y, position.z); geom.setOffsetQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); spaceLock.release(); } /** * Corrects the initial axis of eg cylinders or capsules * @param geom the geometry to correct */ protected void setOffsetRotation(DGeom geom){ spaceLock.acquireUninterruptibly(); geom.setOffsetRotation(CollisionBodyCreation.AXIS_CORRECTION_MATRIX); spaceLock.release(); } /** * Gets the position of the body in a thread-safe way * @param body The body to get the position of * @return The position */ protected Vector3d getBodyPosition(DBody body){ Vector3d rVal = null; spaceLock.acquireUninterruptibly(); rVal = PhysicsUtils.odeVecToJomlVec(body.getPosition()); spaceLock.release(); return rVal; } /** * Gets the rotation of the body in a thread-safe way * @param body The body to get the rotation of * @return The rotation */ protected Quaterniond getBodyRotation(DBody body){ Quaterniond rVal = null; spaceLock.acquireUninterruptibly(); rVal = PhysicsUtils.odeQuatToJomlQuat(body.getQuaternion()); spaceLock.release(); return rVal; } /** * Sets a body to be kinematic (infinite mass, not affected by gravity) * @param body The body to set */ protected void setKinematic(DBody body){ spaceLock.acquireUninterruptibly(); body.setKinematic(); spaceLock.release(); } /** * Sets the gravity mode of the body * @param body the body * @param gravityMode the gravity mode */ protected void setGravityMode(DBody body, boolean gravityMode){ spaceLock.acquireUninterruptibly(); body.setGravityMode(gravityMode); spaceLock.release(); } /** * Sets the offset position of the first geometry in the body * @param body The body * @param offsetVector The offset position */ protected void setOffsetPosition(DBody body, Vector3d offsetVector){ spaceLock.acquireUninterruptibly(); body.getGeomIterator().next().setOffsetPosition(offsetVector.x,offsetVector.y,offsetVector.z); spaceLock.release(); } /** * Sets whether the body is angularly static or not * @param body The body * @param angularlyStatic true if angularly static, false otherwise */ protected void setAngularlyStatic(DBody body, boolean angularlyStatic){ spaceLock.acquireUninterruptibly(); if(angularlyStatic){ body.setMaxAngularSpeed(0); } else { body.setMaxAngularSpeed(1000); } spaceLock.release(); } /** * Removes the geometry from the body * @param body the body * @param geom the geometry */ protected void removeGeometryFromBody(DBody body, DGeom geom){ geom.setBody(null); } /** * Destroys a geometry * @param geom The geometry */ protected void destroyGeom(DGeom geom){ geom.destroy(); } /** * Attaches a geom to a body * @param body the body * @param geom the geom */ protected void attachGeomToBody(DBody body, DGeom geom){ geom.setBody(body); } /** * A callback for resolving collisions between two entities */ public interface CollisionResolutionCallback { /** * Resolves a collision between two collidables in the engine * @param contactGeom the ode4j contact geom * @param geom1 The first geometry * @param geom2 The second geometry * @param impactor The collidable initiating the contact * @param receiver The collidable recieving the contact * @param normal The normal of the collision * @param localPosition The local position of the collision * @param worldPos The world position of the collision * @param magnitude The magnitude of the collision */ public void resolve(DContactGeom contactGeom, DGeom geom1, DGeom geom2, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude); } }