/* Triangle/triangle intersection test routine, * by Tomas Moller, 1997. * See article "A Fast Triangle-Triangle Intersection Test", * Journal of Graphics Tools, 2(2), 1997 * * * Copyright (C) 1997 Tomas Möller * Copyright (C) 2000-2013 Raven Software, Inc. * Copyright (C) 2001-2013 Activision, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * * int tri_tri_intersect(float V0[3],float V1[3],float V2[3], * float U0[3],float U1[3],float U2[3]) * * parameters: vertices of triangle 1: V0,V1,V2 * vertices of triangle 2: U0,U1,U2 * result : returns 1 if the triangles intersect, otherwise 0 * */ #include "tri_coll_test.h" /* if USE_EPSILON_TEST is true then we do a check: if |dv|b) \ { \ float c; \ c=a; \ a=b; \ b=c; \ } #define ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1) \ isect0=VV0+(VV1-VV0)*D0/(D0-D1); \ isect1=VV0+(VV2-VV0)*D0/(D0-D2); #define COMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1) \ 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 */ \ ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \ } \ else if(D0D2>0.0f) \ { \ /* here we know that d0d1<=0.0 */ \ ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \ } \ else if(D1*D2>0.0f || D0!=0.0f) \ { \ /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1); \ } \ else if(D1!=0.0f) \ { \ ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \ } \ else if(D2!=0.0f) \ { \ ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \ } \ else \ { \ /* triangles are coplanar */ \ return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \ } /* this edge to edge test is based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202 */ #define EDGE_EDGE_TEST(V0,U0,U1) \ Bx=U0[i0]-U1[i0]; \ By=U0[i1]-U1[i1]; \ Cx=V0[i0]-U0[i0]; \ Cy=V0[i1]-U0[i1]; \ f=Ay*Bx-Ax*By; \ d=By*Cx-Bx*Cy; \ if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \ { \ e=Ax*Cy-Ay*Cx; \ if(f>0) \ { \ if(e>=0 && e<=f) return qtrue; \ } \ else \ { \ if(e<=0 && e>=f) return qtrue; \ } \ } #define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \ { \ float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \ Ax=V1[i0]-V0[i0]; \ Ay=V1[i1]-V0[i1]; \ /* test edge U0,U1 against V0,V1 */ \ EDGE_EDGE_TEST(V0,U0,U1); \ /* test edge U1,U2 against V0,V1 */ \ EDGE_EDGE_TEST(V0,U1,U2); \ /* test edge U2,U1 against V0,V1 */ \ EDGE_EDGE_TEST(V0,U2,U0); \ } #define POINT_IN_TRI(V0,U0,U1,U2) \ { \ 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) \ { \ if(d0*d2>0.0) return qtrue; \ } \ } qboolean coplanar_tri_tri(vec3_t N,vec3_t V0,vec3_t V1,vec3_t V2, vec3_t U0,vec3_t U1,vec3_t U2) { vec3_t A; short i0,i1; /* first project onto an axis-aligned plane, that maximizes the area */ /* of the triangles, compute indices: i0,i1. */ A[0]=fabs(N[0]); A[1]=fabs(N[1]); A[2]=fabs(N[2]); if(A[0]>A[1]) { if(A[0]>A[2]) { i0=1; /* A[0] is greatest */ i1=2; } else { i0=0; /* A[2] is greatest */ i1=1; } } else /* A[0]<=A[1] */ { if(A[2]>A[1]) { 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 */ EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2); EDGE_AGAINST_TRI_EDGES(V1,V2,U0,U1,U2); EDGE_AGAINST_TRI_EDGES(V2,V0,U0,U1,U2); /* finally, test if tri1 is totally contained in tri2 or vice versa */ POINT_IN_TRI(V0,U0,U1,U2); POINT_IN_TRI(U0,V0,V1,V2); return qfalse; } qboolean tri_tri_intersect(vec3_t V0,vec3_t V1,vec3_t V2, vec3_t U0,vec3_t U1,vec3_t U2) { vec3_t E1,E2; vec3_t N1,N2; float d1,d2; float du0,du1,du2,dv0,dv1,dv2; vec3_t D; float isect1[2], isect2[2]; float du0du1,du0du2,dv0dv1,dv0dv2; short index; float vp0,vp1,vp2; float up0,up1,up2; float b,c,max; /* compute plane equation of triangle(V0,V1,V2) */ SUB(E1,V1,V0); SUB(E2,V2,V0); CROSS(N1,E1,E2); d1=-DOT(N1,V0); /* plane equation 1: N1.X+d1=0 */ /* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/ du0=DOT(N1,U0)+d1; du1=DOT(N1,U1)+d1; du2=DOT(N1,U2)+d1; /* coplanarity robustness check */ #if USE_EPSILON_TEST if(fabs(du0)0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */ return qfalse; /* no intersection occurs */ /* compute plane of triangle (U0,U1,U2) */ SUB(E1,U1,U0); SUB(E2,U2,U0); CROSS(N2,E1,E2); d2=-DOT(N2,U0); /* plane equation 2: N2.X+d2=0 */ /* put V0,V1,V2 into plane equation 2 */ dv0=DOT(N2,V0)+d2; dv1=DOT(N2,V1)+d2; dv2=DOT(N2,V2)+d2; #if USE_EPSILON_TEST if(fabs(dv0)0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */ return qfalse; /* no intersection occurs */ /* compute direction of intersection line */ CROSS(D,N1,N2); /* compute and index to the largest component of D */ max=fabs(D[0]); index=0; b=fabs(D[1]); c=fabs(D[2]); if(b>max) max=b,index=1; if(c>max) max=c,index=2; /* this is the simplified projection onto L*/ vp0=V0[index]; vp1=V1[index]; vp2=V2[index]; up0=U0[index]; up1=U1[index]; up2=U2[index]; /* compute interval for triangle 1 */ COMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,isect1[0],isect1[1]); /* compute interval for triangle 2 */ COMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,isect2[0],isect2[1]); SORT(isect1[0],isect1[1]); SORT(isect2[0],isect2[1]); if(isect1[1] 0.001f ) { float s = -( (v2v2*DotProduct( v1, start_dif )) - (v1v2*DotProduct( v2, start_dif )) ) / denom; float t = ( (v1v1*DotProduct( v2, start_dif )) - (v1v2*DotProduct( v1, start_dif )) ) / denom; qboolean done = qtrue; if ( s < 0 ) { done = qfalse; s = 0;// and see note below } if ( s > 1 ) { done = qfalse; s = 1;// and see note below } if ( t < 0 ) { done = qfalse; t = 0;// and see note below } if ( t > 1 ) { done = qfalse; t = 1;// and see note below } //vec close_pnt1 = start1 + s * v1 VectorMA( start1, s, v1, close_pnt1 ); //vec close_pnt2 = start2 + t * v2 VectorMA( start2, t, v2, close_pnt2 ); current_dist = Distance( close_pnt1, close_pnt2 ); //now, if none of those if's fired, you are done. if ( done ) { return current_dist; } //If they did fire, then we need to do some additional tests. //What we are gonna do is see if we can find a shorter distance than the above //involving the endpoints. } else { //******start here for paralell lines with current_dist = infinity**** current_dist = Q3_INFINITE; } //test 2 close_pnts first /* G_FindClosestPointOnLineSegment( start1, end1, close_pnt2, new_pnt ); new_dist = Distance( close_pnt2, new_pnt ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( new_pnt, close_pnt1 ); VectorCopy( close_pnt2, close_pnt2 ); current_dist = new_dist; } G_FindClosestPointOnLineSegment( start2, end2, close_pnt1, new_pnt ); new_dist = Distance( close_pnt1, new_pnt ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( close_pnt1, close_pnt1 ); VectorCopy( new_pnt, close_pnt2 ); current_dist = new_dist; } */ //test all the endpoints new_dist = Distance( start1, start2 ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( start1, close_pnt1 ); VectorCopy( start2, close_pnt2 ); current_dist = new_dist; } new_dist = Distance( start1, end2 ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( start1, close_pnt1 ); VectorCopy( end2, close_pnt2 ); current_dist = new_dist; } new_dist = Distance( end1, start2 ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( end1, close_pnt1 ); VectorCopy( start2, close_pnt2 ); current_dist = new_dist; } new_dist = Distance( end1, end2 ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( end1, close_pnt1 ); VectorCopy( end2, close_pnt2 ); current_dist = new_dist; } //Then we have 4 more point / segment tests G_FindClosestPointOnLineSegment( start2, end2, start1, new_pnt ); new_dist = Distance( start1, new_pnt ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( start1, close_pnt1 ); VectorCopy( new_pnt, close_pnt2 ); current_dist = new_dist; } G_FindClosestPointOnLineSegment( start2, end2, end1, new_pnt ); new_dist = Distance( end1, new_pnt ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( end1, close_pnt1 ); VectorCopy( new_pnt, close_pnt2 ); current_dist = new_dist; } G_FindClosestPointOnLineSegment( start1, end1, start2, new_pnt ); new_dist = Distance( start2, new_pnt ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( new_pnt, close_pnt1 ); VectorCopy( start2, close_pnt2 ); current_dist = new_dist; } G_FindClosestPointOnLineSegment( start1, end1, end2, new_pnt ); new_dist = Distance( end2, new_pnt ); if ( new_dist < current_dist ) {//then update close_pnt1 close_pnt2 and current_dist VectorCopy( new_pnt, close_pnt1 ); VectorCopy( end2, close_pnt2 ); current_dist = new_dist; } return current_dist; }