Home | History | Annotate | Download | only in NarrowPhaseCollision
      1 /*
      2 Bullet Continuous Collision Detection and Physics Library
      3 Copyright (c) 2003-2014 Erwin Coumans http://bulletphysics.org
      4 
      5 This software is provided 'as-is', without any express or implied warranty.
      6 In no event will the authors be held liable for any damages arising from the use of this software.
      7 Permission is granted to anyone to use this software for any purpose,
      8 including commercial applications, and to alter it and redistribute it freely,
      9 subject to the following restrictions:
     10 
     11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
     12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
     13 3. This notice may not be removed or altered from any source distribution.
     14 */
     15 
     16 #ifndef BT_GJK_EPA_PENETATION_CONVEX_COLLISION_H
     17 #define BT_GJK_EPA_PENETATION_CONVEX_COLLISION_H
     18 
     19 #include "LinearMath/btTransform.h" // Note that btVector3 might be double precision...
     20 #include "btGjkEpa3.h"
     21 #include "btGjkCollisionDescription.h"
     22 #include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h"
     23 
     24 
     25 
     26 
     27 
     28 
     29 template <typename btConvexTemplate>
     30 bool btGjkEpaCalcPenDepth(const btConvexTemplate& a, const btConvexTemplate& b,
     31                           const btGjkCollisionDescription& colDesc,
     32                           btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB)
     33 {
     34     (void)v;
     35 
     36     //	const btScalar				radialmargin(btScalar(0.));
     37 
     38     btVector3	guessVector(b.getWorldTransform().getOrigin()-a.getWorldTransform().getOrigin());//?? why not use the GJK input?
     39 
     40     btGjkEpaSolver3::sResults	results;
     41 
     42 
     43     if(btGjkEpaSolver3_Penetration(a,b,guessVector,results))
     44 
     45     {
     46         //	debugDraw->drawLine(results.witnesses[1],results.witnesses[1]+results.normal,btVector3(255,0,0));
     47         //resultOut->addContactPoint(results.normal,results.witnesses[1],-results.depth);
     48         wWitnessOnA = results.witnesses[0];
     49         wWitnessOnB = results.witnesses[1];
     50         v = results.normal;
     51         return true;
     52     } else
     53     {
     54         if(btGjkEpaSolver3_Distance(a,b,guessVector,results))
     55         {
     56             wWitnessOnA = results.witnesses[0];
     57             wWitnessOnB = results.witnesses[1];
     58             v = results.normal;
     59             return false;
     60         }
     61     }
     62     return false;
     63 }
     64 
     65 template <typename btConvexTemplate, typename btGjkDistanceTemplate>
     66 int	btComputeGjkEpaPenetration(const btConvexTemplate& a, const btConvexTemplate& b, const btGjkCollisionDescription& colDesc, btVoronoiSimplexSolver& simplexSolver, btGjkDistanceTemplate* distInfo)
     67 {
     68 
     69     bool m_catchDegeneracies  = true;
     70     btScalar m_cachedSeparatingDistance = 0.f;
     71 
     72     btScalar distance=btScalar(0.);
     73     btVector3	normalInB(btScalar(0.),btScalar(0.),btScalar(0.));
     74 
     75     btVector3 pointOnA,pointOnB;
     76     btTransform	localTransA = a.getWorldTransform();
     77     btTransform localTransB = b.getWorldTransform();
     78 
     79     btScalar marginA = a.getMargin();
     80     btScalar marginB = b.getMargin();
     81 
     82     int m_curIter = 0;
     83     int gGjkMaxIter = colDesc.m_maxGjkIterations;//this is to catch invalid input, perhaps check for #NaN?
     84     btVector3 m_cachedSeparatingAxis = colDesc.m_firstDir;
     85 
     86     bool isValid = false;
     87     bool checkSimplex = false;
     88     bool checkPenetration = true;
     89     int m_degenerateSimplex = 0;
     90 
     91     int m_lastUsedMethod = -1;
     92 
     93     {
     94         btScalar squaredDistance = BT_LARGE_FLOAT;
     95         btScalar delta = btScalar(0.);
     96 
     97         btScalar margin = marginA + marginB;
     98 
     99 
    100 
    101         simplexSolver.reset();
    102 
    103         for ( ; ; )
    104             //while (true)
    105         {
    106 
    107             btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* localTransA.getBasis();
    108             btVector3 seperatingAxisInB = m_cachedSeparatingAxis* localTransB.getBasis();
    109 
    110             btVector3 pInA = a.getLocalSupportWithoutMargin(seperatingAxisInA);
    111             btVector3 qInB = b.getLocalSupportWithoutMargin(seperatingAxisInB);
    112 
    113             btVector3  pWorld = localTransA(pInA);
    114             btVector3  qWorld = localTransB(qInB);
    115 
    116 
    117 
    118             btVector3 w	= pWorld - qWorld;
    119             delta = m_cachedSeparatingAxis.dot(w);
    120 
    121             // potential exit, they don't overlap
    122             if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * colDesc.m_maximumDistanceSquared))
    123             {
    124                 m_degenerateSimplex = 10;
    125                 checkSimplex=true;
    126                 //checkPenetration = false;
    127                 break;
    128             }
    129 
    130             //exit 0: the new point is already in the simplex, or we didn't come any closer
    131             if (simplexSolver.inSimplex(w))
    132             {
    133                 m_degenerateSimplex = 1;
    134                 checkSimplex = true;
    135                 break;
    136             }
    137             // are we getting any closer ?
    138             btScalar f0 = squaredDistance - delta;
    139             btScalar f1 = squaredDistance * colDesc.m_gjkRelError2;
    140 
    141             if (f0 <= f1)
    142             {
    143                 if (f0 <= btScalar(0.))
    144                 {
    145                     m_degenerateSimplex = 2;
    146                 } else
    147                 {
    148                     m_degenerateSimplex = 11;
    149                 }
    150                 checkSimplex = true;
    151                 break;
    152             }
    153 
    154             //add current vertex to simplex
    155             simplexSolver.addVertex(w, pWorld, qWorld);
    156             btVector3 newCachedSeparatingAxis;
    157 
    158             //calculate the closest point to the origin (update vector v)
    159             if (!simplexSolver.closest(newCachedSeparatingAxis))
    160             {
    161                 m_degenerateSimplex = 3;
    162                 checkSimplex = true;
    163                 break;
    164             }
    165 
    166             if(newCachedSeparatingAxis.length2()<colDesc.m_gjkRelError2)
    167             {
    168                 m_cachedSeparatingAxis = newCachedSeparatingAxis;
    169                 m_degenerateSimplex = 6;
    170                 checkSimplex = true;
    171                 break;
    172             }
    173 
    174             btScalar previousSquaredDistance = squaredDistance;
    175             squaredDistance = newCachedSeparatingAxis.length2();
    176 #if 0
    177             ///warning: this termination condition leads to some problems in 2d test case see Bullet/Demos/Box2dDemo
    178             if (squaredDistance>previousSquaredDistance)
    179             {
    180                 m_degenerateSimplex = 7;
    181                 squaredDistance = previousSquaredDistance;
    182                 checkSimplex = false;
    183                 break;
    184             }
    185 #endif //
    186 
    187 
    188             //redundant m_simplexSolver->compute_points(pointOnA, pointOnB);
    189 
    190             //are we getting any closer ?
    191             if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance)
    192             {
    193                 //				m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
    194                 checkSimplex = true;
    195                 m_degenerateSimplex = 12;
    196 
    197                 break;
    198             }
    199 
    200             m_cachedSeparatingAxis = newCachedSeparatingAxis;
    201 
    202             //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject
    203             if (m_curIter++ > gGjkMaxIter)
    204             {
    205 #if defined(DEBUG) || defined (_DEBUG)
    206 
    207                 printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter);
    208                 printf("sepAxis=(%f,%f,%f), squaredDistance = %f\n",
    209                        m_cachedSeparatingAxis.getX(),
    210                        m_cachedSeparatingAxis.getY(),
    211                        m_cachedSeparatingAxis.getZ(),
    212                        squaredDistance);
    213 #endif
    214 
    215                 break;
    216 
    217             }
    218 
    219 
    220             bool check = (!simplexSolver.fullSimplex());
    221             //bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex());
    222 
    223             if (!check)
    224             {
    225                 //do we need this backup_closest here ?
    226                 //				m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
    227                 m_degenerateSimplex = 13;
    228                 break;
    229             }
    230         }
    231 
    232         if (checkSimplex)
    233         {
    234             simplexSolver.compute_points(pointOnA, pointOnB);
    235             normalInB = m_cachedSeparatingAxis;
    236 
    237             btScalar lenSqr =m_cachedSeparatingAxis.length2();
    238 
    239             //valid normal
    240             if (lenSqr < 0.0001)
    241             {
    242                 m_degenerateSimplex = 5;
    243             }
    244             if (lenSqr > SIMD_EPSILON*SIMD_EPSILON)
    245             {
    246                 btScalar rlen = btScalar(1.) / btSqrt(lenSqr );
    247                 normalInB *= rlen; //normalize
    248 
    249                 btScalar s = btSqrt(squaredDistance);
    250 
    251                 btAssert(s > btScalar(0.0));
    252                 pointOnA -= m_cachedSeparatingAxis * (marginA / s);
    253                 pointOnB += m_cachedSeparatingAxis * (marginB / s);
    254                 distance = ((btScalar(1.)/rlen) - margin);
    255                 isValid = true;
    256 
    257                 m_lastUsedMethod = 1;
    258             } else
    259             {
    260                 m_lastUsedMethod = 2;
    261             }
    262         }
    263 
    264         bool catchDegeneratePenetrationCase =
    265         (m_catchDegeneracies &&  m_degenerateSimplex && ((distance+margin) < 0.01));
    266 
    267         //if (checkPenetration && !isValid)
    268         if (checkPenetration && (!isValid || catchDegeneratePenetrationCase ))
    269         {
    270             //penetration case
    271 
    272             //if there is no way to handle penetrations, bail out
    273 
    274             // Penetration depth case.
    275             btVector3 tmpPointOnA,tmpPointOnB;
    276 
    277             m_cachedSeparatingAxis.setZero();
    278 
    279             bool isValid2 = btGjkEpaCalcPenDepth(a,b,
    280                                                  colDesc,
    281                                                  m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB);
    282 
    283             if (isValid2)
    284             {
    285                 btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA;
    286                 btScalar lenSqr = tmpNormalInB.length2();
    287                 if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON))
    288                 {
    289                     tmpNormalInB = m_cachedSeparatingAxis;
    290                     lenSqr = m_cachedSeparatingAxis.length2();
    291                 }
    292 
    293                 if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON))
    294                 {
    295                     tmpNormalInB /= btSqrt(lenSqr);
    296                     btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length();
    297                     //only replace valid penetrations when the result is deeper (check)
    298                     if (!isValid || (distance2 < distance))
    299                     {
    300                         distance = distance2;
    301                         pointOnA = tmpPointOnA;
    302                         pointOnB = tmpPointOnB;
    303                         normalInB = tmpNormalInB;
    304 
    305                         isValid = true;
    306                         m_lastUsedMethod = 3;
    307                     } else
    308                     {
    309                         m_lastUsedMethod = 8;
    310                     }
    311                 } else
    312                 {
    313                     m_lastUsedMethod = 9;
    314                 }
    315             } else
    316 
    317             {
    318                 ///this is another degenerate case, where the initial GJK calculation reports a degenerate case
    319                 ///EPA reports no penetration, and the second GJK (using the supporting vector without margin)
    320                 ///reports a valid positive distance. Use the results of the second GJK instead of failing.
    321                 ///thanks to Jacob.Langford for the reproduction case
    322                 ///http://code.google.com/p/bullet/issues/detail?id=250
    323 
    324 
    325                 if (m_cachedSeparatingAxis.length2() > btScalar(0.))
    326                 {
    327                     btScalar distance2 = (tmpPointOnA-tmpPointOnB).length()-margin;
    328                     //only replace valid distances when the distance is less
    329                     if (!isValid || (distance2 < distance))
    330                     {
    331                         distance = distance2;
    332                         pointOnA = tmpPointOnA;
    333                         pointOnB = tmpPointOnB;
    334                         pointOnA -= m_cachedSeparatingAxis * marginA ;
    335                         pointOnB += m_cachedSeparatingAxis * marginB ;
    336                         normalInB = m_cachedSeparatingAxis;
    337                         normalInB.normalize();
    338 
    339                         isValid = true;
    340                         m_lastUsedMethod = 6;
    341                     } else
    342                     {
    343                         m_lastUsedMethod = 5;
    344                     }
    345                 }
    346             }
    347         }
    348     }
    349 
    350 
    351 
    352     if (isValid && ((distance < 0) || (distance*distance < colDesc.m_maximumDistanceSquared)))
    353     {
    354 
    355         m_cachedSeparatingAxis = normalInB;
    356         m_cachedSeparatingDistance = distance;
    357         distInfo->m_distance = distance;
    358         distInfo->m_normalBtoA = normalInB;
    359         distInfo->m_pointOnB = pointOnB;
    360         distInfo->m_pointOnA = pointOnB+normalInB*distance;
    361         return 0;
    362     }
    363     return -m_lastUsedMethod;
    364 }
    365 
    366 
    367 
    368 
    369 #endif //BT_GJK_EPA_PENETATION_CONVEX_COLLISION_H
    370