In a nutshell, we added a more reliable check, based on if the origin is in the GJK simplex, to determine if we are really intersecting and need to run EPA.
See also "Real-time Collision Detection with Implicit Objects" by Leif Olvang
Todo: integrate the simplex penetration check directly inside the Bullet btVoronoiSimplexSolver
and remove this temporary code from libCCD
Note, for large differences in shapes, use double precision build!
This commit is contained in:
erwincoumans
2018-08-30 18:35:51 -07:00
parent 4f7dfc2069
commit ee9fca8c29
2 changed files with 965 additions and 221 deletions

View File

@@ -33,8 +33,8 @@ bool btGjkEpaPenetrationDepthSolver::calcPenDepth(btSimplexSolverInterface& simp
(void)simplexSolver; (void)simplexSolver;
btVector3 guessVectors[] = { btVector3 guessVectors[] = {
btVector3(transformB.getOrigin() - transformA.getOrigin()), btVector3(transformB.getOrigin() - transformA.getOrigin()).normalized(),
btVector3(transformA.getOrigin() - transformB.getOrigin()), btVector3(transformA.getOrigin() - transformB.getOrigin()).normalized(),
btVector3(0, 0, 1), btVector3(0, 0, 1),
btVector3(0, 1, 0), btVector3(0, 1, 0),
btVector3(1, 0, 0), btVector3(1, 0, 0),

View File

@@ -1,4 +1,4 @@
/* /*
Bullet Continuous Collision Detection and Physics Library Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
@@ -83,6 +83,593 @@ void btGjkPairDetector::getClosestPoints(const ClosestPointInput& input,Result&
getClosestPointsNonVirtual(input,output,debugDraw); getClosestPointsNonVirtual(input,output,debugDraw);
} }
static void btComputeSupport(const btConvexShape* convexA, const btTransform& localTransA, const btConvexShape* convexB, const btTransform& localTransB, const btVector3& dir, bool check2d, btVector3& supAworld, btVector3& supBworld, btVector3& aMinb)
{
btVector3 seperatingAxisInA = (dir)* localTransA.getBasis();
btVector3 seperatingAxisInB = (-dir)* localTransB.getBasis();
btVector3 pInANoMargin = convexA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
btVector3 qInBNoMargin = convexB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);
btVector3 pInA = pInANoMargin;
btVector3 qInB = qInBNoMargin;
supAworld = localTransA(pInA);
supBworld = localTransB(qInB);
if (check2d)
{
supAworld[2] = 0.f;
supBworld[2] = 0.f;
}
aMinb = supAworld - supBworld;
}
struct btSupportVector
{
btVector3 v; //!< Support point in minkowski sum
btVector3 v1; //!< Support point in obj1
btVector3 v2; //!< Support point in obj2
};
struct btSimplex
{
btSupportVector ps[4];
int last; //!< index of last added point
};
static btVector3 ccd_vec3_origin(0, 0, 0);
inline void btSimplexInit(btSimplex *s)
{
s->last = -1;
}
inline int btSimplexSize(const btSimplex *s)
{
return s->last + 1;
}
inline const btSupportVector *btSimplexPoint(const btSimplex *s, int idx)
{
// here is no check on boundaries
return &s->ps[idx];
}
inline void btSupportCopy(btSupportVector *d, const btSupportVector *s)
{
*d = *s;
}
inline void btVec3Copy(btVector3 *v, const btVector3* w)
{
*v = *w;
}
inline void ccdVec3Add(btVector3*v, const btVector3*w)
{
v->m_floats[0] += w->m_floats[0];
v->m_floats[1] += w->m_floats[1];
v->m_floats[2] += w->m_floats[2];
}
inline void ccdVec3Sub(btVector3 *v, const btVector3 *w)
{
*v -= *w;
}
inline void btVec3Sub2(btVector3 *d, const btVector3 *v, const btVector3 *w)
{
*d = (*v) - (*w);
}
inline btScalar btVec3Dot(const btVector3 *a, const btVector3 *b)
{
btScalar dot;
dot = a->dot(*b);
return dot;
}
inline btScalar ccdVec3Dist2(const btVector3 *a, const btVector3*b)
{
btVector3 ab;
btVec3Sub2(&ab, a, b);
return btVec3Dot(&ab, &ab);
}
inline void btVec3Scale(btVector3 *d, btScalar k)
{
d->m_floats[0] *= k;
d->m_floats[1] *= k;
d->m_floats[2] *= k;
}
inline void btVec3Cross(btVector3 *d, const btVector3 *a, const btVector3 *b)
{
d->m_floats[0] = (a->m_floats[1] * b->m_floats[2]) - (a->m_floats[2] * b->m_floats[1]);
d->m_floats[1] = (a->m_floats[2] * b->m_floats[0]) - (a->m_floats[0] * b->m_floats[2]);
d->m_floats[2] = (a->m_floats[0] * b->m_floats[1]) - (a->m_floats[1] * b->m_floats[0]);
}
inline void btTripleCross(const btVector3 *a, const btVector3 *b,
const btVector3 *c, btVector3 *d)
{
btVector3 e;
btVec3Cross(&e, a, b);
btVec3Cross(d, &e, c);
}
inline int ccdEq(btScalar _a, btScalar _b)
{
btScalar ab;
btScalar a, b;
ab = btFabs(_a - _b);
if (btFabs(ab) < SIMD_EPSILON)
return 1;
a = btFabs(_a);
b = btFabs(_b);
if (b > a) {
return ab < SIMD_EPSILON * b;
}
else {
return ab < SIMD_EPSILON * a;
}
}
btScalar ccdVec3X(const btVector3* v)
{
return v->x();
}
btScalar ccdVec3Y(const btVector3* v)
{
return v->y();
}
btScalar ccdVec3Z(const btVector3* v)
{
return v->z();
}
inline int btVec3Eq(const btVector3 *a, const btVector3 *b)
{
return ccdEq(ccdVec3X(a), ccdVec3X(b))
&& ccdEq(ccdVec3Y(a), ccdVec3Y(b))
&& ccdEq(ccdVec3Z(a), ccdVec3Z(b));
}
inline void btSimplexAdd(btSimplex *s, const btSupportVector *v)
{
// here is no check on boundaries in sake of speed
++s->last;
btSupportCopy(s->ps + s->last, v);
}
inline void btSimplexSet(btSimplex *s, size_t pos, const btSupportVector *a)
{
btSupportCopy(s->ps + pos, a);
}
inline void btSimplexSetSize(btSimplex *s, int size)
{
s->last = size - 1;
}
inline const btSupportVector *ccdSimplexLast(const btSimplex *s)
{
return btSimplexPoint(s, s->last);
}
inline int ccdSign(btScalar val)
{
if (btFuzzyZero(val)) {
return 0;
}
else if (val < btScalar(0)) {
return -1;
}
return 1;
}
inline btScalar btVec3PointSegmentDist2(const btVector3 *P,
const btVector3 *x0,
const btVector3 *b,
btVector3 *witness)
{
// The computation comes from solving equation of segment:
// S(t) = x0 + t.d
// where - x0 is initial point of segment
// - d is direction of segment from x0 (|d| > 0)
// - t belongs to <0, 1> interval
//
// Than, distance from a segment to some point P can be expressed:
// D(t) = |x0 + t.d - P|^2
// which is distance from any point on segment. Minimization
// of this function brings distance from P to segment.
// Minimization of D(t) leads to simple quadratic equation that's
// solving is straightforward.
//
// Bonus of this method is witness point for free.
btScalar dist, t;
btVector3 d, a;
// direction of segment
btVec3Sub2(&d, b, x0);
// precompute vector from P to x0
btVec3Sub2(&a, x0, P);
t = -btScalar(1.) * btVec3Dot(&a, &d);
t /= btVec3Dot(&d, &d);
if (t < btScalar(0) || btFuzzyZero(t)) {
dist = ccdVec3Dist2(x0, P);
if (witness)
btVec3Copy(witness, x0);
}
else if (t > btScalar(1) || ccdEq(t, btScalar(1))) {
dist = ccdVec3Dist2(b, P);
if (witness)
btVec3Copy(witness, b);
}
else {
if (witness) {
btVec3Copy(witness, &d);
btVec3Scale(witness, t);
ccdVec3Add(witness, x0);
dist = ccdVec3Dist2(witness, P);
}
else {
// recycling variables
btVec3Scale(&d, t);
ccdVec3Add(&d, &a);
dist = btVec3Dot(&d, &d);
}
}
return dist;
}
btScalar btVec3PointTriDist2(const btVector3 *P,
const btVector3 *x0, const btVector3 *B,
const btVector3 *C,
btVector3 *witness)
{
// Computation comes from analytic expression for triangle (x0, B, C)
// T(s, t) = x0 + s.d1 + t.d2, where d1 = B - x0 and d2 = C - x0 and
// Then equation for distance is:
// D(s, t) = | T(s, t) - P |^2
// This leads to minimization of quadratic function of two variables.
// The solution from is taken only if s is between 0 and 1, t is
// between 0 and 1 and t + s < 1, otherwise distance from segment is
// computed.
btVector3 d1, d2, a;
double u, v, w, p, q, r;
double s, t, dist, dist2;
btVector3 witness2;
btVec3Sub2(&d1, B, x0);
btVec3Sub2(&d2, C, x0);
btVec3Sub2(&a, x0, P);
u = btVec3Dot(&a, &a);
v = btVec3Dot(&d1, &d1);
w = btVec3Dot(&d2, &d2);
p = btVec3Dot(&a, &d1);
q = btVec3Dot(&a, &d2);
r = btVec3Dot(&d1, &d2);
s = (q * r - w * p) / (w * v - r * r);
t = (-s * r - q) / w;
if ((btFuzzyZero(s) || s > btScalar(0))
&& (ccdEq(s, btScalar(1)) || s < btScalar(1))
&& (btFuzzyZero(t) || t > btScalar(0))
&& (ccdEq(t, btScalar(1)) || t < btScalar(1))
&& (ccdEq(t + s, btScalar(1)) || t + s < btScalar(1))) {
if (witness)
{
btVec3Scale(&d1, s);
btVec3Scale(&d2, t);
btVec3Copy(witness, x0);
ccdVec3Add(witness, &d1);
ccdVec3Add(witness, &d2);
dist = ccdVec3Dist2(witness, P);
}
else
{
dist = s * s * v;
dist += t * t * w;
dist += btScalar(2.) * s * t * r;
dist += btScalar(2.) * s * p;
dist += btScalar(2.) * t * q;
dist += u;
}
}
else {
dist = btVec3PointSegmentDist2(P, x0, B, witness);
dist2 = btVec3PointSegmentDist2(P, x0, C, &witness2);
if (dist2 < dist) {
dist = dist2;
if (witness)
btVec3Copy(witness, &witness2);
}
dist2 = btVec3PointSegmentDist2(P, B, C, &witness2);
if (dist2 < dist) {
dist = dist2;
if (witness)
btVec3Copy(witness, &witness2);
}
}
return dist;
}
static int btDoSimplex2(btSimplex *simplex, btVector3 *dir)
{
const btSupportVector *A, *B;
btVector3 AB, AO, tmp;
btScalar dot;
// get last added as A
A = ccdSimplexLast(simplex);
// get the other point
B = btSimplexPoint(simplex, 0);
// compute AB oriented segment
btVec3Sub2(&AB, &B->v, &A->v);
// compute AO vector
btVec3Copy(&AO, &A->v);
btVec3Scale(&AO, -btScalar(1));
// dot product AB . AO
dot = btVec3Dot(&AB, &AO);
// check if origin doesn't lie on AB segment
btVec3Cross(&tmp, &AB, &AO);
if (btFuzzyZero(btVec3Dot(&tmp, &tmp)) && dot > btScalar(0)) {
return 1;
}
// check if origin is in area where AB segment is
if (btFuzzyZero(dot) || dot < btScalar(0)) {
// origin is in outside are of A
btSimplexSet(simplex, 0, A);
btSimplexSetSize(simplex, 1);
btVec3Copy(dir, &AO);
}
else {
// origin is in area where AB segment is
// keep simplex untouched and set direction to
// AB x AO x AB
btTripleCross(&AB, &AO, &AB, dir);
}
return 0;
}
static int btDoSimplex3(btSimplex *simplex, btVector3 *dir)
{
const btSupportVector *A, *B, *C;
btVector3 AO, AB, AC, ABC, tmp;
btScalar dot, dist;
// get last added as A
A = ccdSimplexLast(simplex);
// get the other points
B = btSimplexPoint(simplex, 1);
C = btSimplexPoint(simplex, 0);
// check touching contact
dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &B->v, &C->v, 0);
if (btFuzzyZero(dist)) {
return 1;
}
// check if triangle is really triangle (has area > 0)
// if not simplex can't be expanded and thus no itersection is found
if (btVec3Eq(&A->v, &B->v) || btVec3Eq(&A->v, &C->v)) {
return -1;
}
// compute AO vector
btVec3Copy(&AO, &A->v);
btVec3Scale(&AO, -btScalar(1));
// compute AB and AC segments and ABC vector (perpendircular to triangle)
btVec3Sub2(&AB, &B->v, &A->v);
btVec3Sub2(&AC, &C->v, &A->v);
btVec3Cross(&ABC, &AB, &AC);
btVec3Cross(&tmp, &ABC, &AC);
dot = btVec3Dot(&tmp, &AO);
if (btFuzzyZero(dot) || dot > btScalar(0)) {
dot = btVec3Dot(&AC, &AO);
if (btFuzzyZero(dot) || dot > btScalar(0)) {
// C is already in place
btSimplexSet(simplex, 1, A);
btSimplexSetSize(simplex, 2);
btTripleCross(&AC, &AO, &AC, dir);
}
else {
dot = btVec3Dot(&AB, &AO);
if (btFuzzyZero(dot) || dot > btScalar(0)) {
btSimplexSet(simplex, 0, B);
btSimplexSet(simplex, 1, A);
btSimplexSetSize(simplex, 2);
btTripleCross(&AB, &AO, &AB, dir);
}
else {
btSimplexSet(simplex, 0, A);
btSimplexSetSize(simplex, 1);
btVec3Copy(dir, &AO);
}
}
}
else {
btVec3Cross(&tmp, &AB, &ABC);
dot = btVec3Dot(&tmp, &AO);
if (btFuzzyZero(dot) || dot > btScalar(0))
{
dot = btVec3Dot(&AB, &AO);
if (btFuzzyZero(dot) || dot > btScalar(0)) {
btSimplexSet(simplex, 0, B);
btSimplexSet(simplex, 1, A);
btSimplexSetSize(simplex, 2);
btTripleCross(&AB, &AO, &AB, dir);
}
else {
btSimplexSet(simplex, 0, A);
btSimplexSetSize(simplex, 1);
btVec3Copy(dir, &AO);
}
}
else {
dot = btVec3Dot(&ABC, &AO);
if (btFuzzyZero(dot) || dot > btScalar(0)) {
btVec3Copy(dir, &ABC);
}
else {
btSupportVector tmp;
btSupportCopy(&tmp, C);
btSimplexSet(simplex, 0, B);
btSimplexSet(simplex, 1, &tmp);
btVec3Copy(dir, &ABC);
btVec3Scale(dir, -btScalar(1));
}
}
}
return 0;
}
static int btDoSimplex4(btSimplex *simplex, btVector3 *dir)
{
const btSupportVector *A, *B, *C, *D;
btVector3 AO, AB, AC, AD, ABC, ACD, ADB;
int B_on_ACD, C_on_ADB, D_on_ABC;
int AB_O, AC_O, AD_O;
btScalar dist;
// get last added as A
A = ccdSimplexLast(simplex);
// get the other points
B = btSimplexPoint(simplex, 2);
C = btSimplexPoint(simplex, 1);
D = btSimplexPoint(simplex, 0);
// check if tetrahedron is really tetrahedron (has volume > 0)
// if it is not simplex can't be expanded and thus no intersection is
// found
dist = btVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, 0);
if (btFuzzyZero(dist)) {
return -1;
}
// check if origin lies on some of tetrahedron's face - if so objects
// intersect
dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &B->v, &C->v, 0);
if (btFuzzyZero(dist))
return 1;
dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &C->v, &D->v, 0);
if (btFuzzyZero(dist))
return 1;
dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &B->v, &D->v, 0);
if (btFuzzyZero(dist))
return 1;
dist = btVec3PointTriDist2(&ccd_vec3_origin, &B->v, &C->v, &D->v, 0);
if (btFuzzyZero(dist))
return 1;
// compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors
btVec3Copy(&AO, &A->v);
btVec3Scale(&AO, -btScalar(1));
btVec3Sub2(&AB, &B->v, &A->v);
btVec3Sub2(&AC, &C->v, &A->v);
btVec3Sub2(&AD, &D->v, &A->v);
btVec3Cross(&ABC, &AB, &AC);
btVec3Cross(&ACD, &AC, &AD);
btVec3Cross(&ADB, &AD, &AB);
// side (positive or negative) of B, C, D relative to planes ACD, ADB
// and ABC respectively
B_on_ACD = ccdSign(btVec3Dot(&ACD, &AB));
C_on_ADB = ccdSign(btVec3Dot(&ADB, &AC));
D_on_ABC = ccdSign(btVec3Dot(&ABC, &AD));
// whether origin is on same side of ACD, ADB, ABC as B, C, D
// respectively
AB_O = ccdSign(btVec3Dot(&ACD, &AO)) == B_on_ACD;
AC_O = ccdSign(btVec3Dot(&ADB, &AO)) == C_on_ADB;
AD_O = ccdSign(btVec3Dot(&ABC, &AO)) == D_on_ABC;
if (AB_O && AC_O && AD_O) {
// origin is in tetrahedron
return 1;
// rearrange simplex to triangle and call btDoSimplex3()
}
else if (!AB_O) {
// B is farthest from the origin among all of the tetrahedron's
// points, so remove it from the list and go on with the triangle
// case
// D and C are in place
btSimplexSet(simplex, 2, A);
btSimplexSetSize(simplex, 3);
}
else if (!AC_O) {
// C is farthest
btSimplexSet(simplex, 1, D);
btSimplexSet(simplex, 0, B);
btSimplexSet(simplex, 2, A);
btSimplexSetSize(simplex, 3);
}
else { // (!AD_O)
btSimplexSet(simplex, 0, C);
btSimplexSet(simplex, 1, B);
btSimplexSet(simplex, 2, A);
btSimplexSetSize(simplex, 3);
}
return btDoSimplex3(simplex, dir);
}
static int btDoSimplex(btSimplex *simplex, btVector3 *dir)
{
if (btSimplexSize(simplex) == 2) {
// simplex contains segment only one segment
return btDoSimplex2(simplex, dir);
}
else if (btSimplexSize(simplex) == 3) {
// simplex contains triangle
return btDoSimplex3(simplex, dir);
}
else { // btSimplexSize(simplex) == 4
// tetrahedron - this is the only shape which can encapsule origin
// so btDoSimplex4() also contains test on it
return btDoSimplex4(simplex, dir);
}
}
#ifdef __SPU__ #ifdef __SPU__
void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw)
#else #else
@@ -125,23 +712,132 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
m_degenerateSimplex = 0; m_degenerateSimplex = 0;
m_lastUsedMethod = -1; m_lastUsedMethod = -1;
int status = -2;
btVector3 orgNormalInB(0, 0, 0);
btScalar margin = marginA + marginB;
//we add a separate implementation to check if the convex shapes intersect
//See also "Real-time Collision Detection with Implicit Objects" by Leif Olvang
//Todo: integrate the simplex penetration check directly inside the Bullet btVoronoiSimplexSolver
//and remove this temporary code from libCCD
//this fixes issue https://github.com/bulletphysics/bullet3/issues/1703
//note, for large differences in shapes, use double precision build!
{ {
btScalar squaredDistance = BT_LARGE_FLOAT; btScalar squaredDistance = BT_LARGE_FLOAT;
btScalar delta = btScalar(0.); btScalar delta = btScalar(0.);
btScalar margin = marginA + marginB;
btSimplex simplex1;
btSimplex* simplex = &simplex1;
btSimplexInit(simplex);
btVector3 dir(1, 0, 0);
{
btVector3 lastSupV;
btVector3 supAworld;
btVector3 supBworld;
btComputeSupport(m_minkowskiA, localTransA, m_minkowskiB, localTransB, dir, check2d, supAworld, supBworld, lastSupV);
btSupportVector last;
last.v = lastSupV;
last.v1 = supAworld;
last.v2 = supBworld;
btSimplexAdd(simplex, &last);
dir = -lastSupV;
// start iterations
for (int iterations = 0; iterations <gGjkMaxIter; iterations++)
{
// obtain support point
btComputeSupport(m_minkowskiA, localTransA, m_minkowskiB, localTransB, dir, check2d, supAworld, supBworld, lastSupV);
// check if farthest point in Minkowski difference in direction dir
// isn't somewhere before origin (the test on negative dot product)
// - because if it is, objects are not intersecting at all.
btScalar delta = lastSupV.dot(dir);
if (delta < 0)
{
//no intersection, besides margin
status = -1;
break;
}
// add last support vector to simplex
last.v = lastSupV;
last.v1 = supAworld;
last.v2 = supBworld;
btSimplexAdd(simplex, &last);
// if btDoSimplex returns 1 if objects intersect, -1 if objects don't
// intersect and 0 if algorithm should continue
btVector3 newDir;
int do_simplex_res = btDoSimplex(simplex, &dir);
if (do_simplex_res == 1)
{
status = 0; // intersection found
break;
}
else if (do_simplex_res == -1)
{
// intersection not found
status = -1;
break;
}
if (btFuzzyZero(btVec3Dot(&dir, &dir)))
{
// intersection not found
status = -1;
}
if (dir.length2() < SIMD_EPSILON)
{
//no intersection, besides margin
status = -1;
break;
}
if (dir.fuzzyZero())
{
// intersection not found
status = -1;
break;
}
}
}
m_simplexSolver->reset(); m_simplexSolver->reset();
if (status == 0)
{
//status = 0;
//printf("Intersect!\n");
}
if (status==-1)
{
//printf("not intersect\n");
}
//printf("dir=%f,%f,%f\n",dir[0],dir[1],dir[2]);
if (1)
{
for (; ; ) for (; ; )
//while (true) //while (true)
{ {
btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis(); btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* localTransA.getBasis();
btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis(); btVector3 seperatingAxisInB = m_cachedSeparatingAxis* localTransB.getBasis();
btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
@@ -185,7 +881,8 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
if (f0 <= btScalar(0.)) if (f0 <= btScalar(0.))
{ {
m_degenerateSimplex = 2; m_degenerateSimplex = 2;
} else }
else
{ {
m_degenerateSimplex = 11; m_degenerateSimplex = 11;
} }
@@ -297,19 +994,24 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
pointOnB += m_cachedSeparatingAxis * (marginB / s); pointOnB += m_cachedSeparatingAxis * (marginB / s);
distance = ((btScalar(1.) / rlen) - margin); distance = ((btScalar(1.) / rlen) - margin);
isValid = true; isValid = true;
orgNormalInB = normalInB;
m_lastUsedMethod = 1; m_lastUsedMethod = 1;
} else }
else
{ {
m_lastUsedMethod = 2; m_lastUsedMethod = 2;
} }
} }
}
bool catchDegeneratePenetrationCase = bool catchDegeneratePenetrationCase =
(m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < gGjkEpaPenetrationTolerance)); (m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < gGjkEpaPenetrationTolerance));
//if (checkPenetration && !isValid) //if (checkPenetration && !isValid)
if (checkPenetration && (!isValid || catchDegeneratePenetrationCase )) if ((checkPenetration && (!isValid || catchDegeneratePenetrationCase )) || (status == 0))
{ {
//penetration case //penetration case
@@ -331,6 +1033,8 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
); );
if (m_cachedSeparatingAxis.length2())
{
if (isValid2) if (isValid2)
{ {
btVector3 tmpNormalInB = tmpPointOnB - tmpPointOnA; btVector3 tmpNormalInB = tmpPointOnB - tmpPointOnA;
@@ -353,18 +1057,20 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
pointOnA = tmpPointOnA; pointOnA = tmpPointOnA;
pointOnB = tmpPointOnB; pointOnB = tmpPointOnB;
normalInB = tmpNormalInB; normalInB = tmpNormalInB;
isValid = true; isValid = true;
} else }
else
{ {
m_lastUsedMethod = 8; m_lastUsedMethod = 8;
} }
} else }
else
{ {
m_lastUsedMethod = 9; m_lastUsedMethod = 9;
} }
} else }
else
{ {
///this is another degenerate case, where the initial GJK calculation reports a degenerate case ///this is another degenerate case, where the initial GJK calculation reports a degenerate case
@@ -390,12 +1096,17 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
isValid = true; isValid = true;
m_lastUsedMethod = 6; m_lastUsedMethod = 6;
} else }
else
{ {
m_lastUsedMethod = 5; m_lastUsedMethod = 5;
} }
} }
} }
} else
{
//printf("EPA didn't return a valid value\n");
}
} }
@@ -409,17 +1120,17 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
m_cachedSeparatingAxis = normalInB; m_cachedSeparatingAxis = normalInB;
m_cachedSeparatingDistance = distance; m_cachedSeparatingDistance = distance;
if (1)
{ {
///todo: need to track down this EPA penetration solver degeneracy ///todo: need to track down this EPA penetration solver degeneracy
///the penetration solver reports penetration but the contact normal ///the penetration solver reports penetration but the contact normal
///connecting the contact points is pointing in the opposite direction ///connecting the contact points is pointing in the opposite direction
///until then, detect the issue and revert the normal ///until then, detect the issue and revert the normal
btScalar d1=0; btScalar d2 = 0.f;
{ {
btVector3 seperatingAxisInA = (normalInB)* input.m_transformA.getBasis(); btVector3 seperatingAxisInA = (-orgNormalInB)* localTransA.getBasis();
btVector3 seperatingAxisInB = -normalInB* input.m_transformB.getBasis(); btVector3 seperatingAxisInB = orgNormalInB* localTransB.getBasis();
btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
@@ -428,7 +1139,24 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
btVector3 pWorld = localTransA(pInA); btVector3 pWorld = localTransA(pInA);
btVector3 qWorld = localTransB(qInB); btVector3 qWorld = localTransB(qInB);
btVector3 w = pWorld - qWorld; btVector3 w = pWorld - qWorld;
d1 = (-normalInB).dot(w); d2 = orgNormalInB.dot(w)- margin;
}
btScalar d1=0;
{
btVector3 seperatingAxisInA = (normalInB)* localTransA.getBasis();
btVector3 seperatingAxisInB = -normalInB* localTransB.getBasis();
btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);
btVector3 pWorld = localTransA(pInA);
btVector3 qWorld = localTransB(qInB);
btVector3 w = pWorld - qWorld;
d1 = (-normalInB).dot(w)- margin;
} }
btScalar d0 = 0.f; btScalar d0 = 0.f;
{ {
@@ -442,21 +1170,37 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu
btVector3 pWorld = localTransA(pInA); btVector3 pWorld = localTransA(pInA);
btVector3 qWorld = localTransB(qInB); btVector3 qWorld = localTransB(qInB);
btVector3 w = pWorld - qWorld; btVector3 w = pWorld - qWorld;
d0 = normalInB.dot(w); d0 = normalInB.dot(w)-margin;
} }
if (d1>d0) if (d1>d0)
{ {
m_lastUsedMethod = 10; m_lastUsedMethod = 10;
normalInB*=-1; normalInB*=-1;
} }
if (orgNormalInB.length2())
{
if (d2 > d0 && d2 > d1 && d2 > distance)
{
normalInB = orgNormalInB;
distance = d2;
} }
}
}
output.addContactPoint( output.addContactPoint(
normalInB, normalInB,
pointOnB+positionOffset, pointOnB+positionOffset,
distance); distance);
} }
else
{
//printf("invalid gjk query\n");
}
} }