Browse Source

Bugfix: Cast sphere vs triangle could return false positive (#888)

Fixes #886
Jorrit Rouwe 1 year ago
parent
commit
a70c8acdfa

+ 9 - 2
Jolt/Geometry/ClosestPoint.h

@@ -14,7 +14,8 @@ namespace ClosestPoint
 {
 	/// Compute barycentric coordinates of closest point to origin for infinite line defined by (inA, inB)
 	/// Point can then be computed as inA * outU + inB * outV
-	inline void GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV)
+	/// Returns false if the points inA, inB do not form a line (are at the same point)
+	inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV)
 	{
 		Vec3 ab = inB - inA;
 		float denominator = ab.LengthSq();
@@ -33,17 +34,20 @@ namespace ClosestPoint
 				outU = 0.0f;
 				outV = 1.0f;
 			}
+			return false;
 		}
 		else
 		{
 			outV = -inA.Dot(ab) / denominator;
 			outU = 1.0f - outV;
 		}
+		return true;
 	}
 
 	/// Compute barycentric coordinates of closest point to origin for plane defined by (inA, inB, inC)
 	/// Point can then be computed as inA * outU + inB * outV + inC * outW
-	inline void GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, float &outU, float &outV, float &outW)
+	/// Returns false if the points inA, inB, inC do not form a plane (are on the same line or at the same point)
+	inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, float &outU, float &outV, float &outW)
 	{
 		// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Barycentric Coordinates)
 		// With p = 0
@@ -77,6 +81,7 @@ namespace ClosestPoint
 					GetBaryCentricCoordinates(inA, inC, outU, outW);
 					outV = 0.0f;
 				}
+				return false;
 			}
 			else
 			{
@@ -106,6 +111,7 @@ namespace ClosestPoint
 					GetBaryCentricCoordinates(inB, inC, outV, outW);
 					outU = 0.0f;
 				}
+				return false;
 			}
 			else
 			{
@@ -116,6 +122,7 @@ namespace ClosestPoint
 				outW = 1.0f - outU - outV;
 			}
 		}
+		return true;
 	}
 
 	/// Get the closest point to the origin of line (inA, inB)

+ 2 - 2
Jolt/Physics/Collision/CastSphereVsTriangles.cpp

@@ -183,8 +183,8 @@ void CastSphereVsTriangles::Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8
 
 				// Check if this is an interior point
 				float u, v, w;
-				ClosestPoint::GetBaryCentricCoordinates(v0 - p, v1 - p, v2 - p, u, v, w);
-				if (u >= 0.0f && v >= 0.0f && w >= 0.0f)
+				if (ClosestPoint::GetBaryCentricCoordinates(v0 - p, v1 - p, v2 - p, u, v, w)
+					&& u >= 0.0f && v >= 0.0f && w >= 0.0f)
 				{
 					// Interior point, we found the collision point. We don't need to check active edges.
 					AddHit(back_facing, inSubShapeID2, plane_intersection, p, p, back_facing? triangle_normal : -triangle_normal);

+ 14 - 0
UnitTests/Physics/CastShapeTests.cpp

@@ -15,6 +15,7 @@
 #include <Jolt/Physics/Collision/Shape/BoxShape.h>
 #include <Jolt/Physics/Collision/ShapeFilter.h>
 #include <Jolt/Physics/Collision/CollisionDispatch.h>
+#include <Jolt/Physics/Collision/CastSphereVsTriangles.h>
 #include "PhysicsTestContext.h"
 #include "Layers.h"
 
@@ -345,4 +346,17 @@ TEST_SUITE("CastShapeTests")
 		// Ensure that we indeed stopped after the first hit
 		CHECK(collector2.mNumHits == 1);
 	}
+
+	// Test a problem case where a sphere cast would incorrectly hit a degenerate triangle (see: https://github.com/jrouwe/JoltPhysics/issues/886)
+	TEST_CASE("TestCastSphereVsDegenerateTriangle")
+	{
+		AllHitCollisionCollector<CastShapeCollector> collector;
+		SphereShape sphere(0.2f);
+		sphere.SetEmbedded();
+		ShapeCast cast(&sphere, Vec3::sReplicate(1.0f), Mat44::sTranslation(Vec3(14.8314590f, 8.19055080f, -4.30825043f)), Vec3(-0.0988006592f, 5.96046448e-08f, 0.000732421875f));
+		ShapeCastSettings settings;
+		CastSphereVsTriangles caster(cast, settings, Vec3::sReplicate(1.0f), Mat44::sIdentity(), { }, collector);
+		caster.Cast(Vec3(14.5536213f, 10.5973721f, -0.00600051880f), Vec3(14.5536213f, 10.5969315f, -3.18638134f), Vec3(14.5536213f, 10.5969315f, -5.18637228f), 0b111, SubShapeID());
+		CHECK(!collector.HadHit());
+	}
 }