bool lineIntersectsTriangle(Line3 const & line, Vector3 const & v0, Vector3 const & edge01, Vector3 const & edge02, Real * time) { // The code is taken from the ray-triangle intersection test in Dave Eberly's Wild Magic library, v5.3, released under the // Boost license: http://www.boost.org/LICENSE_1_0.txt . static Real const EPS = 1e-30f; Vector3 diff = line.getPoint() - v0; Vector3 normal = edge01.cross(edge02); // Solve Q + t*D = b1*E1 + b2*E2 (Q = diff, D = line direction, E1 = edge01, E2 = edge02, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) Real DdN = line.getDirection().dot(normal); int sign; if (DdN > EPS) sign = 1; else if (DdN < -EPS) { sign = -1; DdN = -DdN; } else { // Line and triangle are parallel, call it a "no intersection" even if the line does intersect return false; } Real DdQxE2 = sign * line.getDirection().dot(diff.cross(edge02)); if (DdQxE2 >= 0) { Real DdE1xQ = sign * line.getDirection().dot(edge01.cross(diff)); if (DdE1xQ >= 0) { if (DdQxE2 + DdE1xQ <= DdN) { // Line intersects triangle Real QdN = -sign * diff.dot(normal); if (time) *time = QdN / DdN; return true; } // else: b1 + b2 > 1, no intersection } // else: b2 < 0, no intersection } // else: b1 < 0, no intersection return false; }
Real closestPtLineTriangle(Line3 const & line, Vector3 const & v0, Vector3 const & edge01, Vector3 const & edge02, Real & s, Vector3 & c1, Vector3 & c2) { if (lineIntersectsTriangle(line, v0, edge01, edge02, &s)) { c1 = c2 = line.getPoint() + s * line.getDirection(); return 0; } // Either (1) the line is not parallel to the triangle and the point of // intersection of the line and the plane of the triangle is outside the // triangle or (2) the line and triangle are parallel. Regardless, the // closest point on the triangle is on an edge of the triangle. Compare // the line to all three edges of the triangle. Real min_sqdist = -1; Vector3 v[3] = { v0, v0 + edge01, v0 + edge02 }; Vector3 tmp_c1, tmp_c2; float tmp_s, t; for (int i0 = 2, i1 = 0; i1 < 3; i0 = i1++) { Real sqdist = closestPtSegmentLine(v[i0], v[i1], line.getPoint(), line.getPoint() + line.getDirection(), t, tmp_s, tmp_c2, tmp_c1); if (min_sqdist < 0 || sqdist < min_sqdist) { min_sqdist = sqdist; s = tmp_s; c1 = tmp_c1; c2 = tmp_c2; } } return min_sqdist; }