Michael Ludwig avatar Michael Ludwig committed c0b3d9b

Improve documentation in CollisionBody.
Add documentation to all of the classes in the dynamics package.
Remove the force and torque properties in RigidBody because they only complicated things unnecessarily.

Comments (0)

Files changed (9)

ferox-physics/src/main/java/com/ferox/physics/collision/CollisionBody.java

 import com.ferox.math.Matrix4;
 import com.ferox.math.entreri.Matrix4Property.DefaultMatrix4;
 import com.lhkbob.entreri.Component;
+import com.lhkbob.entreri.NoAutoVersion;
+import com.lhkbob.entreri.Within;
 import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
 import com.lhkbob.entreri.property.SharedInstance;
 
  * world position and orientation. Additionally, it has pseudo-physical parameters for determining its
  * surface's friction coefficients and collision response (e.g. elastic or inelastic).
  * <p/>
- * CollisionBodies also have configurable "groups" which can restrict the sets of objects that can collide
- * with each other. This is useful for supporting no-clip like features in multiplyer games for members of the
- * same team. Each CollisionBody belongs to some number of integer groups; they also have a bit mask
- * specifying which groups they can collide against. When considering a pair of CollisionBodies for collision,
- * the pair can collide if any of either instance's groups are in the opposite's collision mask. A
- * CollisionBody with no group cannot collide with anything.
+ * A CollisionBody without a RigidBody component is a static object in the scene that can be collided with but
+ * will not react to collisions.
  *
  * @author Michael Ludwig
  */
      *
      * @return This instance
      */
-    public CollisionBody setFriction(double friction);
+    public CollisionBody setFriction(@Within(min = 0.0) double friction);
 
     /**
      * Get the restitution coefficient of this CollisionBody. Restitution is an approximation of the
      * @param restitution The new restitution value
      *
      * @return This instance
-     *
-     * @throws IllegalArgumentException if restitution is less than 0
      */
-    public CollisionBody setRestitution(double restitution);
+    public CollisionBody setRestitution(@Within(min = 0.0) double restitution);
 
     /**
      * Return the matrix instance holding this Collidable's world transform.
 
     /**
      * Set the world transform of this collision body. A task is responsible for computing the world bounds
-     * based of this component's shape's local bounds and its transform.
+     * based of this component's shape's local bounds and its transform. Note that this method does not update
+     * the version of the component, because the world bounds is meant to be a computed property from the
+     * local bounds and transform, both of which already increment the version.
      *
      * @param bounds The new world bounds
      *
      * @return This component
      */
+    @NoAutoVersion
     public CollisionBody setWorldBounds(@Const AxisAlignedBox bounds);
 }

ferox-physics/src/main/java/com/ferox/physics/dynamics/ContactManifoldPool.java

 
 import java.util.*;
 
