/*
* Copyright (c) 2003-2008 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme.intersection;
import com.jme.math.*;
import DPJRuntime.*;
/**
* <code>Intersection</code> provides functional methods for calculating the
* intersection of some objects. All the methods are static to allow for quick
* and easy calls. <code>Intersection</code> relays requests to specific
* classes to handle the actual work. By providing checks to just
* <code>BoundingVolume</code> the client application need not worry about
* what type of bounding volume is being used.
*
* @author Mark Powell
* @version $Id: Intersection.java,v 1.26 2006/06/21 20:33:02 nca Exp $
*/
public class Intersection {
/**
* EPSILON represents the error buffer used to denote a hit.
*/
public static final double EPSILON = 1e-12;
/**
* This method tests for the intersection between two triangles defined by
* their vertexes. Converted to java from C code found at
* http://www.acm.org/jgt/papers/Moller97/tritri.html
*
* @param v0
* First triangle's first vertex.
* @param v1
* First triangle's second vertex.
* @param v2
* First triangle's third vertex.
* @param u0
* Second triangle's first vertex.
* @param u1
* Second triangle's second vertex.
* @param u2
* Second triangle's third vertex.
* @return True if the two triangles intersect, false otherwise.
*/
/*
* Thread-safe versions of intersection routine
*/
public static <region RVals> boolean intersection_r(Vector3f<RVals> v0, Vector3f<RVals> v1, Vector3f<RVals> v2,
Vector3f<RVals> u0, Vector3f<RVals> u1, Vector3f<RVals> u2) {
region RTemps;
Vector3f<RTemps> e1 = new Vector3f<RTemps>();
Vector3f<RTemps> e2 = new Vector3f<RTemps>();
Vector3f<RTemps> n1 = new Vector3f<RTemps>();
Vector3f<RTemps> n2 = new Vector3f<RTemps>();
ArrayFloat<RTemps> isect1 = new ArrayFloat<RTemps>(2);
ArrayFloat<RTemps> isect2 = new ArrayFloat<RTemps>(2);
return Intersection.intersection_r(v1, v1, v2, u0, u1, u2, e1, e2, n1, n2, isect1, isect2);
}
public static <region RVals, RTemps>
boolean intersection_r(Vector3f<RVals> v0, Vector3f<RVals> v1,
Vector3f<RVals> v2, Vector3f<RVals> u0,
Vector3f<RVals> u1, Vector3f<RVals> u2,
Vector3f<RTemps> e1, Vector3f<RTemps> e2,
Vector3f<RTemps> n1, Vector3f<RTemps> n2,
ArrayFloat<RTemps> isect1, ArrayFloat<RTemps> isect2)
writes RVals, RTemps:* {
float d1, d2;
float du0, du1, du2, dv0, dv1, dv2;
Vector3f<RTemps> d = new Vector3f<RTemps>();
float du0du1, du0du2, dv0dv1, dv0dv2;
short index;
float vp0, vp1, vp2;
float up0, up1, up2;
float bb, cc, max;
float xx, yy, xxyy, tmp;
/* compute plane equation of triangle(v0,v1,v2) */
v1.subtract(v0, e1);
v2.subtract(v0, e2);
e1.cross(e2, n1);
d1 = -n1.dot(v0);
/* plane equation 1: n1.X+d1=0 */
/*
* put u0,u1,u2 into plane equation 1 to compute signed distances to the
* plane
*/
du0 = n1.dot(u0) + d1;
du1 = n1.dot(u1) + d1;
du2 = n1.dot(u2) + d1;
/* coplanarity robustness check */
if (Math.abs(du0) < EPSILON)
du0 = 0.0f;
if (Math.abs(du1) < EPSILON)
du1 = 0.0f;
if (Math.abs(du2) < EPSILON)
du2 = 0.0f;
du0du1 = du0 * du1;
du0du2 = du0 * du2;
if (du0du1 > 0.0f && du0du2 > 0.0f) {
return false;
}
/* compute plane of triangle (u0,u1,u2) */
u1.subtract(u0, e1);
u2.subtract(u0, e2);
e1.cross(e2, n2);
d2 = -n2.dot(u0);
/* plane equation 2: n2.X+d2=0 */
/* put v0,v1,v2 into plane equation 2 */
dv0 = n2.dot(v0) + d2;
dv1 = n2.dot(v1) + d2;
dv2 = n2.dot(v2) + d2;
if (Math.abs(dv0) < EPSILON)
dv0 = 0.0f;
if (Math.abs(dv1) < EPSILON)
dv1 = 0.0f;
if (Math.abs(dv2) < EPSILON)
dv2 = 0.0f;
dv0dv1 = dv0 * dv1;
dv0dv2 = dv0 * dv2;
if (dv0dv1 > 0.0f && dv0dv2 > 0.0f) { /*
* same sign on all of them + not
* equal 0 ?
*/
return false; /* no intersection occurs */
}
/* compute direction of intersection line */
n1.cross(n2, d);
/* compute and index to the largest component of d */
max = Math.abs(d.x);
index = 0;
bb = Math.abs(d.y);
cc = Math.abs(d.z);
if (bb > max) {
max = bb;
index = 1;
}
if (cc > max) {
max = cc;
vp0 = v0.z;
vp1 = v1.z;
vp2 = v2.z;
up0 = u0.z;
up1 = u1.z;
up2 = u2.z;
} else if (index == 1) {
vp0 = v0.y;
vp1 = v1.y;
vp2 = v2.y;
up0 = u0.y;
up1 = u1.y;
up2 = u2.y;
} else {
vp0 = v0.x;
vp1 = v1.x;
vp2 = v2.x;
up0 = u0.x;
up1 = u1.x;
up2 = u2.x;
}
/* compute interval for triangle 1 */
Vector3f<RTemps> abc = e1;
Vector2f<RTemps> x0x1 = new Vector2f<RTemps>();
if (Intersection.newComputeIntervals(vp0, vp1, vp2, dv0, dv1, dv2, dv0dv1, dv0dv2,
abc, x0x1)) {
return Intersection.coplanarTriTri(n1, v0, v1, v2, u0, u1, u2);
}
/* compute interval for triangle 2 */
Vector3f<RTemps> def = e2;
Vector2f<RTemps> y0y1 = new Vector2f<RTemps>();
if (Intersection.newComputeIntervals(up0, up1, up2, du0, du1, du2, du0du1, du0du2,
def, y0y1)) {
return Intersection.coplanarTriTri(n1, v0, v1, v2, u0, u1, u2);
}
xx = x0x1.x * x0x1.y;
yy = y0y1.x * y0y1.y;
xxyy = xx * yy;
tmp = abc.x * xxyy;
isect1[0] = tmp + abc.y * x0x1.y * yy;
isect1[1] = tmp + abc.z * x0x1.x * yy;
tmp = def.x * xxyy;
isect2[0] = tmp + def.y * xx * y0y1.y;
isect2[1] = tmp + def.z * xx * y0y1.x;
Intersection.sort(isect1);
Intersection.sort(isect2);
if (isect1[1] < isect2[0] || isect2[1] < isect1[0]) {
return false;
}
return true;
}
private static <region RTemps> void sort(ArrayFloat<RTemps> f) writes RTemps:* {
if (f[0] > f[1]) {
float c = f[0];
f[0] = f[1];
f[1] = c;
}
}
private static <region Rabc, Rx> boolean newComputeIntervals(float vv0, float vv1, float vv2,
float d0, float d1, float d2, float d0d1, float d0d2, Vector3f<Rabc> abc,
Vector2f<Rx> x0x1) writes Rabc, Rx {
if (d0d1 > 0.0f) {
/* here we know that d0d2 <=0.0 */
/*
* that is d0, d1 are on the same side, d2 on the other or on the
* plane
*/
abc.x = vv2;
abc.y = (vv0 - vv2) * d2;
abc.z = (vv1 - vv2) * d2;
x0x1.x = d2 - d0;
x0x1.y = d2 - d1;
} else if (d0d2 > 0.0f) {
/* here we know that d0d1 <=0.0 */
abc.x = vv1;
abc.y = (vv0 - vv1) * d1;
abc.z = (vv2 - vv1) * d1;
x0x1.x = d1 - d0;
x0x1.y = d1 - d2;
} else if (d1 * d2 > 0.0f || d0 != 0.0f) {
/* here we know that d0d1 <=0.0 or that d0!=0.0 */
abc.x = vv0;
abc.y = (vv1 - vv0) * d0;
abc.z = (vv2 - vv0) * d0;
x0x1.x = d0 - d1;
x0x1.y = d0 - d2;
} else if (d1 != 0.0f) {
abc.x = vv1;
abc.y = (vv0 - vv1) * d1;
abc.z = (vv2 - vv1) * d1;
x0x1.x = d1 - d0;
x0x1.y = d1 - d2;
} else if (d2 != 0.0f) {
abc.x = vv2;
abc.y = (vv0 - vv2) * d2;
abc.z = (vv1 - vv2) * d2;
x0x1.x = d2 - d0;
x0x1.y = d2 - d1;
} else {
/* triangles are coplanar */
return true;
}
return false;
}
private static <region Rn, Rv> boolean coplanarTriTri(Vector3f<Rn> n, Vector3f<Rv> v0, Vector3f<Rv> v1,
Vector3f<Rv> v2, Vector3f<Rv> u0, Vector3f<Rv> u1, Vector3f<Rv> u2) reads Rv writes Rn:* {
Vector3f<Rn> a = new Vector3f<Rn>();
short i0, i1;
a.x = Math.abs(n.x);
a.y = Math.abs(n.y);
a.z = Math.abs(n.z);
if (a.x > a.y) {
if (a.x > a.z) {
i0 = 1; /* a[0] is greatest */
i1 = 2;
} else {
i0 = 0; /* a[2] is greatest */
i1 = 1;
}
} else /* a[0] <=a[1] */{
if (a.z > a.y) {
i0 = 0; /* a[2] is greatest */
i1 = 1;
} else {
i0 = 0; /* a[1] is greatest */
i1 = 2;
}
}
/* test all edges of triangle 1 against the edges of triangle 2 */
ArrayFloat<Rn> v0f = new ArrayFloat<Rn>(3);
v0.toArray(v0f);
ArrayFloat<Rn> v1f = new ArrayFloat<Rn>(3);
v1.toArray(v1f);
ArrayFloat<Rn> v2f = new ArrayFloat<Rn>(3);
v2.toArray(v2f);
ArrayFloat<Rn> u0f = new ArrayFloat<Rn>(3);
u0.toArray(u0f);
ArrayFloat<Rn> u1f = new ArrayFloat<Rn>(3);
u1.toArray(u1f);
ArrayFloat<Rn> u2f = new ArrayFloat<Rn>(3);
u2.toArray(u2f);
if (Intersection.edgeAgainstTriEdges(v0f, v1f, u0f, u1f, u2f, i0, i1)) {
return true;
}
if (Intersection.edgeAgainstTriEdges(v1f, v2f, u0f, u1f, u2f, i0, i1)) {
return true;
}
if (Intersection.edgeAgainstTriEdges(v2f, v0f, u0f, u1f, u2f, i0, i1)) {
return true;
}
/* finally, test if tri1 is totally contained in tri2 or vice versa */
Intersection.pointInTri(v0f, u0f, u1f, u2f, i0, i1);
Intersection.pointInTri(u0f, v0f, v1f, v2f, i0, i1);
return false;
}
private static <region R>
boolean pointInTri(ArrayFloat<R> V0, ArrayFloat<R> U0,
ArrayFloat<R> U1, ArrayFloat<R> U2,
int i0, int i1)
reads R:*
{
float a, b, c, d0, d1, d2;
/* is T1 completly inside T2? */
/* check if V0 is inside tri(U0,U1,U2) */
a = U1[i1] - U0[i1];
b = -(U1[i0] - U0[i0]);
c = -a * U0[i0] - b * U0[i1];
d0 = a * V0[i0] + b * V0[i1] + c;
a = U2[i1] - U1[i1];
b = -(U2[i0] - U1[i0]);
c = -a * U1[i0] - b * U1[i1];
d1 = a * V0[i0] + b * V0[i1] + c;
a = U0[i1] - U2[i1];
b = -(U0[i0] - U2[i0]);
c = -a * U2[i0] - b * U2[i1];
d2 = a * V0[i0] + b * V0[i1] + c;
if (d0 * d1 > 0.0 && d0 * d2 > 0.0)
return true;
return false;
}
private static <region R>
boolean edgeAgainstTriEdges(ArrayFloat<R> v0, ArrayFloat<R> v1,
ArrayFloat<R> u0, ArrayFloat<R> u1,
ArrayFloat<R> u2, int i0, int i1)
reads R:*
{
float aX, aY;
aX = v1[i0] - v0[i0];
aY = v1[i1] - v0[i1];
/* test edge u0,u1 against v0,v1 */
if (Intersection.edgeEdgeTest(v0, u0, u1, i0, i1, aX, aY)) {
return true;
}
/* test edge u1,u2 against v0,v1 */
if (Intersection.edgeEdgeTest(v0, u1, u2, i0, i1, aX, aY)) {
return true;
}
/* test edge u2,u1 against v0,v1 */
if (Intersection.edgeEdgeTest(v0, u2, u0, i0, i1, aX, aY)) {
return true;
}
return false;
}
private static <region R>
boolean edgeEdgeTest(ArrayFloat<R> v0, ArrayFloat<R> u0,
ArrayFloat<R> u1, int i0, int i1,
float aX, float Ay)
reads R:*
{
float Bx = u0[i0] - u1[i0];
float By = u0[i1] - u1[i1];
float Cx = v0[i0] - u0[i0];
float Cy = v0[i1] - u0[i1];
float f = Ay * Bx - aX * By;
float d = By * Cx - Bx * Cy;
if ((f > 0 && d >= 0 && d <= f) || (f < 0 && d <= 0 && d >= f)) {
float e = aX * Cy - Ay * Cx;
if (f > 0) {
if (e >= 0 && e <= f)
return true;
} else {
if (e <= 0 && e >= f)
return true;
}
}
return false;
}
}