diff --git a/src/com/blarg/gdx/math/IntersectionTester.java b/src/com/blarg/gdx/math/IntersectionTester.java index ff41c24..f9ea4e8 100644 --- a/src/com/blarg/gdx/math/IntersectionTester.java +++ b/src/com/blarg/gdx/math/IntersectionTester.java @@ -3,14 +3,20 @@ package com.blarg.gdx.math; import com.badlogic.gdx.math.Plane; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.collision.BoundingBox; +import com.badlogic.gdx.math.collision.Ray; import com.badlogic.gdx.math.collision.Sphere; -// providing support for the odd gaps in testing methods in libgdx's own Intersector class ... -// (plus a few extras here and there!) - +/** + * Various intersection tests between 3D geometric shapes. + * Note that libgdx's own {@link com.badlogic.gdx.math.Intersector} does implement some of these + * tests already, but not all of them. I've just included all of these here for consistency's sake + * and to make porting some of my other projects easier. + */ public class IntersectionTester { static final Vector3 tmp1 = new Vector3(); static final Vector3 tmp2 = new Vector3(); + static final Vector3 tmp3 = new Vector3(); + static final Vector3 point = new Vector3(); public static float getSquaredDistanceFromPointToBox(Vector3 point, BoundingBox box) { float distanceSq = 0.0f; @@ -41,7 +47,47 @@ public class IntersectionTester { return (float)Math.sqrt(getSquaredDistanceFromPointToBox(point, box)); } - public static boolean overlaps(BoundingBox a, BoundingBox b) { + public static boolean test(BoundingBox box, Vector3 point) { + if ((point.x >= box.min.x && point.x <= box.max.x) && + (point.y >= box.min.y && point.y <= box.max.y) && + (point.z >= box.min.z && point.z <= box.max.z)) + return true; + else + return false; + } + + public static boolean test(Sphere sphere, Vector3 point) { + if (Math.abs(point.dst2(sphere.center)) < (sphere.radius * sphere.radius)) + return true; + else + return false; + } + + public static boolean test(BoundingBox box, Vector3[] vertices, Vector3 outFirstIntersection) { + for (int i = 0; i < vertices.length; ++i) { + if ((vertices[i].x >= box.min.x && vertices[i].x <= box.max.x) && + (vertices[i].y >= box.min.y && vertices[i].y <= box.max.y) && + (vertices[i].z >= box.min.z && vertices[i].z <= box.max.z)) { + if (outFirstIntersection != null) + outFirstIntersection.set(vertices[i]); + return true; + } + } + return false; + } + + public static boolean test(Sphere sphere, Vector3[] vertices, Vector3 outFirstIntersection) { + for (int i = 0; i < vertices.length; ++i) { + if (Math.abs(vertices[i].dst2(sphere.center)) < (sphere.radius * sphere.radius)) { + if (outFirstIntersection != null) + outFirstIntersection.set(vertices[i]); + return true; + } + } + return false; + } + + public static boolean test(BoundingBox a, BoundingBox b) { if (a.max.x < b.min.x || a.min.x > b.max.x) return false; else if (a.max.y < b.min.y || a.min.y > b.max.y) @@ -52,23 +98,17 @@ public class IntersectionTester { return true; } - public static boolean overlaps(BoundingBox box, Sphere sphere) { - float distanceSq = getSquaredDistanceFromPointToBox(sphere.center, box); - - if (distanceSq <= (sphere.radius * sphere.radius)) + public static boolean test(Sphere a, Sphere b) { + tmp1.set(a.center).sub(b.center); + float distanceSquared = tmp1.dot(tmp1); + float radiusSum = a.radius + b.radius; + if (distanceSquared <= (radiusSum * radiusSum)) return true; else return false; } - public static boolean contains(Sphere sphere, Vector3 point) { - if (Math.abs(point.dst(sphere.center)) < sphere.radius) - return true; - else - return false; - } - - public static boolean intersects(Sphere sphere, Plane plane) { + public static boolean test(Sphere sphere, Plane plane) { float distance = sphere.center.dot(plane.normal) - plane.d; if (Math.abs(distance) <= sphere.radius) return true; @@ -76,13 +116,13 @@ public class IntersectionTester { return false; } - public static boolean intersects(BoundingBox box, Plane plane) { + public static boolean test(BoundingBox box, Plane plane) { tmp1.set(box.max).add(box.min).scl(0.5f); // (box.max + box.min) / 2.0f tmp2.set(box.max).sub(tmp1); - float radius = (tmp2.x * Math.abs(plane.normal.x)) - + (tmp2.y * Math.abs(plane.normal.y)) - + (tmp2.z * Math.abs(plane.normal.z)); + float radius = (tmp2.x * Math.abs(plane.normal.x)) + + (tmp2.y * Math.abs(plane.normal.y)) + + (tmp2.z * Math.abs(plane.normal.z)); float distance = plane.normal.dot(tmp1) - plane.d; @@ -91,4 +131,184 @@ public class IntersectionTester { else return false; } + + public static boolean test(Ray ray, Plane plane, Vector3 outIntersection) { + float denominator = ray.direction.dot(plane.normal); + if (denominator == 0.0f) + return false; + + float t = ((-plane.d - ray.origin.dot(plane.normal)) / denominator); + if (t < 0.0f) + return false; + + if (outIntersection != null) + ray.getEndPoint(outIntersection, t); + + return true; + } + + public static boolean test(Ray ray, Sphere sphere, Vector3 outFirstIntersection) { + tmp1.set(ray.origin).sub(sphere.center); + + float b = tmp1.dot(ray.direction); + float c = tmp1.dot(tmp1) - (sphere.radius * sphere.radius); + + if (c > 0.0f && b > 0.0f) + return false; + + float discriminant = b * b - c; + if (discriminant < 0.0f) + return false; + + float t = -b - (float)Math.sqrt(discriminant); + if (t < 0.0f) + t = 0.0f; + + if (outFirstIntersection != null) + ray.getEndPoint(outFirstIntersection, t); + + return true; + } + + public static boolean test(Ray ray, BoundingBox box, Vector3 outFirstIntersection) { + float tmin = 0.0f; + float tmax = Float.MAX_VALUE; + + if (Math.abs(ray.direction.x) < MathHelpers.FLOAT_EPSILON) { + if (ray.origin.x < box.min.x || ray.origin.x > box.max.x) + return false; + } else { + float invD = 1.0f / ray.direction.x; + float t1 = (box.min.x - ray.origin.x) * invD; + float t2 = (box.max.x - ray.origin.x) * invD; + + if (t1 > t2) { + float tswap = t1; + t1 = t2; + t2 = tswap; + } + + tmin = Math.max(tmin, t1); + tmax = Math.min(tmax, t2); + + if (tmin > tmax) + return false; + } + + if (Math.abs(ray.direction.y) < MathHelpers.FLOAT_EPSILON) { + if (ray.origin.y < box.min.y || ray.origin.y > box.max.y) + return false; + } else { + float invD = 1.0f / ray.direction.y; + float t1 = (box.min.y - ray.origin.y) * invD; + float t2 = (box.max.y - ray.origin.y) * invD; + + if (t1 > t2) { + float tswap = t1; + t1 = t2; + t2 = tswap; + } + + tmin = Math.max(tmin, t1); + tmax = Math.min(tmax, t2); + + if (tmin > tmax) + return false; + } + + if (Math.abs(ray.direction.z) < MathHelpers.FLOAT_EPSILON) { + if (ray.origin.z < box.min.z || ray.origin.z > box.max.z) + return false; + } else { + float invD = 1.0f / ray.direction.z; + float t1 = (box.min.z - ray.origin.z) * invD; + float t2 = (box.max.z - ray.origin.z) * invD; + + if (t1 > t2) { + float tswap = t1; + t1 = t2; + t2 = tswap; + } + + tmin = Math.max(tmin, t1); + tmax = Math.min(tmax, t2); + + if (tmin > tmax) + return false; + } + + if (outFirstIntersection != null) + ray.getEndPoint(outFirstIntersection, tmin); + + return true; + } + + public static boolean test(BoundingBox box, Sphere sphere) { + float distanceSq = getSquaredDistanceFromPointToBox(sphere.center, box); + if (distanceSq <= (sphere.radius * sphere.radius)) + return true; + else + return false; + } + + public static boolean test(Ray ray, Vector3 a, Vector3 b, Vector3 c, Vector3 outIntersection) { + float r; + float num1; + float num2; + + tmp1.set(b).sub(a); + tmp2.set(c).sub(a); + tmp3.set(tmp1).crs(tmp2); + if (tmp3.x == 0.0f && tmp3.y == 0.0f && tmp3.z == 0.0f) + return false; + + tmp1.set(ray.origin).sub(a); + num1 = -tmp3.dot(tmp1); + num2 = tmp3.dot(ray.direction); + if (Math.abs(num2) < MathHelpers.FLOAT_EPSILON) { + if (num1 == 0.0f) { + if (outIntersection != null) + outIntersection.set(ray.origin); + return true; + } else + return false; + } + + r = num1 / num2; + if (r < 0.0f) + return false; + + ray.getEndPoint(point, r); + if (test(point, a, b, c)) { + if (outIntersection != null) + outIntersection.set(point); + return true; + } else + return false; + } + + public static boolean test(Vector3 point, Vector3 a, Vector3 b, Vector3 c) { + tmp1.set(c).sub(a); + tmp2.set(b).sub(a); + tmp3.set(point).sub(a); + + float dot00 = (tmp1.x * tmp1.x) + (tmp1.y * tmp1.y) + (tmp1.z * tmp1.z); + float dot01 = (tmp1.x * tmp2.x) + (tmp1.y * tmp2.y) + (tmp1.z * tmp2.z); + float dot02 = (tmp1.x * tmp3.x) + (tmp1.y * tmp3.y) + (tmp1.z * tmp3.z); + float dot11 = (tmp2.x * tmp2.x) + (tmp2.y * tmp2.y) + (tmp2.z * tmp2.z); + float dot12 = (tmp2.x * tmp3.x) + (tmp2.y * tmp3.y) + (tmp2.z * tmp3.z); + + float denom = dot00 * dot11 - dot01 * dot01; + if (denom == 0) + return false; + + float u = (dot11 * dot02 - dot01 * dot12) / denom; + float v = (dot00 * dot12 - dot01 * dot02) / denom; + + if (u >= 0 && v >= 0 && u + v <= 1) + return true; + else + return false; + } } + diff --git a/src/com/blarg/gdx/math/MathHelpers.java b/src/com/blarg/gdx/math/MathHelpers.java index 001b812..660d21a 100644 --- a/src/com/blarg/gdx/math/MathHelpers.java +++ b/src/com/blarg/gdx/math/MathHelpers.java @@ -8,6 +8,7 @@ import com.badlogic.gdx.math.collision.BoundingBox; public final class MathHelpers { static final Vector2 v2tmpA = new Vector2(); + public static final float FLOAT_EPSILON = 1.401298E-45f; // smallest floating point value greater then zero public static final float EPSILON = 0.0000000001f; public static final float UP_2D = 90.0f;