Commits

Michael Ludwig committed b507c7f

Add documentation to collision algorithms, implemented collision shapes, etc.

Comments (0)

Files changed (14)

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

  */
 package com.ferox.physics.collision;
 
+/**
+ * CollisionPair is a pair of CollisionBodies involved in a potential collision.
+ *
+ * @author Michael Ludwig
+ */
 public class CollisionPair {
     private CollisionBody a;
     private CollisionBody b;
 
+    /**
+     * Create an empty pair that can have its contents updated later (useful for querying collections of
+     * pairs).
+     */
     public CollisionPair() {
     }
 
+    /**
+     * Create a new pair over the given two bodies.
+     *
+     * @param a The first body
+     * @param b The second body
+     *
+     * @see #set(CollisionBody, CollisionBody)
+     */
     public CollisionPair(CollisionBody a, CollisionBody b) {
         set(a, b);
     }
 
+    /**
+     * Set the two bodies on this pair. This should not be called on pairs that are stored inside collections
+     * that depend on {@link #equals(Object)} and {@link #hashCode()}.
+     *
+     * @param a The first body
+     * @param b The second body
+     *
+     * @throws NullPointerException     if a or b are null
+     * @throws IllegalArgumentException if a or b are flyweight instances
+     */
     public void set(CollisionBody a, CollisionBody b) {
         if (a.isFlyweight() || b.isFlyweight()) {
             throw new IllegalArgumentException("Bodies should not be flyweight instances");
         this.b = b;
     }
 
+    /**
+     * @return The first body in the pair
+     */
     public CollisionBody getBodyA() {
         return a;
     }
 
+    /**
+     * @return The second body in the pair
+     */
     public CollisionBody getBodyB() {
         return b;
     }
         // sum of hashes -> follow Set hashcode since a pair is just a 2 element set
         return a.hashCode() + b.hashCode();
     }
-}
+}

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

 import com.ferox.physics.collision.algorithm.JitteringCollisionAlgorithm;
 import com.ferox.physics.collision.algorithm.SphereSphereCollisionAlgorithm;
 import com.ferox.physics.collision.algorithm.SwappingCollisionAlgorithm;
-import com.ferox.physics.collision.shape.ConvexShape;
 
 import java.util.ArrayList;
 import java.util.HashMap;
      * a {@link SphereSphereCollisionAlgorithm} registered.
      */
     public DefaultCollisionAlgorithmProvider() {
-        algorithms = new ArrayList<CollisionAlgorithm<?, ?>>();
+        algorithms = new ArrayList<>();
 
-        algorithmCache = new HashMap<TypePair, CollisionAlgorithm<?, ?>>();
+        algorithmCache = new HashMap<>();
         lookup = new TypePair(null, null);
 
         // wrap the GJK/EPA algorithm with a jittering algorithm to help overcome
         // numerical instabilities
-        register(new JitteringCollisionAlgorithm<ConvexShape, ConvexShape>(new GjkEpaCollisionAlgorithm()));
+        register(new JitteringCollisionAlgorithm<>(new GjkEpaCollisionAlgorithm()));
         register(new SphereSphereCollisionAlgorithm());
     }
 
         if (swapped != null) {
             // return a wrapped instance of the algorithm that swaps everything
             // and store it in the cache
-            algo = new SwappingCollisionAlgorithm<A, B>(swapped);
+            algo = new SwappingCollisionAlgorithm<>(swapped);
             algorithmCache.put(new TypePair(aType, bType), algo);
             return algo;
         }
             algorithmCache.put(orig, algo);
 
             // store swapped algorithm
-            algo = new SwappingCollisionAlgorithm<A, B>(swapped);
+            algo = new SwappingCollisionAlgorithm<>(swapped);
             algorithmCache.put(new TypePair(aType, bType), algo);
             return algo;
         }

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

      *
      * @return The Shape's local bounds
      */
-    public
     @Const
