/*******************************************************************************
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package rajawali.util;
import rajawali.math.Number3D;
import rajawali.math.Plane;
/** Class offering various static methods for intersection testing between different geometric objects.
*
* Originally written by Badlogic Games. Ported for Rajawali by Andrew Jo.
*
* @author badlogicgames@gmail.com
* @author jan.stria
* @author andrewjo@gmail.com
*/
public final class Intersector {
private final static Number3D v0 = new Number3D();
private final static Number3D v1 = new Number3D();
private final static Number3D v2 = new Number3D();
/**
* Intersects a ray defined by a start and end point and a {@link Plane}.
* @param rayStart Startpoint of the ray
* @param rayEnd Endpoint of the ray
* @param plane The plane
* @param hitPoint The intersection point (optional)
* @return True if there is an intersection, false otherwise.
*/
public static boolean intersectRayPlane(Number3D rayStart, Number3D rayEnd, Plane plane, Number3D hitPoint) {
Number3D rayDir = Number3D.subtract(rayEnd, rayStart);
float denorm = rayDir.dot(plane.getNormal());
if (denorm != 0) {
float t = -(rayStart.dot(plane.getNormal()) + plane.getD()) / denorm;
if (t < 0) return false;
if (hitPoint != null) hitPoint.setAllFrom(Number3D.add(rayStart, Number3D.multiply(rayDir, t)));
return true;
} else if (plane.getPointSide(rayStart) == Plane.PlaneSide.OnPlane) {
if (hitPoint != null) hitPoint.setAllFrom(rayStart);
return true;
} else {
return false;
}
}
private static final Plane p = new Plane(new Number3D(), 0);
private static final Number3D i = new Number3D();
/**
* Intersects a ray defined by a start and end point and a triangle.
* @param rayStart Startpoint of the ray
* @param rayEnd Endpoint of the ray
* @param t1 The first vertex of the triangle
* @param t2 The second vertex of the triangle
* @param t3 The third vertex of the triangle
* @param hitPoint The intersection point (optional)
* @return True if there is an intersection, false otherwise.
*/
public static boolean intersectRayTriangle(Number3D rayStart, Number3D rayEnd, Number3D t1, Number3D t2, Number3D t3, Number3D hitPoint) {
Number3D rayDir = Number3D.subtract(rayEnd, rayStart);
rayDir.normalize();
p.set(t1, t2, t3);
if (!intersectRayPlane(rayStart, rayEnd, p, i)) return false;
v0.setAllFrom(Number3D.subtract(t3, t1));
v1.setAllFrom(Number3D.subtract(t2, t1));
v2.setAllFrom(Number3D.subtract(i, t1));
float dot00 = v0.dot(v0);
float dot01 = v0.dot(v1);
float dot02 = v0.dot(v2);
float dot11 = v1.dot(v1);
float dot12 = v1.dot(v2);
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) {
if (hitPoint != null) hitPoint.setAllFrom(i);
return true;
} else
return false;
}
/**
* Intersects a ray defined by the start and end point and a sphere, returning the intersection point in intersection.
* @param rayStart Startpoint of the ray
* @param rayEnd Endpoint of the ray
* @param sphereCenter The center of the sphere
* @param sphereRadius The radius of the sphere
* @param hitPoint The intersection point (optional)
* @return True if there is an intersection, false otherwise.
*/
public static boolean intersectRaySphere(Number3D rayStart, Number3D rayEnd, Number3D sphereCenter, float sphereRadius, Number3D hitPoint) {
rayStart = new Number3D(rayStart);
rayEnd = new Number3D(rayEnd);
Number3D dir = Number3D.subtract(rayEnd, rayStart);
dir.normalize();
sphereCenter = new Number3D(sphereCenter);
float radius2 = sphereRadius * sphereRadius;
/*
* Refer to http://paulbourke.net/geometry/circlesphere/ for mathematics
* behind ray-sphere intersection.
*/
float a = Number3D.dot(dir, dir);
float b = 2.0f * Number3D.dot(dir, Number3D.subtract(rayStart, sphereCenter));
float c = Number3D.dot(sphereCenter, sphereCenter) + Number3D.dot(rayStart, rayStart) - 2.0f * Number3D.dot(sphereCenter, rayStart) - radius2;
// Test for intersection.
float result = b * b - 4.0f * a * c;
if (result < 0) return false;
// Starting with this section, the code was referenced from libGDX.
float distSqrt = (float)Math.sqrt(result);
float q;
if (b < 0)
q = (-b - distSqrt) / 2.0f;
else
q = (-b + distSqrt) / 2.0f;
float t0 = q / 1;
float t1 = c / q;
// If t0 is larger than t1, swap them around.
if (t0 > t1) {
float temp = t0;
t0 = t1;
t1 = temp;
}
// If t1 is less than zero, the object is in the ray's negative direction
// and consequently ray misses the sphere.
if (t1 < 0) return false;
// If t0 is less than zero, intersection point is at t1.
if (t0 < 0) {
hitPoint = rayStart.add(Number3D.multiply(dir, t1));
return true;
} else {
hitPoint = rayStart.add(Number3D.multiply(dir, t0));
return true;
}
}
}