package game.util;
import math.Matrix;
import math.Vector;
public class Physics {
private static final double G = 7e-7f; // real world: 6.67384e-11f;
private static double SPHERE_VOLUME_FACTOR = (double) (4f / 3f * Math.PI);
public static final Matrix DEFAULT_TENSOR = Physics
.getSphereInertialTensor(1, 1);
/**
* Translates an object defined by its position, velocity and mass, using a
* translational force and a passing time interval to calculate the
* afterwards position and velocity.
*/
public static void translateObjectEuler(Vector pos, Vector v, double m,
Vector force, double timeDelta) {
// a*t = (F/m)*t
// pos = 0.5at� + vt + pos_0
// v = at + v_0
Vector at = force.clone().scale(timeDelta / m);
pos.add(at.clone().scale(0.5f).add(v).scale(timeDelta));
v.add(at);
}
/**
* Translates an object defined by its position, velocity and mass, using a
* translational force and a passing time interval to calculate the
* afterwards position and velocity.
* This method uses the two-step Leapfrog method instead of the instable Euler method.
*/
public static Vector translateObject(Vector pos, Vector v, double m,
Vector force, double timeDelta, Vector oldAcc) {
Vector acc = force.clone().scale(1 / m);
v.add(oldAcc.add(acc).scale(0.5f * timeDelta));
pos.add(acc.clone().scale(0.5f * timeDelta).add(v).scale(timeDelta));
return oldAcc = acc;
}
/**
* Rotates an object defined by its direction, rotation and momentum of
* inertia, using a rotational momentum and a passing time interval to
* calculate the afterwards direction and rotation.
*/
public static void rotateObject(Matrix dir, Vector rot, double inertia,
Vector momentum, double timeDelta) {
// angular acceleration = M / J (like in F = m*a)
Vector rotAt = momentum.clone().scale(
(inertia < 0.0005f) ? 0 : (timeDelta / inertia));
Vector deltaDir = rotAt.clone().scale(0.5f).add(rot).scale(timeDelta);
dir.set(Matrix.mult(dir, Matrix.getRotationalMatrix(deltaDir)));
rot.add(rotAt);
}
public static void applyTranslationalImpulse(Vector vel, double m, Vector impulse, Vector forceArm) {
if (forceArm == null) {
vel.add(impulse.scale(1 / m));
return;
}
vel.add(Physics.getTranslationalForce(impulse, forceArm).scale(1 / m));
}
public static void applyRotationalImpulse(Vector rot, Matrix tensor, Vector impulse, Vector forceArm) {
if (forceArm != null) {
Vector rotImpulse = Physics.getRotationalMomentum(impulse, forceArm);
double inertia = Physics.getMomentumOfInertia(tensor, rotImpulse);
rot.add(rotImpulse.scale((inertia < 0.0005f) ? 0 : 1 / inertia));
}
}
/**
* Calculates the specific momentum of inertia for a given axis from the
* general momentum of inertia tensor.
*/
public static double getMomentumOfInertia(Matrix tensor, Vector axis) {
// J = n^T * (I * n); (n: normalized rotation axis, I: inertial momentum
// tensor)
double len = axis.getLength();
axis = axis.clone().scale(1 / len);
return (len < 0.005f) ? 0 : Vector.applyMatrix(tensor, axis).dot(axis);
}
/**
* Calculates the part of the given force that is parallel to the given
* lever and thus inducing a translation.
*/
public static Vector getTranslationalForce(Vector totalForce, Vector forceArm) {
return (forceArm.isZero()) ? totalForce
: forceArm.clone().scale(totalForce.dot(forceArm) / forceArm.dot(forceArm));
}
/**
* Calculates the rotational momentum induced by the given force. If the
* force arm has length zero, no rotational momentum is induced.
*/
public static Vector getRotationalMomentum(Vector totalForce, Vector forceArm) {
return (forceArm.isZero()) ? new Vector(0, 0, 0)
: Vector.cross(forceArm, totalForce);
}
/**
* Returns the momentum of inertia tensor corresponding to a homgenuously
* filled sphere of the given radius and mass.
*/
public static Matrix getSphereInertialTensor(double m, double r) {
double momentum = 0.4f * m * r * r;
return new Matrix(new double[][] { { momentum, 0, 0 },
{ 0, momentum, 0 }, { 0, 0, momentum } });
}
public static double getSphereVolume(double r) {
return SPHERE_VOLUME_FACTOR * r * r * r;
}
public static Vector getGForce(double m1, double m2, Vector pos1, Vector pos2) {
Vector dir = pos1.clone().invert().add(pos2);
double r = dir.getLength();
return (r < 0.0005f) ? dir.reset()
: dir.scale(G * m1 * m2 / (r * r * r));
}
/**
* Simulates a collision between two objects. As result of the simulation,
* the given linear and angular velocities (v1, v2, rot1, rot2) of the bodies
* are modified.
* @param e coefficient of restitution (0 for plastic, 1 for elastic)
* @param n mesh surface normal at the point of collision
* @param m1 mass of object 1
* @param m2 mass of object 2
* @param tensor1 tensor of inertia of object 1
* @param tensor2 tensor of inertia of object 2
* @param arm1 point of collision relative to the center of mass of object 1
* @param arm2 point of collision relative to the center of mass of object 2
*/
public static void resolveCollision(double e, Vector n, double m1, double m2, Matrix tensor1,
Matrix tensor2, Vector arm1, Vector arm2, Vector v1, Vector v2, Vector rot1, Vector rot2) {
// see http://www.euclideanspace.com/physics/dynamics/collision/threed/index.htm
Vector dRot1 = Vector.applyMatrix(tensor1.clone().invert(), Vector.cross(n, arm1));
Vector dRot2 = Vector.applyMatrix(tensor2.clone().invert(), Vector.cross(n, arm2));
double impulse = (e + 1) * Vector.getDistance(v1, v2) / (1 / m1 + 1 / m2
+ Vector.cross(dRot1, arm1).dot(n) + Vector.cross(dRot2, arm2).dot(n));
v1.add(n.clone().scale(- impulse / m1));
v2.add(n.clone().scale(impulse / m2));
rot1.add(dRot1.invert());
rot2.add(dRot2.invert());
}
}