-    AxisAlignedBox getBounds();
+    public AxisAlignedBox getBounds();
 
     /**
      * Return the vector containing the inertia tensor for this shape, in its local transform space. The

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/EPA.java

 import com.ferox.physics.collision.ClosestPair;
 import com.ferox.util.Bag;
 
+/**
+ * EPA implements the expanding polytope algorithm that computes penetration distance of two convex hulls.
+ * This should not be used directly, the {@link GjkEpaCollisionAlgorithm} should be used instead.
+ *
+ * @author Michael Ludwig
+ */
 public class EPA {
     private static final int EPA_MAX_ITERATIONS = 255;
     private static final double EPA_ACCURACY = .00001;

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/GJK.java

 import com.ferox.math.Const;
 import com.ferox.math.Vector3;
 
+/**
+ * GJK implements the GJK convex hull distance algorithm in a MinkowskiShape. Should not be used directly, but
+ * accessed through {@link GjkEpaCollisionAlgorithm}.
+ *
+ * @author Michael Ludwig
+ */
 public class GJK {
     private static final int GJK_MAX_ITERATIONS = 128;
     private static final double GJK_MIN_DISTANCE = .00001;
         double alpha = 0.0;
 
         // add first vertex
-        ray.set(simplex.addVertex(guess, true));
+        ray.set(simplex.addNegatedVertex(guess));
         simplex.setWeight(0, 1.0);
 
         Vector3[] oldSupports = new Vector3[] {
             }
 
             // add another vertex
-            Vector3 support = simplex.addVertex(ray, true);
+            Vector3 support = simplex.addNegatedVertex(ray);
 
             // check for duplicates
             for (int j = 0; j < oldSupports.length; j++) {

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/GjkEpaCollisionAlgorithm.java

 import com.ferox.physics.collision.CollisionAlgorithm;
 import com.ferox.physics.collision.shape.ConvexShape;
 
+/**
+ * GjkEpaCollisionAlgorithm is a collision algorithm that combines the GJK and EPA algorithms to compute
+ * closest pairs for both separated and penetrating collision bodies. By disabling the margin many small
+ * penetrations within the margin radius can still be computed with GJK. It will also increase the margin to
+ * increase penetration if numerical stabilities prevent the EPA from determining a solution.
+ * <p/>
+ * Even so, it is strongly recommended to wrap this algorithm in a {@link JitteringCollisionAlgorithm} to
+ * further reduce the likelihood of a missed collision.
+ *
+ * @author Michael Ludwig
+ */
 public class GjkEpaCollisionAlgorithm implements CollisionAlgorithm<ConvexShape, ConvexShape> {
     private static final int MAX_EPA_CHECKS = 4;
 
     @Override
     public ClosestPair getClosestPair(ConvexShape shapeA, @Const Matrix4 transA, ConvexShape shapeB,
                                       @Const Matrix4 transB) {
-        ClosestPair p = null;
+        ClosestPair p;
 
         MinkowskiShape shape = new MinkowskiShape(shapeA, transA, shapeB, transB);
         shape.setAppliedMargins(0);

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/MinkowskiShape.java

 import com.ferox.physics.collision.ClosestPair;
 import com.ferox.physics.collision.shape.ConvexShape;
 
+/**
+ * MinkowskiShape represents the minkowski difference between two convex shapes that each have an affine
+ * transform applied to them. It contains functions to evaluate the combined support function of the minkowski
+ * difference.
+ *
+ * @author Michael Ludwig
+ */
 public class MinkowskiShape {
     private static final double CONTACT_NORMAL_ACCURACY = .0001;
 

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/Simplex.java

 import com.ferox.math.Const;
 import com.ferox.math.Vector3;
 
+/**
+ * Simplex is a data class that builds up a simplex for use by the GJK and EPA algorithms. Much of this class
+ * was ported and restructured from the Simplex used in Bullet's GJK algorithms.
+ *
+ * @author Michael Ludwig
+ */
 public class Simplex {
     public static final int MAX_RANK = 4;
 
         this.rank = rank;
     }
 
-    // FIXME John Carmack makes a good point that a boolean argument in this
-    // case could be hard to remember how to use properly
-    public Vector3 addVertex(@Const Vector3 dir, boolean negate) {
+    public Vector3 addVertex(@Const Vector3 dir) {
         weights[rank] = 0.0;
-        if (negate) {
-            inputs[rank].scale(dir, -1.0).normalize();
-        } else {
-            inputs[rank].normalize(dir);
-        }
+        inputs[rank].normalize(dir);
+        shape.getSupport(inputs[rank], vertices[rank]);
+        return vertices[rank++];
+    }
+
+    public Vector3 addNegatedVertex(@Const Vector3 dir) {
+        weights[rank] = 0.0;
+        inputs[rank].scale(dir, -1.0).normalize();
         shape.getSupport(inputs[rank], vertices[rank]);
         return vertices[rank++];
     }
             Vector3 axis = new Vector3();
             for (int i = 0; i < 3; i++) {
                 axis.set(0, 0, 0).set(i, 1.0);
-                addVertex(axis, false);
+                addVertex(axis);
                 if (encloseOriginImpl()) {
                     return true;
                 }
                 discardLastVertex();
-                addVertex(axis, true);
+                addNegatedVertex(axis);
                 if (encloseOriginImpl()) {
                     return true;
                 }
                 axis.set(0, 0, 0).set(i, 1.0);
                 axis.cross(d, axis);
                 if (axis.lengthSquared() > 0) {
-                    addVertex(axis, false);
+                    addVertex(axis);
                     if (encloseOriginImpl()) {
                         return true;
                     }
                     discardLastVertex();
-                    addVertex(axis, true);
+                    addNegatedVertex(axis);
                     if (encloseOriginImpl()) {
                         return true;
                     }
         case 3: {
             Vector3 n = Util.normal(vertices[0], vertices[1], vertices[2], null);
             if (n.lengthSquared() > 0) {
-                addVertex(n, false);
+                addVertex(n);
                 if (encloseOriginImpl()) {
                     return true;
                 }
                 discardLastVertex();
-                addVertex(n, true);
+                addNegatedVertex(n);
                 if (encloseOriginImpl()) {
                     return true;
                 }

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/SwappingCollisionAlgorithm.java

 import com.ferox.physics.collision.Shape;
 
 /**
- * SwappingCollisionAlgorithm is a utility to swap the shape types that a true collision algorithm can handle.
- * For example, there might be a SphereBoxCollisionAlgorithm. This class can be used to automatically create a
- * BoxSphereCollisionAlgorithm.
+ * SwappingCollisionAlgorithm is a algorithm that swaps the shape types that a true collision algorithm can
+ * handle. For example, there might be a SphereBoxCollisionAlgorithm. This class can be used to automatically
+ * create a BoxSphereCollisionAlgorithm.
  *
  * @param <A> The first Shape type
  * @param <B> The second Shape type

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/Util.java

 import com.ferox.math.Const;
 import com.ferox.math.Vector3;
 
+/**
+ * Vector utilities used by the GJK and EPA algorithms.
+ *
+ * @author Michael Ludwig
+ */
 public class Util {
-
+    /**
+     * Compute the triple product over the three vectors.
+     *
+     * @param a First vector
+     * @param b Second vector
+     * @param c Third vector
+     *
+     * @return The triple product over a, b, and c
+     *
+     * @throws NullPointerException if a, b, or c are null
+     */
     public static double tripleProduct(@Const Vector3 a, @Const Vector3 b, @Const Vector3 c) {
         return a.y * b.z * c.x + a.z * b.x * c.y - a.x * b.z * c.y - a.y * b.x * c.z + a.x * b.y * c.z -
                a.z * b.y * c.x;
     }
 
+    /**
+     * Compute the normal vector for the triangle formed by {@code va}, {@code vb}, {@code vc} and store it in
+     * {@code result}.
+     *
+     * @param va     First vertex
+     * @param vb     Second vertex
+     * @param vc     Third vertex
+     * @param result The output
+     *
+     * @return The result vector with normal, or a new vector if result is null
+     *
+     * @throws NullPointerException if va, vb, vc are null
+     */
     public static Vector3 normal(@Const Vector3 va, @Const Vector3 vb, @Const Vector3 vc, Vector3 result) {
         // inline subtraction of 2 vectors
         double e1x = vb.x - va.x;

ferox-physics/src/main/java/com/ferox/physics/collision/shape/Box.java

 import com.ferox.math.Const;
 import com.ferox.math.Vector3;
 
+/**
+ * Box represents a three dimensional rectangular prism centered on its local origin with configurable
+ * dimensions aligned with its local x, y, and z axis.
+ *
+ * @author Michael Ludwig
+ */
 public class Box extends ConvexShape {
     private final Vector3 localTensorPartial;
     private final Vector3 halfExtents;
 
+    /**
+     * Create a new Box with the given dimensions.
+     *
+     * @param xExtent The x dimension or width
+     * @param yExtent The y dimension or height
+     * @param zExtent The z dimension or depth
+     */
     public Box(double xExtent, double yExtent, double zExtent) {
         localTensorPartial = new Vector3();
         halfExtents = new Vector3();
         setExtents(xExtent, yExtent, zExtent);
     }
 
-    public
+    /**
+     * @return Get the half extents of this box. It should not be mutated
+     */
     @Const
-    Vector3 getHalfExtents() {
+    public Vector3 getHalfExtents() {
         return halfExtents;
     }
 
+    /**
+     * @return Get the full extents of this box. This creates a new vector
+     */
     public Vector3 getExtents() {
         return new Vector3().scale(halfExtents, 2.0);
     }
 
+    /**
+     * Set the extents of this box to the given dimensions.
+     *
+     * @param width  The x dimension or width of the box
+     * @param height The y dimension or height of the box
+     * @param depth  The z dimension or depth of the box
+     *
+     * @throws IllegalArgumentException if any dimension is less than or equal to 0
+     */
     public void setExtents(double width, double height, double depth) {
         if (width <= 0) {
             throw new IllegalArgumentException("Invalid width, must be greater than 0, not: " + width);
         updateBounds();
     }
 
+    /**
+     * @return Get the width of this box
+     */
     public double getWidth() {
         return halfExtents.x * 2.0;
     }
 
+    /**
+     * @return Get the height of this box
+     */
     public double getHeight() {
         return halfExtents.y * 2.0;
     }
 
+    /**
+     * @return Get the depth of this box
+     */
     public double getDepth() {
         return halfExtents.z * 2.0;
     }

ferox-physics/src/main/java/com/ferox/physics/collision/shape/Cone.java

 import com.ferox.math.Const;
 import com.ferox.math.Vector3;
 
+/**
+ * Cone is a convex shape implementation that represents a cone. It has two parameters, the height of the cone
+ * and the radius of its base. The orientation of the cylinder is chosen by its dominant axis. The base of the
+ * cone is on the negative half-space along the axis, and the tip is in the positive half-space.
+ *
+ * @author Michael Ludwig
+ */
 public class Cone extends AxisSweptShape {
     private double halfHeight;
     private double baseRadius;
 
+    /**
+     * Create a new Cone with the given radius and height aligned on the Z axis.
+     *
+     * @param baseRadius The cone's base radius
+     * @param height     The height of the cone
+     */
     public Cone(double baseRadius, double height) {
         this(baseRadius, height, Axis.Z);
     }
 
+    /**
+     * Create a new Cone with the given radius, height, and axis of revolution.
+     *
+     * @param baseRadius   The cone's base radius
+     * @param height       The height of the cone
+     * @param dominantAxis The dominant axis that the cone is aligned with
+     */
     public Cone(double baseRadius, double height, Axis dominantAxis) {
         super(dominantAxis);
         setBaseRadius(baseRadius);
         setHeight(height);
     }
 
+    /**
+     * @return The height of the cone
+     */
     public double getHeight() {
         return 2.0 * halfHeight;
     }
 
+    /**
+     * @return The base radius of the cone
+     */
     public double getBaseRadius() {
         return baseRadius;
     }
 
+    /**
+     * Set the height of the cone
+     *
+     * @param height The new height
+     *
+     * @throws IllegalArgumentException if height is less than or equal to 0
+     */
     public void setHeight(double height) {
         if (height <= 0f) {
             throw new IllegalArgumentException("Height must be greater than 0, not: " + height);
         update();
     }
 
+    /**
+     * Set the base radius of the cone
+     *
+     * @param radius The new radius
+     *
+     * @throws IllegalArgumentException if radius is less than or equal to 0
+     */
     public void setBaseRadius(double radius) {
         if (radius <= 0f) {
             throw new IllegalArgumentException("Radius must be greater than 0, not: " + radius);

ferox-physics/src/main/java/com/ferox/physics/collision/shape/ConvexShape.java

         Vector3 d = new Vector3();
         Vector3 t = new Vector3();
 
-        // FIXME why is it 2 * margin, and not just margin? are we trying to make
-        // the bounds have extra padding to encourage non-intersecting shapes?
-        // Should experiment with changing this once things start running again
         computeSupport(d.set(1, 0, 0), t);
-        double maxX = t.x + 2 * margin;
+        double maxX = t.x + margin;
         computeSupport(d.set(-1, 0, 0), t);
-        double minX = t.x - 2 * margin;
+        double minX = t.x - margin;
 
         computeSupport(d.set(0, 1, 0), t);
-        double maxY = t.y + 2 * margin;
+        double maxY = t.y + margin;
         computeSupport(d.set(0, -1, 0), t);
-        double minY = t.y - 2 * margin;
+        double minY = t.y - margin;
 
         computeSupport(d.set(0, 0, 1), t);
-        double maxZ = t.z + 2 * margin;
+        double maxZ = t.z + margin;
         computeSupport(d.set(0f, 0f, -1f), t);
-        double minZ = t.z - 2 * margin;
+        double minZ = t.z - margin;
 
         bounds.max.set(maxX, maxY, maxZ);
         bounds.min.set(minX, minY, minZ);

ferox-physics/src/main/java/com/ferox/physics/collision/shape/Cylinder.java

 /**
  * Cylinder is a convex shape implementation that represents a cylinder. It has two parameters, the height of
  * the cylinder and the radius of the cylinder caps. The orientation of the cylinder is chosen by its dominant
- * axis.
+ * axis. The cylinder is centered on its local origin, with half the cylinder in the positive half-space and
+ * half in the negative half-space.
  *
  * @author Michael Ludwig
  */
     private double halfHeight;
 
     /**
-     * @param capRadius
-     * @param height
+     * Create a new cylinder that has a radius of {@code capRadius} and the given {@code height}. Its axis of
+     * revolution is the Z axis.
+     *
+     * @param capRadius The radius of the cylinder
+     * @param height    The height of the cylinder
      */
     public Cylinder(double capRadius, double height) {
         this(capRadius, height, Axis.Z);
     }
 
+    /**
+     * Create a new cylinder with a radius of {@code capRadius} and the given {@code height}. The axis of
+     * revolution is {@code dominantAxis}.
+     *
+     * @param capRadius    The radius of the cylinder
+     * @param height       The height of the cylinder
+     * @param dominantAxis The dominant axis
+     */
     public Cylinder(double capRadius, double height, Axis dominantAxis) {
         super(dominantAxis);
-        setCapRadius(capRadius);
+        setRadius(capRadius);
         setHeight(height);
     }
 
-    public double getCapRadius() {
+    /**
+     * @return The radius of the cylinder
+     */
+    public double getRadius() {
         return capRadius;
     }
 
+    /**
+     * @return The height of the cylinder
+     */
     public double getHeight() {
         return 2.0 * halfHeight;
     }
 
-    public void setCapRadius(double radius) {
+    /**
+     * Set the radius of the cylinder
+     *
+     * @param radius The new radius
+     *
+     * @throws IllegalArgumentException if radius is less than or equal to 0
+     */
+    public void setRadius(double radius) {
         if (radius <= 0.0) {
             throw new IllegalArgumentException("Radius must be greater than 0, not: " + radius);
         }
         update();
     }
 
+    /**
+     * Set the height of the cylinder
+     *
+     * @param height The new height
+     *
+     * @throws IllegalArgumentException if height is less than or equal to 0
+     */
     public void setHeight(double height) {
         if (height <= 0.0) {
             throw new IllegalArgumentException("Height must be greater than 0, not: " + height);