+/**
+ * ContactManifoldPool is a packed data structure that records the approximate contact manifolds of all
+ * collisions and certain near collisions in a simulation. It is responsible for converting these manifolds
+ * into linear constraints to resolve the penetration and friction forces and submit them to the constraint
+ * solver.
+ * <p/>
+ * Credit is due to the Bullet physics engine for the linear algebra involved in computing the requisite
+ * contact and friction constraints.
+ *
+ * @author Michael Ludwig
+ */
 public class ContactManifoldPool {
     private static final int MANIFOLD_POINT_SIZE = 4;
     private static final int RESTING_CONTACT_THRESHOLD = 2;
 
     private final Matrix4 transform = new Matrix4();
 
+    /**
+     * Create a new pool that uses a default processing threshold of {@code 0.1} and a breaking threshold of
+     * {@code 0.0343}.
+     */
     public ContactManifoldPool() {
-        // increasing breaking threshold seems to create more stable box drops,
-        // so i should investigate why .0343 was the original default
         this(.1, .0343);
     }
 
+    /**
+     * Create a new pool that uses the given processing and breaking thresholds.
+     *
+     * @param contactProcessingThreshold The contact processing threshold passed to {@link
+     *                                   #setContactProcessingThreshold(double)}
+     * @param contactBreakingThreshold   The breaking threshold passed to {@link #setContactBreakingThreshold(double)}
+     */
     public ContactManifoldPool(double contactProcessingThreshold, double contactBreakingThreshold) {
         manifolds = new HashMap<>();
         query = new CollisionPair();
         setContactBreakingThreshold(contactBreakingThreshold);
     }
 
+    /**
+     * Set the EntitySystem used by this pool. This should be invoked by the collision task owning the pool
+     * once it has determined the EntitySystem the task is processing.
+     *
+     * @param system The entity system
+     */
     public void setEntitySystem(EntitySystem system) {
         entitySystem = system;
     }
 
+    /**
+     * @return The current entity system that collisions are a part of
+     */
     public EntitySystem getEntitySystem() {
         return entitySystem;
     }
 
+    /**
+     * Set the threshold for which near collisions are remembered in the manifold. This can be useful to help
+     * dampen jostling collisions that slightly bounce repeatedly over a few contact points. When a manifold
+     * contains no points within this threshold it is discarded.
+     *
+     * @param threshold The new threshold
+     *
+     * @throws IllegalArgumentException if threshold is negative
+     */
     public void setContactProcessingThreshold(double threshold) {
         if (threshold <= 0.0) {
             throw new IllegalArgumentException("Threshold must be positive, not " + threshold);
         contactProcessingThreshold = threshold;
     }
 
+    /**
+     * @return Get the contact processing threshold
+     */
     public double getContactProcessingThreshold() {
         return contactProcessingThreshold;
     }
 
+    /**
+     * Set the threshold that prevents a manifold from creating collision and friction constraints. If the
+     * manifold has no collision pairs closer than this threshold, it will not produce constraints. This
+     * threshold should be less than the processing threshold
+     *
+     * @param threshold The new threshold
+     *
+     * @throws IllegalArgumentException if threshold is negative
+     */
     public void setContactBreakingThreshold(double threshold) {
         if (threshold <= 0.0) {
             throw new IllegalArgumentException("Threshold must be positive, not " + threshold);
         contactBreakingThreshold = threshold;
     }
 
+    /**
+     * @return Get the contact breaking threshold
+     */
     public double getContactBreakingThreshold() {
         return contactBreakingThreshold;
     }
 
+    /**
+     * Compute the "warmstart" impulses to use in the next frame of simulation based on the solved results
+     * from the contact and friction pools. These pools should have been previously passed to {@link
+     * #generateConstraints(double, LinearConstraintPool, LinearConstraintPool)}.
+     *
+     * @param contactPool  The pool containing constraints for contact
+     * @param frictionPool The pool containing constraints for friction simulation
+     */
     public void computeWarmstartImpulses(LinearConstraintPool contactPool,
                                          LinearConstraintPool frictionPool) {
         for (int manifold = 0; manifold < maxAliveContact; manifold++) {
         }
     }
 
+    /**
+     * Compute and submit all contact and friction constraints to the given constraint pools.
+     *
+     * @param dt           The time delta in seconds for the simulation step
+     * @param contactPool  The pool that will hold the contact constraints
+     * @param frictionPool The pool that will hold the friction constraints
+     */
     public void generateConstraints(double dt, LinearConstraintPool contactPool,
                                     LinearConstraintPool frictionPool) {
         CollisionBody bodyA, bodyB;
         }
     }
 
+    /**
+     * Record a contact between the two bodies. The order of {@code objA} and {@code objB} must be consistent
+     * with what produced the closest pair, but it does not have to be same ordering if the pair (considered
+     * as a set) was already recorded in the pool.
+     *
+     * @param objA The first object in a contact situation
+     * @param objB The second object involved in the contact
+     * @param pair The contact data
+     *
+     * @throws IllegalArgumentException if objA or objB are flyweight instances
+     */
     public void addContact(CollisionBody objA, CollisionBody objB, ClosestPair pair) {
         int manifold = getManifold(objA, objB);
         addManifoldPoint(manifold, objA, objB, pair);
         return true;
     }
 
+    /**
+     * Set the internal capacity of this pool. Normally this is managed automatically, but you can resize it
+     * up or down as necessary. Shrinking the pool may discard collision manifolds that are still valid. This
+     * does not prevent the collision from being resolved the next frame, but it may hurt simulation
+     * stability.
+     *
+     * @param newCount The new number of manifolds to hold
+     */
     public void setCapacity(int newCount) {
         int newManifoldCount = newCount * MANIFOLD_POINT_SIZE;
 

ferox-physics/src/main/java/com/ferox/physics/dynamics/ExplicitEulerIntegrator.java

 import com.ferox.math.Quat4;
 import com.ferox.math.Vector3;
 
+/**
+ * ExplicitEulerIntegrator is an integrator implemented using simple Euler integration for the linear
+ * acceleration, velocity, and angular acceleration. Damping is applied to the angular velocity to improve
+ * system stability. The angular velocity integration is ported from the Bullet's integrator.
+ *
+ * @author Michael Ludwig
+ */
 public class ExplicitEulerIntegrator implements Integrator {
     private static final double MAX_ANGULAR_VELOCITY = Math.PI / 2.0;
     private static final double ANGULAR_MOTION_THRESHOLD = Math.PI / 4.0;
         // clamp angular velocity
         Vector3 axis = tempv;
 
-        double veclength = v.length();
-        double angvel = veclength;
+        double angvel = v.length();
         if (angvel * dt > MAX_ANGULAR_VELOCITY) {
             // set axis to be linear velocity but with magnitude = MAX / dt
             axis.scale(v, MAX_ANGULAR_VELOCITY / (angvel * dt));

ferox-physics/src/main/java/com/ferox/physics/dynamics/Gravity.java

 import com.lhkbob.entreri.Component;
 import com.lhkbob.entreri.property.SharedInstance;
 
+/**
+ * Gravity is a component that can be added to a rigid body entity to give it a unique gravity acceleration
+ * that is different from the system's default (which is by default configured through the {@link
+ * com.ferox.physics.task.IntegrationTask}.
+ * <p/>
+ * The gravity components records the acceleration so that it is not dependent on the mass of the entity.
+ *
+ * @author Michael Ludwig
+ */
 public interface Gravity extends Component {
+    /**
+     * @return The current custom gravity acceleration vector
+     */
     @Const
     @SharedInstance
     @DefaultVector3(x = 0.0, y = -9.8, z = 0.0)
     public Vector3 getGravity();
 
+    /**
+     * Set the custom gravity acceleration vector.
+     *
+     * @param gravity The new gravity vector
+     *
+     * @return This component
+     */
     public Gravity setGravity(@Const Vector3 gravity);
 }

ferox-physics/src/main/java/com/ferox/physics/dynamics/Integrator.java

 import com.ferox.math.Matrix3;
 import com.ferox.math.Vector3;
 
+/**
+ * Integrator implementations are used to perform the numerical computing necessary to integrate accelerations
+ * into velocities, and velocities into positions over a time delta. At the moment, only explicit euler
+ * integration is implemented in {@link ExplicitEulerIntegrator}.
+ *
+ * @author Michael Ludwig
+ */
 public interface Integrator {
+    /**
+     * Integrate the given acceleration vector {@code a} over the time delta {@code dt}, measured in seconds.
+     * The computed velocity should be added to {@code velocity}, which can be assumed not to be null.
+     *
+     * @param a        The linear acceleration
+     * @param dt       The time delta in seconds
+     * @param velocity The velocity output vector
+     *
+     * @throws NullPointerException if a or velocity are null
+     */
     public void integrateLinearAcceleration(@Const Vector3 a, double dt, Vector3 velocity);
 
+    /**
+     * Integrate the given angular acceleration vector {@code a} over the time delta {@code dt}, measured in
+     * seconds. The computed velocity should be added to {@code velocity}, which can be assumed not to be
+     * null.
+     *
+     * @param a               The angular acceleration
+     * @param dt              The time delta in seconds
+     * @param angularVelocity The angular velocity output vector
+     *
+     * @throws NullPointerException if a or angularVelocity are null
+     */
     public void integrateAngularAcceleration(@Const Vector3 a, double dt, Vector3 angularVelocity);
 
+    /**
+     * Integrate the given linear velocity vector {@code v} over the time delta {@code dt}, measured in
+     * seconds. The computed delta position should be added to the 'current' position stored in {@code
+     * position}, which can be assumed not to be null.
+     *
+     * @param v        The linear velocity
+     * @param dt       The time delta in seconds
+     * @param position The position output vector
+     *
+     * @throws NullPointerException if v or position are null
+     */
     public void integrateLinearVelocity(@Const Vector3 v, double dt, Vector3 position);
 
+    /**
+     * Integrate the given angular velocity vector {@code a} over the time delta {@code dt}, measured in
+     * seconds. The computed delta orientation should be accumulated into the 'current' orientation stored in
+     * {@code orientation}, which can be assumed not to be null.
+     *
+     * @param v           The angular velocity
+     * @param dt          The time delta in seconds
+     * @param orientation The output orientation matrix
+     *
+     * @throws NullPointerException if a or velocity are null
+     */
     public void integrateAngularVelocity(@Const Vector3 v, double dt, Matrix3 orientation);
 }

ferox-physics/src/main/java/com/ferox/physics/dynamics/LinearConstraintPool.java

 
 import java.util.Arrays;
 
+/**
+ * LinearConstraintPool is a packed data structure for storing many linear constraints in a representation
+ * usable by {@link LinearConstraintSolver}. Its capacity grows automatically as more constraints are added.
+ *
+ * @author Michael Ludwig
+ */
 public class LinearConstraintPool {
     private int count; // current number of valid constraints in the pool
 
     private final Vector3 angularA = new Vector3();
     private final Vector3 angularB = new Vector3();
 
+    /**
+     * Create a new constraint pool that is optionally linked with {@code linkedPool}. If the linked pool is
+     * not null, then this new pool can have dynamic force limits based on the forces in the linked pool.
+     *
+     * @param linkedPool The optional linked pool
+     */
     public LinearConstraintPool(LinearConstraintPool linkedPool) {
         count = 0;
-        dynamicPool = (linkedPool == null ? this : linkedPool);
+        dynamicPool = linkedPool;
         setCapacity(10);
     }
 
+    /**
+     * Manually set the capacity to {@code newCapacity}. If the capacity is smaller than the current capacity,
+     * it will be truncated and the extra constraints discarded.
+     *
+     * @param newCapacity The new capacity
+     */
     public void setCapacity(int newCapacity) {
         constraintForceMixes = Arrays.copyOf(constraintForceMixes, newCapacity);
         solutions = Arrays.copyOf(solutions, newCapacity);
         count = Math.min(newCapacity, count);
     }
 
+    /**
+     * Quickly empty the pool of all currently filled constraints. This resets the pointer within the internal
+     * arrays so old constraints will be overwritten.
+     */
     public void clear() {
         count = 0;
     }
 
-    public double getDynamicScaleFactor(int i) {
-        return dynamicScaleFactors[i];
-    }
-
-    public int getDynamicLimitIndex(int i) {
-        return dynamicLimits[i];
-    }
-
-    public int addConstraint(RigidBody bodyA, RigidBody bodyB, float taX, float taY, float taZ, float tbX,
-                             float tbY, float tbZ, float nX, float nY, float nZ, float laX, float laY,
-                             float laZ, float lbX, float lbY, float lbZ, float aaX, float aaY, float aaZ,
-                             float abX, float abY, float abZ) {
-        int i = count++;
-        int veci = i * 3;
-        if (i >= bodyAs.length) {
-            // increase capacity
-            setCapacity((i + 1) * 2);
-        }
-
-        directions[veci] = nX;
-        directions[veci + 1] = nY;
-        directions[veci + 2] = nZ;
-        torqueAs[veci] = taX;
-        torqueAs[veci + 1] = taY;
-        torqueAs[veci + 2] = taZ;
-        torqueBs[veci] = tbX;
-        torqueBs[veci + 1] = tbY;
-        torqueBs[veci + 2] = tbZ;
-
-        if (bodyA != null) {
-            linearDirAs[veci] = laX;
-            linearDirAs[veci + 1] = laY;
-            linearDirAs[veci + 2] = laZ;
-            angleDirAs[veci] = aaX;
-            angleDirAs[veci + 1] = aaY;
-            angleDirAs[veci + 2] = aaZ;
-            bodyAs[i] = bodyA.getIndex();
-        } else {
-            bodyAs[i] = -1;
-        }
-
-        if (bodyB != null) {
-            linearDirBs[veci] = lbX;
-            linearDirBs[veci + 1] = lbY;
-            linearDirBs[veci + 2] = lbZ;
-            angleDirBs[veci] = abX;
-            angleDirBs[veci + 1] = abY;
-            angleDirBs[veci + 2] = abZ;
-            bodyBs[i] = bodyB.getIndex();
-        } else {
-            bodyBs[i] = -1;
-        }
-
-        return i;
-    }
-
+    /**
+     * Add a new constraint to the pool between the two bodies, one of which may be null. The constraint will
+     * be along the given linear direction and torque vectors. The solution parameters of the constraint are
+     * reset. The index of the constraint for later configuration is returned.
+     *
+     * @param bodyA     The first body
+     * @param bodyB     The second body
+     * @param direction The linear direction of the constraint
+     * @param torqueA   The torque vector for body A
+     * @param torqueB   The torque vector for body B
+     *
+     * @return The index of the new constraint
+     */
     public int addConstraint(RigidBody bodyA, RigidBody bodyB, @Const Vector3 direction,
                              @Const Vector3 torqueA, @Const Vector3 torqueB) {
         int i = count++;
         return i;
     }
 
+    /**
+     * @param i       The index of the constraint to access
+     * @param impulse The impulse scalar
+     *
+     * @return The linear impulse to apply to A. This vector is reused if this method is invoked so the result
+     *         must be copied out
+     */
     @Const
     public Vector3 getLinearImpulseA(int i, double impulse) {
         return linearA.set(linearDirAs, i * 3).scale(impulse);
     }
 
+    /**
+     * @param i       The index of the constraint to access
+     * @param impulse The impulse scalar
+     *
+     * @return The linear impulse to apply to B. This vector is reused if this method is invoked so the result
+     *         must be copied out
+     */
     @Const
     public Vector3 getLinearImpulseB(int i, double impulse) {
         return linearB.set(linearDirBs, i * 3).scale(impulse);
     }
 
+    /**
+     * @param i       The index of the constraint to access
+     * @param impulse The impulse scalar
+     *
+     * @return The angular impulse to apply to A. This vector is reused if this method is invoked so the
+     *         result must be copied out
+     */
     @Const
     public Vector3 getAngularImpulseA(int i, double impulse) {
         return angularA.set(angleDirAs, i * 3).scale(impulse);
     }
 
+    /**
+     * @param i       The index of the constraint to access
+     * @param impulse The impulse scalar
+     *
+     * @return The angular impulse to apply to A. This vector is reused if this method is invoked so the
+     *         result must be copied out
+     */
     @Const
     public Vector3 getAngularImpulseB(int i, double impulse) {
         return angularB.set(angleDirBs, i * 3).scale(impulse);
     }
 
+    /**
+     * Record the applied impulse for the given constraint
+     *
+     * @param i       The constraint to access
+     * @param impulse The total applied impulse for the constraint
+     */
     public void setAppliedImpulse(int i, double impulse) {
         appliedImpulses[i] = impulse;
     }
 
+    /**
+     * Record the warmstart impulse to apply to this constraint
+     *
+     * @param i       The constraint to modify
+     * @param impulse The warmstart impulse
+     */
     public void setWarmstartImpulse(int i, double impulse) {
         warmstartImpulses[i] = impulse;
     }
 
+    /**
+     * Set the solution parameters of the constraint to inform the constraint solving process.
+     *
+     * @param i        The constraint to modify
+     * @param solution The desired solution
+     * @param cfm      The constraint force mix
+     * @param jacobian The inverse of the Jacobian of the constraint
+     */
     public void setSolution(int i, double solution, double cfm, double jacobian) {
         solutions[i] = solution;
         constraintForceMixes[i] = cfm;
         jacobianDiagInverses[i] = jacobian;
     }
 
+    /**
+     * Set the upper and lower limits of the applied impulse on this constraint.
+     *
+     * @param i     The constraint to modify
+     * @param lower The lower bound on the applied impulse
+     * @param upper The upper bound on the applied impulse
+     */
     public void setStaticLimits(int i, double lower, double upper) {
         if (lower > upper) {
             throw new IllegalArgumentException("Lower limit (" + lower + ") must be less than upper limit (" +
         dynamicLimits[i] = -1; // clear dynamic limit
     }
 
+    /**
+     * Set the upper and lower limits of the applied impulse to be proportional to the {@code
+     * linkedConstraint} in the configured linked constraint pool.
+     *
+     * @param i                The constraint to modify
+     * @param linkedConstraint The linked constraint
+     * @param scale            The scaling factor applied to the linked applied impulse
+     */
     public void setDynamicLimits(int i, int linkedConstraint, double scale) {
         if (dynamicPool == null) {
             throw new IllegalStateException("Pool does not have a linked pool");
         dynamicScaleFactors[i] = scale;
     }
 
-    public
+    /**
+     * @param i The constraint index
+     *
+     * @return The constraint direction
+     */
     @Const
-    Vector3 getConstraintDirection(int i) {
+    public Vector3 getConstraintDirection(int i) {
         return direction.set(directions, i * 3);
     }
 
-    public
+    /**
+     * @param i The constraint index
+     *
+     * @return The torque vector for body A
+     */
     @Const
-    Vector3 getTorqueA(int i) {
+    public Vector3 getTorqueA(int i) {
         return torqueA.set(torqueAs, i * 3);
     }
 
-    public
+    /**
+     * @param i The constraint index
+     *
+     * @return The torque vector for body B
+     */
     @Const
-    Vector3 getTorqueB(int i) {
+    public Vector3 getTorqueB(int i) {
         return torqueB.set(torqueBs, i * 3);
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The Jacobian inverse of the constraint
+     */
     public double getJacobianDiagonalInverse(int i) {
         return jacobianDiagInverses[i];
     }
 
+    /**
+     * Get the lower limit on the applied impulse for this constraint. This may be a static lower limit or
+     * dynamically computed from a linked constraint.
+     *
+     * @param i The constraint index
+     *
+     * @return The lower impulse limit
+     */
     public double getLowerImpulseLimit(int i) {
         int dynamic = dynamicLimits[i];
         if (dynamic >= 0) {
         }
     }
 
+    /**
+     * Get the upper limit on the applied impulse for this constraint. This may be a static upper limit or
+     * dynamically computed from a linked constraint.
+     *
+     * @param i The constraint index
+     *
+     * @return The upper impulse limit
+     */
     public double getUpperImpulseLimit(int i) {
         int dynamic = dynamicLimits[i];
         if (dynamic >= 0) {
         }
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The desired constraint solution
+     */
     public double getSolution(int i) {
         return solutions[i];
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The constraint force mix for the constraint
+     */
     public double getConstraintForceMix(int i) {
         return constraintForceMixes[i];
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The warmstart impulse for the constraint
+     */
     public double getWarmstartImpulse(int i) {
         return warmstartImpulses[i];
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The applied impulse for the constraint
+     */
     public double getAppliedImpulse(int i) {
         return appliedImpulses[i];
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The component index of the first RigidBody, or -1 if the constraint didn't have a rigid body at
+     *         this attachment point
+     */
     public int getBodyAIndex(int i) {
         return bodyAs[i];
     }
 
+    /**
+     * @param i The constraint index
+     *
+     * @return The component index of the second RigidBody, or -1 if the constraint didn't have a rigid body
+     *         at this attachment point
+     */
     public int getBodyBIndex(int i) {
         return bodyBs[i];
     }
 
+    /**
+     * @return Get the number of constraints in the pool
+     */
     public int getConstraintCount() {
         return count;
     }

ferox-physics/src/main/java/com/ferox/physics/dynamics/LinearConstraintSolver.java

 
 import java.util.Random;
 
+/**
+ * LinearConstraintSolver is an iteration based constraint solver that estimates the impulses required to
+ * solve the global solution. It does not guarantee perfect correctness, but as the iteration count is
+ * increased its accuracy is improved. This is largely based on the sequential impulse constraint solver from
+ * Bullet.
+ *
+ * @author Michael Ludwig
+ */
 public class LinearConstraintSolver {
     private final Random shuffler;
 
     private final Vector3 linear = new Vector3();
     private final Vector3 angular = new Vector3();
 
+    /**
+     * Create a new LinearConstraintSolver that shuffles the constraints every iteration, for ten iterations.
+     */
     public LinearConstraintSolver() {
         shuffler = new Random();
         setShuffleConstraints(true);
         setIterationCount(10);
     }
 
+    /**
+     * Set the decorated property used to hold the delta linear impulses that are accumulated for each
+     * RigidBody. The vector property must be a decorated property applied to {@link RigidBody} on the
+     * EntitySystem that this solver processes. This can be done with {@link
+     * com.lhkbob.entreri.EntitySystem#decorate(Class, com.lhkbob.entreri.property.PropertyFactory)}.
+     *
+     * @param prop The linear impulse property
+     */
     public void setDeltaLinearImpulseProperty(Vector3Property prop) {
         deltaLinearImpulse = prop;
     }
 
+    /**
+     * Set the decorated property used to hold the delta angular impulses that are accumulated for each
+     * RigidBody. The vector property must be a decorated property applied to {@link RigidBody} on the
+     * EntitySystem that this solver processes. This can be done with {@link
+     * com.lhkbob.entreri.EntitySystem#decorate(Class, com.lhkbob.entreri.property.PropertyFactory)}.
+     *
+     * @param prop The angular impulse property
+     */
     public void setDeltaAngularImpulseProperty(Vector3Property prop) {
         deltaAngularImpulse = prop;
     }
 
+    /**
+     * Set whether or not constraints should be shuffled. If this is set to false, then {@link
+     * #getShuffleEveryIteration()} is ignored. If this is set to true, the order in which constraints are
+     * iterated over is randomized, which can improve simulation stability. However, this comes at the expense
+     * of performance since cache locality over the constraints is broken.
+     *
+     * @param shuffle Whether or not to shuffle the constraints during solving
+     */
     public void setShuffleConstraints(boolean shuffle) {
         shuffleConstraints = shuffle;
     }
 
+    /**
+     * Set whether or not constraints should be shuffled every iteration, or only once at the start. This is
+     * ignored if general constraint shuffling is disabled. When disabled, the primary performance improvement
+     * is that calls to {@link java.util.Random#nextInt()} are reduced. Cache locality is still impacted by
+     * the initial random ordering.
+     *
+     * @param shuffle Whether or not to shuffle each iteration or only once
+     */
     public void setShuffleEveryIteration(boolean shuffle) {
         shuffleEachIteration = shuffle;
     }
 
+    /**
+     * Set the number of iterations used to estimate a solution to all constraints in the supplied pools.
+     *
+     * @param numIters The number of iterations
+     *
+     * @throws IllegalArgumentException if numIters is less than 1
+     */
     public void setIterationCount(int numIters) {
         if (numIters <= 0) {
             throw new IllegalArgumentException("Iteration count must be at least 1, not " + numIters);
         numIterations = numIters;
     }
 
+    /**
+     * @return True if constraints are shuffled
+     */
     public boolean getShuffleConstraints() {
         return shuffleConstraints;
     }
 
+    /**
+     * @return True if constraints are shuffled every iteration, ignored if {@link #getShuffleConstraints()}
+     *         is false
+     */
     public boolean getShuffleEveryIteration() {
         return shuffleEachIteration;
     }
 
+    /**
+     * @return The number of iterations
+     */
     public int getIterationCount() {
         return numIterations;
     }
 
+    /**
+     * Solve the constraints provided in the list of pools. Within each iteration, the groups are solved in
+     * the order provided.
+     *
+     * @param groups The linear constraints to solve
+     */
     public void solve(LinearConstraintPool... groups) {
         // handle warmstarting
         for (int i = 0; i < groups.length; i++) {

ferox-physics/src/main/java/com/ferox/physics/dynamics/RigidBody.java

 import com.ferox.physics.collision.CollisionBody;
 import com.lhkbob.entreri.Component;
 import com.lhkbob.entreri.Requires;
+import com.lhkbob.entreri.Within;
 import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
 import com.lhkbob.entreri.property.SharedInstance;
 
+/**
+ * <p/>
+ * RigidBody represents an instance of an object in a physics simulation that can move, collide, and be
+ * collided with. The RigidBody component is paired with the {@link CollisionBody} and controls the motion
+ * behavior of the entity, such as its velocity, mass, etc.
+ *
+ * @author Michael Ludwig
+ */
 @Requires(CollisionBody.class)
 public interface RigidBody extends Component {
+    /**
+     * @return Get the inverse of the body's inertia tensor matrix
+     */
     @Const
     @SharedInstance
     public Matrix3 getInertiaTensorInverse();
 
+    /**
+     * Set the inverse of the body's inertia tensor matrix. This must be computed by a task, which by default
+     * is handled in the {@link com.ferox.physics.task.IntegrationTask}.
+     *
+     * @param tensorInverse The inverse tensor
+     *
+     * @return This component
+     */
     public RigidBody setInertiaTensorInverse(@Const Matrix3 tensorInverse);
 
-    public RigidBody setForce(@Const Vector3 f);
-
-    public RigidBody setTorque(@Const Vector3 t);
-
+    /**
+     * Set the linear velocity of this rigid body's center of mass.
+     *
+     * @param vel The new linear velocity
+     *
+     * @return This component
+     */
     public RigidBody setVelocity(@Const Vector3 vel);
 
+    /**
+     * Set the angular velocity of this rigid body. The angular velocity is stored as the axis of rotation and
+     * its magnitude is the rate of rotation.
+     *
+     * @param angVel The new angular velocity
+     *
+     * @return This component
+     */
     public RigidBody setAngularVelocity(@Const Vector3 angVel);
 
-    public RigidBody setMass(double mass);
+    /**
+     * Set the mass of this component. The mass must be positive, and to help stability of a simulation, it
+     * must be above 0.0001 'units', which are whatever units you wish the system to be expressed in.
+     *
+     * @param mass The new mass
+     *
+     * @return This component
+     */
+    public RigidBody setMass(@Within(min = 0.0001) double mass);
 
+    /**
+     * @return The mass of the rigid body
+     */
     @DefaultDouble(1.0)
     public double getMass();
 
+    /**
+     * @return The current linear velocity
+     */
     @Const
     @SharedInstance
     public Vector3 getVelocity();
 
+    /**
+     * @return The current angular velocity
+     */
     @Const
     @SharedInstance
     public Vector3 getAngularVelocity();
 
-    @Const
-    @SharedInstance
-    public Vector3 getForce();
-
-    @Const
-    @SharedInstance
-    public Vector3 getTorque();
-
+    /**
+     * Utils contains static methods to operate on RigidBodies in a useful manner, but that can't be placed
+     * directly in RigidBody because it is an interface.
+     */
     public static final class Utils {
         private Utils() {
         }
 
-        public static void addForce(RigidBody body, @Const Vector3 force, @Const Vector3 relPos) {
-            Vector3 totalForce = body.getForce();
-            totalForce.add(force);
-            body.setForce(totalForce);
-
-            if (relPos != null) {
-                Vector3 totalTorque = body.getTorque();
-                // use the body's total force vector as a temporary store, this is safe because we
-                // never use it again and getForce() always refills it
-                totalTorque.add(totalForce.cross(relPos, force));
-                body.setTorque(totalTorque);
-            }
-        }
-
+        /**
+         * Apply the given impulse to the rigid body. This will update both the velocity and angular velocity
+         * based on the mass of the object. An impulse is a force integrated over a time step. If {@code
+         * relPos} is null, it is assumed the impulse is applied to the center of mass.
+         *
+         * @param body    The rigid body to modify
+         * @param impulse The impulse applied to the body
+         * @param relPos  The relative position the impulse affects, or null for the center of mass
+         */
         public static void addImpulse(RigidBody body, @Const Vector3 impulse, @Const Vector3 relPos) {
             Vector3 velocity = body.getVelocity();
             velocity.addScaled(1.0 / body.getMass(), impulse);
                 body.setAngularVelocity(angular);
             }
         }
-
-        public static void clearForces(RigidBody body) {
-            Vector3 force = body.getForce();
-            force.set(0, 0, 0);
-            body.setForce(force);
-
-            Vector3 torque = body.getTorque();
-            torque.set(0, 0, 0);
-            body.setTorque(torque);
-        }
     }
 }

ferox-physics/src/main/java/com/ferox/physics/task/IntegrationTask.java

                 tensor.mulDiagonal(rotation, inertia).mulTransposeRight(rotation);
                 rigidBody.setInertiaTensorInverse(tensor);
 
-                // 4. Add gravity force
+                // 4. Compute and apply gravity force
                 if (gravity.isAlive()) {
                     force.scale(gravity.getGravity(), rigidBody.getMass());
                 } else {
                     force.scale(defaultGravity, rigidBody.getMass());
                 }
-                RigidBody.Utils.addForce(rigidBody, force, null);
 
-                // 5. Integrate and apply forces to the body's velocity
                 Vector3 lv = rigidBody.getVelocity();
-                integrator.integrateLinearAcceleration(
-                        force.scale(rigidBody.getForce(), 1.0 / rigidBody.getMass()), dt, lv);
+                integrator.integrateLinearAcceleration(force.scale(force, 1.0 / rigidBody.getMass()), dt, lv);
                 rigidBody.setVelocity(lv);
-
-                Vector3 la = rigidBody.getAngularVelocity();
-                integrator.integrateAngularAcceleration(force.mul(tensor, rigidBody.getTorque()), dt, la);
-                rigidBody.setAngularVelocity(la);
-
-                // 6. Reset forces
-                RigidBody.Utils.clearForces(rigidBody);
             }
         }
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.