|
@@ -9,6 +9,8 @@
|
|
#include <Jolt/Geometry/GJKClosestPoint.h>
|
|
#include <Jolt/Geometry/GJKClosestPoint.h>
|
|
#include <Jolt/Geometry/EPAConvexHullBuilder.h>
|
|
#include <Jolt/Geometry/EPAConvexHullBuilder.h>
|
|
|
|
|
|
|
|
+//#define JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+
|
|
JPH_NAMESPACE_BEGIN
|
|
JPH_NAMESPACE_BEGIN
|
|
|
|
|
|
/// Implementation of Expanding Polytope Algorithm as described in:
|
|
/// Implementation of Expanding Polytope Algorithm as described in:
|
|
@@ -195,6 +197,12 @@ public:
|
|
// Create hull out of the initial points
|
|
// Create hull out of the initial points
|
|
JPH_ASSERT(support_points.mY.size() >= 3);
|
|
JPH_ASSERT(support_points.mY.size() >= 3);
|
|
EPAConvexHullBuilder hull(support_points.mY);
|
|
EPAConvexHullBuilder hull(support_points.mY);
|
|
|
|
+#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
|
|
+ hull.DrawLabel("Build initial hull");
|
|
|
|
+#endif
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("Init: num_points = %d", support_points.mY.size());
|
|
|
|
+#endif
|
|
hull.Initialize(0, 1, 2);
|
|
hull.Initialize(0, 1, 2);
|
|
for (typename Points::size_type i = 3; i < support_points.mY.size(); ++i)
|
|
for (typename Points::size_type i = 3; i < support_points.mY.size(); ++i)
|
|
{
|
|
{
|
|
@@ -212,6 +220,10 @@ public:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
|
|
+ hull.DrawLabel("Ensure origin in hull");
|
|
|
|
+#endif
|
|
|
|
+
|
|
// Loop until we are sure that the origin is inside the hull
|
|
// Loop until we are sure that the origin is inside the hull
|
|
for (;;)
|
|
for (;;)
|
|
{
|
|
{
|
|
@@ -235,6 +247,17 @@ public:
|
|
if (t->mClosestLenSq >= 0.0f)
|
|
if (t->mClosestLenSq >= 0.0f)
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
|
|
+ hull.DrawLabel("Next iteration");
|
|
|
|
+#endif
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("EncapsulateOrigin: verts = (%d, %d, %d), closest_dist_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)",
|
|
|
|
+ t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx,
|
|
|
|
+ t->mClosestLenSq,
|
|
|
|
+ t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(),
|
|
|
|
+ t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ());
|
|
|
|
+#endif
|
|
|
|
+
|
|
// Remove the triangle from the queue before we start adding new ones (which may result in a new closest triangle at the front of the queue)
|
|
// Remove the triangle from the queue before we start adding new ones (which may result in a new closest triangle at the front of the queue)
|
|
hull.PopClosestTriangleFromQueue();
|
|
hull.PopClosestTriangleFromQueue();
|
|
|
|
|
|
@@ -263,11 +286,16 @@ public:
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
|
|
+ hull.DrawLabel("Main algorithm");
|
|
|
|
+#endif
|
|
|
|
+
|
|
// Current closest distance to origin
|
|
// Current closest distance to origin
|
|
float closest_dist_sq = FLT_MAX;
|
|
float closest_dist_sq = FLT_MAX;
|
|
|
|
|
|
// Remember last good triangle
|
|
// Remember last good triangle
|
|
- Triangle *last = nullptr;
|
|
|
|
|
|
+ Triangle *before_last = nullptr, *last = nullptr;
|
|
|
|
+ float before_last_dist_sq = FLT_MAX, last_dist_sq = FLT_MAX;
|
|
|
|
|
|
// Loop until closest point found
|
|
// Loop until closest point found
|
|
do
|
|
do
|
|
@@ -282,15 +310,20 @@ public:
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
|
|
+ hull.DrawLabel("Next iteration");
|
|
|
|
+#endif
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("FindClosest: verts = (%d, %d, %d), closest_len_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)",
|
|
|
|
+ t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx,
|
|
|
|
+ t->mClosestLenSq,
|
|
|
|
+ t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(),
|
|
|
|
+ t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ());
|
|
|
|
+#endif
|
|
// Check if next triangle is further away than closest point, we've found the closest point
|
|
// Check if next triangle is further away than closest point, we've found the closest point
|
|
if (t->mClosestLenSq >= closest_dist_sq)
|
|
if (t->mClosestLenSq >= closest_dist_sq)
|
|
break;
|
|
break;
|
|
|
|
|
|
- // Replace last good with this triangle
|
|
|
|
- if (last != nullptr)
|
|
|
|
- hull.FreeTriangle(last);
|
|
|
|
- last = t;
|
|
|
|
-
|
|
|
|
// Add support point in direction of normal of the plane
|
|
// Add support point in direction of normal of the plane
|
|
// Note that the article uses the closest point between the origin and plane, but this always has the exact same direction as the normal (if the origin is behind the plane)
|
|
// Note that the article uses the closest point between the origin and plane, but this always has the exact same direction as the normal (if the origin is behind the plane)
|
|
// and this way we do less calculations and lose less precision
|
|
// and this way we do less calculations and lose less precision
|
|
@@ -306,8 +339,21 @@ public:
|
|
return false;
|
|
return false;
|
|
|
|
|
|
// Get the distance squared (along normal) to the support point
|
|
// Get the distance squared (along normal) to the support point
|
|
- float dist_sq = dot * dot / t->mNormal.LengthSq();
|
|
|
|
|
|
+ float dist_sq = Square(dot) / t->mNormal.LengthSq();
|
|
|
|
+
|
|
|
|
+ // Replace last good with this triangle and shift last good to before last
|
|
|
|
+ if (before_last != nullptr)
|
|
|
|
+ hull.FreeTriangle(before_last);
|
|
|
|
+ before_last = last;
|
|
|
|
+ last = t;
|
|
|
|
+ before_last_dist_sq = last_dist_sq;
|
|
|
|
+ last_dist_sq = dist_sq;
|
|
|
|
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("FindClosest: w = (%g, %g, %g), dot = %g, dist_sq = %g",
|
|
|
|
+ w.GetX(), w.GetY(), w.GetZ(),
|
|
|
|
+ dot, dist_sq);
|
|
|
|
+#endif
|
|
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
// Draw the point that we're adding
|
|
// Draw the point that we're adding
|
|
hull.DrawMarker(w, Color::sPurple, 1.0f);
|
|
hull.DrawMarker(w, Color::sPurple, 1.0f);
|
|
@@ -317,19 +363,34 @@ public:
|
|
|
|
|
|
// If the error became small enough, we've converged
|
|
// If the error became small enough, we've converged
|
|
if (dist_sq - t->mClosestLenSq < t->mClosestLenSq * inTolerance)
|
|
if (dist_sq - t->mClosestLenSq < t->mClosestLenSq * inTolerance)
|
|
|
|
+ {
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("Converged");
|
|
|
|
+#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
|
|
|
|
// Keep track of the minimum distance
|
|
// Keep track of the minimum distance
|
|
closest_dist_sq = min(closest_dist_sq, dist_sq);
|
|
closest_dist_sq = min(closest_dist_sq, dist_sq);
|
|
|
|
|
|
// If the triangle thinks this point is not front facing, we've reached numerical precision and we're done
|
|
// If the triangle thinks this point is not front facing, we've reached numerical precision and we're done
|
|
if (!t->IsFacing(w))
|
|
if (!t->IsFacing(w))
|
|
|
|
+ {
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("Not facing triangle");
|
|
|
|
+#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
|
|
|
|
// Add point to hull
|
|
// Add point to hull
|
|
EPAConvexHullBuilder::NewTriangles new_triangles;
|
|
EPAConvexHullBuilder::NewTriangles new_triangles;
|
|
if (!hull.AddPoint(t, new_index, closest_dist_sq, new_triangles))
|
|
if (!hull.AddPoint(t, new_index, closest_dist_sq, new_triangles))
|
|
|
|
+ {
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("Could not add point");
|
|
|
|
+#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
|
|
|
|
// If the hull is starting to form defects then we're reaching numerical precision and we have to stop
|
|
// If the hull is starting to form defects then we're reaching numerical precision and we have to stop
|
|
bool has_defect = false;
|
|
bool has_defect = false;
|
|
@@ -340,7 +401,12 @@ public:
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
if (has_defect)
|
|
if (has_defect)
|
|
|
|
+ {
|
|
|
|
+#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
|
|
+ Trace("Has defect");
|
|
|
|
+#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
while (hull.HasNextTriangle() && support_points.mY.size() < cMaxPoints);
|
|
while (hull.HasNextTriangle() && support_points.mY.size() < cMaxPoints);
|
|
|
|
|
|
@@ -348,6 +414,23 @@ public:
|
|
if (last == nullptr)
|
|
if (last == nullptr)
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
+ // Fall back to before last triangle if the last triangle is significantly worse.
|
|
|
|
+ // This fixes an issue that when the hull becomes invalid due to numerical precision issues and we did one step too many.
|
|
|
|
+ // Note that when colliding curved surfaces the last triangle describes the surface better and results in a better contact point,
|
|
|
|
+ // that's why we only do this when the last triangle is significantly worse than the before last triangle.
|
|
|
|
+ if (before_last_dist_sq < 0.81f * last_dist_sq) // 10%
|
|
|
|
+ {
|
|
|
|
+ JPH_ASSERT(before_last != nullptr);
|
|
|
|
+ last = before_last;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
|
|
|
|
+ hull.DrawLabel("Closest found");
|
|
|
|
+ hull.DrawWireTriangle(*last, Color::sWhite);
|
|
|
|
+ hull.DrawArrow(last->mCentroid, last->mCentroid + last->mNormal.NormalizedOr(Vec3::sZero()), Color::sWhite, 0.1f);
|
|
|
|
+ hull.DrawState();
|
|
|
|
+#endif
|
|
|
|
+
|
|
// Calculate penetration by getting the vector from the origin to the closest point on the triangle:
|
|
// Calculate penetration by getting the vector from the origin to the closest point on the triangle:
|
|
// distance = (centroid - origin) . normal / |normal|, closest = origin + distance * normal / |normal|
|
|
// distance = (centroid - origin) . normal / |normal|, closest = origin + distance * normal / |normal|
|
|
outV = (last->mCentroid.Dot(last->mNormal) / last->mNormal.LengthSq()) * last->mNormal;
|
|
outV = (last->mCentroid.Dot(last->mNormal) / last->mNormal.LengthSq()) * last->mNormal;
|