private static bool TryTetrahedronTriangle(ref Vector3 A, ref Vector3 B, ref Vector3 C, ref Vector3 A1, ref Vector3 B1, ref Vector3 C1, ref Vector3 A2, ref Vector3 B2, ref Vector3 C2, float errorTolerance, ref Vector3 otherPoint, out PairSimplex simplex, out Vector3 point) { //Note that there may be some extra terms that can be removed from this process. //Some conditions could use less parameters, since it is known that the origin //is not 'behind' BC or AC. simplex = new PairSimplex(); point = new Vector3(); Vector3 ab, ac; Vector3.Subtract(ref B, ref A, out ab); Vector3.Subtract(ref C, ref A, out ac); Vector3 normal; Vector3.Cross(ref ab, ref ac, out normal); float AdotN, ADdotN; Vector3 AD; Vector3.Subtract(ref otherPoint, ref A, out AD); Vector3.Dot(ref A, ref normal, out AdotN); Vector3.Dot(ref AD, ref normal, out ADdotN); //If (-A * N) * (AD * N) < 0, D and O are on opposite sides of the triangle. if (AdotN * ADdotN >= -Toolbox.Epsilon * errorTolerance) { //The point we are comparing against the triangle is 0,0,0, so instead of storing an "A->P" vector, //just use -A. //Same for B->, C->P... //Check to see if it's outside A. //TODO: Note that in a boolean-style GJK, it shouldn't be possible to be outside A. float AdotAB, AdotAC; Vector3.Dot(ref ab, ref A, out AdotAB); Vector3.Dot(ref ac, ref A, out AdotAC); AdotAB = -AdotAB; AdotAC = -AdotAC; if (AdotAC <= 0f && AdotAB <= 0) { //It is A! simplex.State = SimplexState.Point; simplex.A = A; simplex.U = 1; simplex.SimplexA.A = A1; simplex.SimplexB.A = A2; point = A; return(true); } //Check to see if it's outside B. //TODO: Note that in a boolean-style GJK, it shouldn't be possible to be outside B. float BdotAB, BdotAC; Vector3.Dot(ref ab, ref B, out BdotAB); Vector3.Dot(ref ac, ref B, out BdotAC); BdotAB = -BdotAB; BdotAC = -BdotAC; if (BdotAB >= 0f && BdotAC <= BdotAB) { //It is B! simplex.State = SimplexState.Point; simplex.A = B; simplex.U = 1; simplex.SimplexA.A = B1; simplex.SimplexB.A = B2; point = B; return(true); } //Check to see if it's outside AB. float vc = AdotAB * BdotAC - BdotAB * AdotAC; if (vc <= 0 && AdotAB > 0 && BdotAB < 0) //Note > and < instead of => <=; avoids possibly division by zero { simplex.State = SimplexState.Segment; simplex.V = AdotAB / (AdotAB - BdotAB); simplex.U = 1 - simplex.V; simplex.A = A; simplex.B = B; simplex.SimplexA.A = A1; simplex.SimplexB.A = A2; simplex.SimplexA.B = B1; simplex.SimplexB.B = B2; Vector3.Multiply(ref ab, simplex.V, out point); Vector3.Add(ref point, ref A, out point); return(true); } //Check to see if it's outside C. //TODO: Note that in a boolean-style GJK, it shouldn't be possible to be outside C. float CdotAB, CdotAC; Vector3.Dot(ref ab, ref C, out CdotAB); Vector3.Dot(ref ac, ref C, out CdotAC); CdotAB = -CdotAB; CdotAC = -CdotAC; if (CdotAC >= 0f && CdotAB <= CdotAC) { //It is C! simplex.State = SimplexState.Point; simplex.A = C; simplex.U = 1; simplex.SimplexA.A = C1; simplex.SimplexB.A = C2; point = C; return(true); } //Check if it's outside AC. //float AdotAB, AdotAC; //Vector3.Dot(ref ab, ref A, out AdotAB); //Vector3.Dot(ref ac, ref A, out AdotAC); //AdotAB = -AdotAB; //AdotAC = -AdotAC; float vb = CdotAB * AdotAC - AdotAB * CdotAC; if (vb <= 0f && AdotAC > 0f && CdotAC < 0f) //Note > instead of >= and < instead of <=; prevents bad denominator { simplex.State = SimplexState.Segment; simplex.A = A; simplex.B = C; simplex.SimplexA.A = A1; simplex.SimplexA.B = C1; simplex.SimplexB.A = A2; simplex.SimplexB.B = C2; simplex.V = AdotAC / (AdotAC - CdotAC); simplex.U = 1 - simplex.V; Vector3.Multiply(ref ac, simplex.V, out point); Vector3.Add(ref point, ref A, out point); return(true); } //Check if it's outside BC. //float BdotAB, BdotAC; //Vector3.Dot(ref ab, ref B, out BdotAB); //Vector3.Dot(ref ac, ref B, out BdotAC); //BdotAB = -BdotAB; //BdotAC = -BdotAC; float va = BdotAB * CdotAC - CdotAB * BdotAC; float d3d4; float d6d5; if (va <= 0f && (d3d4 = BdotAC - BdotAB) > 0f && (d6d5 = CdotAB - CdotAC) > 0f)//Note > instead of >= and < instead of <=; prevents bad denominator { simplex.State = SimplexState.Segment; simplex.A = B; simplex.B = C; simplex.SimplexA.A = B1; simplex.SimplexA.B = C1; simplex.SimplexB.A = B2; simplex.SimplexB.B = C2; simplex.V = d3d4 / (d3d4 + d6d5); simplex.U = 1 - simplex.V; Vector3 bc; Vector3.Subtract(ref C, ref B, out bc); Vector3.Multiply(ref bc, simplex.V, out point); Vector3.Add(ref point, ref B, out point); return(true); } //On the face of the triangle. simplex.A = A; simplex.B = B; simplex.C = C; simplex.SimplexA.A = A1; simplex.SimplexA.B = B1; simplex.SimplexA.C = C1; simplex.SimplexB.A = A2; simplex.SimplexB.B = B2; simplex.SimplexB.C = C2; simplex.State = SimplexState.Triangle; float denom = 1f / (va + vb + vc); simplex.W = vc * denom; simplex.V = vb * denom; simplex.U = 1 - simplex.V - simplex.W; Vector3.Multiply(ref ab, simplex.V, out point); Vector3 acw; Vector3.Multiply(ref ac, simplex.W, out acw); Vector3.Add(ref A, ref point, out point); Vector3.Add(ref point, ref acw, out point); return(true); } return(false); }
///<summary> /// Gets the point on the tetrahedron closest to the origin. ///</summary> ///<param name="point">Closest point to the origin.</param> ///<returns>Whether or not the tetrahedron encloses the origin.</returns> public bool GetPointOnTetrahedronClosestToOrigin(out Vector3 point) { //Thanks to the fact that D is new and that we know that the origin is within the extruded //triangular prism of ABC (and on the "D" side of ABC), //we can immediately ignore voronoi regions: //A, B, C, AC, AB, BC, ABC //and only consider: //D, DA, DB, DC, DAC, DCB, DBA //There is some overlap of calculations in this method, since DAC, DCB, and DBA are tested fully. PairSimplex minimumSimplex = new PairSimplex(); point = new Vector3(); float minimumDistance = float.MaxValue; PairSimplex candidate; float candidateDistance; Vector3 candidatePoint; if (TryTetrahedronTriangle(ref A, ref C, ref D, ref SimplexA.A, ref SimplexA.C, ref SimplexA.D, ref SimplexB.A, ref SimplexB.C, ref SimplexB.D, errorTolerance, ref B, out candidate, out candidatePoint)) { point = candidatePoint; minimumSimplex = candidate; minimumDistance = candidatePoint.LengthSquared(); } //Try BDC instead of CBD if (TryTetrahedronTriangle(ref B, ref D, ref C, ref SimplexA.B, ref SimplexA.D, ref SimplexA.C, ref SimplexB.B, ref SimplexB.D, ref SimplexB.C, errorTolerance, ref A, out candidate, out candidatePoint) && (candidateDistance = candidatePoint.LengthSquared()) < minimumDistance) { point = candidatePoint; minimumSimplex = candidate; minimumDistance = candidateDistance; } //Try ADB instead of BAD if (TryTetrahedronTriangle(ref A, ref D, ref B, ref SimplexA.A, ref SimplexA.D, ref SimplexA.B, ref SimplexB.A, ref SimplexB.D, ref SimplexB.B, errorTolerance, ref C, out candidate, out candidatePoint) && (candidateDistance = candidatePoint.LengthSquared()) < minimumDistance) { point = candidatePoint; minimumSimplex = candidate; minimumDistance = candidateDistance; } if (TryTetrahedronTriangle(ref A, ref B, ref C, ref SimplexA.A, ref SimplexA.B, ref SimplexA.C, ref SimplexB.A, ref SimplexB.B, ref SimplexB.C, errorTolerance, ref D, out candidate, out candidatePoint) && (candidateDistance = candidatePoint.LengthSquared()) < minimumDistance) { point = candidatePoint; minimumSimplex = candidate; minimumDistance = candidateDistance; } if (minimumDistance < float.MaxValue) { minimumSimplex.LocalTransformB = LocalTransformB; minimumSimplex.previousDistanceToClosest = previousDistanceToClosest; minimumSimplex.errorTolerance = errorTolerance; this = minimumSimplex; return(false); } return(true); }