World of Rigid Bodies (WoRB)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
CollisionDetection.cpp
Go to the documentation of this file.
00001 /**
00002  *  @file      CollisionDetection.cpp
00003  *  @brief     Implementation of the collision detection system.
00004  *  @author    Mikica Kocic
00005  *  @version   0.18
00006  *  @date      2012-05-18
00007  *  @copyright GNU Public License.
00008  */
00009 
00010 #include "WoRB.h"
00011 
00012 #include <algorithm> // std::min, std::max
00013 
00014 using namespace WoRB;
00015 
00016 /////////////////////////////////////////////////////////////////////////////////////////
00017 
00018 void Geometry::Detect( CollisionResolver& owner, const Geometry* B ) const
00019 {
00020     if ( ! owner.HasSpaceForMoreContacts () ) {
00021         return;
00022     }
00023 
00024     typedef const Cuboid*     Cuboid_    ;
00025     typedef const Sphere*     Sphere_    ;
00026     typedef const HalfSpace*  HalfSpace_ ;
00027     typedef const TruePlane*  TruePlane_ ;
00028 
00029     switch( Class )
00030     {
00031         case _Sphere: switch( B->Class )
00032         {
00033             case _Sphere:    Sphere_(this)->Check( owner, *Sphere_(B)    ); break;
00034             case _Cuboid:    Cuboid_(B)   ->Check( owner, *Sphere_(this) ); break;
00035             case _HalfSpace: Sphere_(this)->Check( owner, *HalfSpace_(B) ); break;
00036             case _TruePlane: Sphere_(this)->Check( owner, *TruePlane_(B) ); break;
00037         }
00038         break;
00039 
00040         case _Cuboid: switch( B->Class )
00041         {
00042             case _Sphere:    Cuboid_(this)->Check( owner, *Sphere_(B)    ); break;
00043             case _Cuboid:    Cuboid_(this)->Check( owner, *Cuboid_(B)    ); break;
00044             case _HalfSpace: Cuboid_(this)->Check( owner, *HalfSpace_(B) ); break;
00045             case _TruePlane: /* not implemented */                        ; break;
00046         }
00047         break;
00048 
00049         case _HalfSpace: switch( B->Class )
00050         {
00051             case _Sphere:    Sphere_(B)->Check( owner, *HalfSpace_(this) ); break;
00052             case _Cuboid:    Cuboid_(B)->Check( owner, *HalfSpace_(this) ); break;
00053             case _HalfSpace: /* not implemented */                        ; break;
00054             case _TruePlane: /* not implemented */                        ; break;
00055         }
00056         break;
00057 
00058         case _TruePlane: switch( B->Class )
00059         {
00060             case _Sphere:    Sphere_(B)->Check( owner, *TruePlane_(this) ); break;
00061             case _Cuboid:    /* not implemented */                        ; break;
00062             case _HalfSpace: /* not implemented */                        ; break;
00063             case _TruePlane: /* not implemented */                        ; break;
00064         }
00065         break;
00066     }
00067 }
00068 
00069 /////////////////////////////////////////////////////////////////////////////////////////
00070 
00071 unsigned Sphere::Check( CollisionResolver& owner, const TruePlane& plane ) const
00072 {
00073     if ( ! owner.HasSpaceForMoreContacts () ) { 
00074         return 0; // We do not have space left for new contacts
00075     }
00076 
00077     // Get the position of the center of the sphere
00078     //
00079     Quaternion position = Position ();
00080 
00081     // Find the distance the center of the sphere to the plane
00082     //
00083     double distance = plane.Direction.Dot( position ) - plane.Offset;
00084 
00085     // Check if there is an overlap
00086     //
00087     if ( distance * distance > Radius * Radius )
00088     {
00089         return 0;
00090     }
00091 
00092     // Check which side of the plane we're on
00093     //
00094     Quaternion normal = plane.Direction;
00095     double penetration = -distance;
00096     if ( distance < 0 )
00097     {
00098         normal = -normal;
00099         penetration = -penetration;
00100     }
00101     penetration += Radius;
00102 
00103     return owner.RegisterNewContact(
00104         /* a */ Body,
00105         /* b */ 0, /* scenery */
00106         /* X */ position - plane.Direction * distance,
00107         /* N */ normal,
00108         /* d */ penetration
00109     );
00110 }
00111 
00112 /////////////////////////////////////////////////////////////////////////////////////////
00113 
00114 unsigned Sphere::Check( CollisionResolver& owner, const HalfSpace& plane ) const
00115 {
00116     if ( ! owner.HasSpaceForMoreContacts () ) { 
00117         return 0; // We do not have space left for new contacts
00118     }
00119 
00120     // Get the position of the center of the sphere
00121     //
00122     Quaternion position = Position ();
00123 
00124     // Find the distance from the plane
00125     //
00126     double distance = plane.Direction.Dot( position ) - Radius - plane.Offset;
00127 
00128     if ( distance >= 0 ) {
00129         return 0;
00130     }
00131 
00132     // New contact: it has a normal in the plane direction.
00133     //
00134     return owner.RegisterNewContact(
00135         /* a */ Body,
00136         /* b */ 0, /* scenery */
00137         /* X */ position - plane.Direction * ( distance + Radius ),
00138         /* N */ plane.Direction,
00139         /* d */ -distance
00140     );
00141 }
00142 
00143 /////////////////////////////////////////////////////////////////////////////////////////
00144 
00145 unsigned Sphere::Check( CollisionResolver& owner, const Sphere& B ) const
00146 {
00147     if ( ! owner.HasSpaceForMoreContacts () ) { 
00148         return 0; // We do not have space left for new contacts
00149     }
00150 
00151     // Get the positions of the centra of both spheres
00152     //
00153     Quaternion position_A = Position ();
00154     Quaternion position_B = B.Position ();
00155 
00156     // Find the displacement and the distance between the two spheres
00157     //
00158     Quaternion displacement = position_A - position_B;
00159     double distance = displacement.ImNorm ();
00160 
00161     // Check if the separation is large enough
00162     //
00163     if ( distance >= Radius + B.Radius) {
00164         return 0;
00165     }
00166 
00167     // The contact-normal is along the displacement with the position half-way
00168     //
00169     return owner.RegisterNewContact(
00170         /* a */ Body,
00171         /* b */ B.Body,
00172         /* X */ position_B + displacement * 0.5,
00173         /* N */ displacement * ( 1.0 / distance ),
00174         /* d */ Radius + B.Radius - distance
00175     );
00176 }
00177 
00178 /////////////////////////////////////////////////////////////////////////////////////////
00179 
00180 inline Quaternion Cuboid::FindContactPointOnEdges(
00181         const Quaternion& ptOn_A, const Quaternion& axis_A, double edge_A,
00182         const Quaternion& ptOn_B, const Quaternion& axis_B, double edge_B,
00183         bool use_A
00184     )
00185 {
00186     // If use_A is true, and the contact point is outside the edge (in the case of 
00187     // an edge-face contact) then we use 'this' cuboid's midpoint, otherwise we use B's.
00188 
00189     double sqNorm_dA = axis_A.ImSquaredNorm(); // d_A = Axis A
00190     double sqNorm_dB = axis_B.ImSquaredNorm(); // d_B = Axis B
00191     double axis_AB = axis_B.Dot( axis_A ); // Scalar product axis_A and axis_B
00192 
00193     // Displacement between point on edge of this cuboid (A) and point on edge B
00194     //
00195     Quaternion p_AB = ptOn_A - ptOn_B;
00196     double dpSta_A = p_AB.Dot( axis_A );
00197     double dpSta_B = p_AB.Dot( axis_B );
00198 
00199     double denominator = sqNorm_dA * sqNorm_dB - axis_AB * axis_AB;
00200 
00201     // In case of parallel lines
00202     //
00203     if ( fabs( denominator ) < 1e-4 ) {
00204         return use_A ? ptOn_A : ptOn_B;
00205     }
00206 
00207     double mu_A = ( axis_AB   * dpSta_B - sqNorm_dB * dpSta_A ) / denominator;
00208     double mu_B = ( sqNorm_dA * dpSta_B - axis_AB   * dpSta_A ) / denominator;
00209 
00210     // If either of the edges has the nearest point out of bounds, 
00211     // then the edges aren't crossed, we have an edge-face contact. 
00212     //
00213     if ( mu_A > edge_A || mu_A < -edge_A || mu_B > edge_B || mu_B < -edge_B )
00214     {
00215         // Our point is on the edge of body which we know from the use_A parameter.
00216         return use_A ? ptOn_A : ptOn_B;
00217     }
00218     else
00219     {
00220         return ( ptOn_A + axis_A * mu_A ) * 0.5
00221              + ( ptOn_B + axis_B * mu_B ) * 0.5;
00222     }
00223 }
00224 
00225 /////////////////////////////////////////////////////////////////////////////////////////
00226 
00227 unsigned Cuboid::RegisterContactOnAxis_Thorough
00228 (
00229     CollisionResolver& owner, const Cuboid& B,
00230     const Quaternion& displacement, // = B.Position () - Position ()
00231     unsigned axis
00232     ) const
00233 {
00234     Quaternion En = Axis( axis );
00235     bool onRight = En.Dot( displacement ) > 0;
00236 
00237     // Go through each combination of +/- for each half-size
00238     //
00239     unsigned contactCount = 0;
00240 
00241     for ( unsigned i = 0; i < 8 && owner.HasSpaceForMoreContacts (); ++i )
00242     {
00243         // Calculate the position of each vertex
00244         //
00245         static const double vertices[8][3] = 
00246         {
00247             { 1, 1,  1 }, { -1, 1,  1 }, { 1, -1,  1 }, { -1, -1,  1 },
00248             { 1, 1, -1 }, { -1, 1, -1 }, { 1, -1, -1 }, { -1, -1, -1 }
00249         };
00250         Quaternion vertexPos( 0, vertices[i][0], vertices[i][1], vertices[i][2] );
00251         vertexPos = vertexPos.ComponentWiseProduct( B.HalfExtent );
00252         vertexPos = B.Body->ToWorld( vertexPos );
00253 
00254         // Calculate the distance between the B's vertex and the A's center
00255         // projected on A's axis
00256         //
00257         double distance = ( vertexPos - Position() ).Dot( En );
00258 
00259         // Compare this to the A's face distance from A's center
00260         //
00261         if ( ( onRight && distance <= HalfExtent[ axis ] )
00262           || ( !onRight && distance >= -HalfExtent[ axis ] ) )
00263         {
00264             // New contact with the vertex as the point of contact
00265             //
00266             contactCount += owner.RegisterNewContact(
00267                 /* a */ Body,
00268                 /* b */ B.Body,
00269                 /* X */ vertexPos,
00270                 /* N */ onRight ? -En : En,
00271                 /* d */ onRight ? HalfExtent[ axis ] - distance
00272                                 : distance - HalfExtent[ axis ]
00273             );
00274         }
00275     }
00276 
00277     return contactCount;
00278 }
00279 
00280 /////////////////////////////////////////////////////////////////////////////////////////
00281 
00282 unsigned Cuboid::RegisterContactOnAxis
00283 (
00284     CollisionResolver& owner, const Cuboid& B,
00285     const Quaternion& displacement,
00286     const Quaternion& axis,
00287     double penetration
00288     ) const
00289 {
00290     // Find out which of the B faces is on the specified axis.
00291     //
00292     Quaternion normal = axis;
00293     if ( normal.Dot( displacement ) > 0 ) {
00294         normal = -normal;
00295     }
00296 
00297     Quaternion axis_Bn( 0,
00298         B.Axis(0).Dot( normal ),
00299         B.Axis(1).Dot( normal ),
00300         B.Axis(2).Dot( normal )
00301     );
00302 
00303     // Find out which vertex of cuboid B we are colliding with.
00304 
00305     Quaternion contactPointOn_B; // = zero
00306 
00307     for ( unsigned i = 0; i < 3; ++i ) // for each axis in B
00308     {
00309         // In case of B's edge that is almost normal to the contact normal 
00310         // we take a mid-point of A's & B's edges intersection projected on B.Axis(i).
00311         // Otherwise, if not normal, we take the vertex which is the closest.
00312         //
00313         if ( fabs( axis_Bn[i] ) < 1e-4 )
00314         {
00315             // Project the A's half-extents and displacement onto B's axis
00316             // deltaCenter +/- halfExtent_A describes A's vertices in B's frame of ref.
00317             //
00318             double distance_BA  = -displacement.Dot( B.Axis(i) );
00319             double halfExtent_A = ProjectOn( B.Axis(i) );
00320             double halfExtent_B = B.HalfExtent[i];
00321 
00322             // Get mid-point of A's & B's projections' intersection on B's axis
00323             //
00324             double vxL = std::max( distance_BA - halfExtent_A, -halfExtent_B );
00325             double vxR = std::min( distance_BA + halfExtent_A,  halfExtent_B );
00326             double vxM = 0.5 * ( vxL + vxR );
00327             contactPointOn_B[i] = fabs( vxM ) < 1e-4 ? 0 : vxM;
00328         }
00329         else // if B's edge is not almost normal to the contact normal 
00330         {
00331             // Select the closest vertex
00332             //
00333             contactPointOn_B[i] = axis_Bn[i] > 0 ?  B.HalfExtent[i]
00334                                                  : -B.HalfExtent[i];
00335         }
00336     }
00337 
00338     // No parallel axes with faces found; single collision point
00339     //
00340     return owner.RegisterNewContact(
00341         /* a */ Body,
00342         /* b */ B.Body,
00343         /* X */ B.Body->ToWorld( contactPointOn_B ),
00344         /* N */ normal,
00345         /* d */ penetration
00346     );
00347 }
00348 
00349 /////////////////////////////////////////////////////////////////////////////////////////
00350 
00351 unsigned Cuboid::Check( CollisionResolver& owner, const Cuboid& B ) const
00352 {
00353     // Find the displacement between the two centra
00354     //
00355     Quaternion displacement = B.Position() - Position();
00356 
00357     // Assume that there is no contact
00358     //
00359     double penetration = Const::Max;
00360     unsigned int axisIndex_A = 0xFF;
00361     unsigned int axisIndex_B = 0xFF;
00362 
00363     #define Quit_If_No_Overlaps( axis, indexA, indexB ) \
00364     if ( ! CheckOverlapOnAxis( B, (axis), displacement, penetration, \
00365             (indexA), (indexB), axisIndex_A, axisIndex_B ) ) { \
00366         return 0; \
00367     }
00368 
00369     // Check each body axes, keeping track of the axis with the smallest penetration
00370     //
00371     Quit_If_No_Overlaps( Axis(0)  ,    0, 0xFF );
00372     Quit_If_No_Overlaps( Axis(1)  ,    1, 0xFF );
00373     Quit_If_No_Overlaps( Axis(2)  ,    2, 0xFF );
00374     Quit_If_No_Overlaps( B.Axis(0), 0xFF,    0 );
00375     Quit_If_No_Overlaps( B.Axis(1), 0xFF,    1 );
00376     Quit_If_No_Overlaps( B.Axis(2), 0xFF,    2 );
00377 
00378     // Remember the single axis with the smallest penetration
00379     //
00380     bool use_A = axisIndex_B != 0xFF;
00381 
00382     // Continue with the axes cross products
00383     //
00384     Quit_If_No_Overlaps( Axis(0).Cross( B.Axis(0) ), 0, 0 );
00385     Quit_If_No_Overlaps( Axis(0).Cross( B.Axis(1) ), 0, 1 );
00386     Quit_If_No_Overlaps( Axis(0).Cross( B.Axis(2) ), 0, 2 );
00387     Quit_If_No_Overlaps( Axis(1).Cross( B.Axis(0) ), 1, 0 );
00388     Quit_If_No_Overlaps( Axis(1).Cross( B.Axis(1) ), 1, 1 );
00389     Quit_If_No_Overlaps( Axis(1).Cross( B.Axis(2) ), 1, 2 );
00390     Quit_If_No_Overlaps( Axis(2).Cross( B.Axis(0) ), 2, 0 );
00391     Quit_If_No_Overlaps( Axis(2).Cross( B.Axis(1) ), 2, 1 );
00392     Quit_If_No_Overlaps( Axis(2).Cross( B.Axis(2) ), 2, 2 );
00393                                                            
00394     #undef Quit_If_No_Overlaps
00395 
00396     // At this point we have detected a collision!
00397 
00398     if ( axisIndex_B == 0xFF ) // single axis A
00399     {
00400         // The collision between cuboid B's vertex and this cuboid's face
00401         //
00402         return this->RegisterContactOnAxis( owner, B, 
00403             displacement, Axis( axisIndex_A ), penetration );
00404     }
00405     else if ( axisIndex_A == 0xFF ) // single axis B
00406     {
00407         // The collision between this cuboid's vertex and cuboid B's face
00408         //
00409         return B.RegisterContactOnAxis( owner, *this, 
00410             -displacement, B.Axis( axisIndex_B ), penetration );
00411     }
00412 
00413     // It is an edge-edge collision; get the involved axes from indices
00414     //
00415     Quaternion axis_A = Axis( axisIndex_A );
00416     Quaternion axis_B = B.Axis( axisIndex_B );
00417 
00418     Quaternion normal = axis_A.Cross( axis_B ).Unit ();
00419 
00420     // Make the normal always point from A to B
00421     //
00422     if ( normal.Dot( displacement ) > 0 ) {
00423         normal = -normal;
00424     }
00425 
00426     // Find the closest point on the involved edges to the other axes.
00427     // By default the closest point is mid-point on the edge.
00428     // @note  Compare to RegisterContactOnAxis algorithm.
00429     //
00430     Quaternion ptOn_Edge_A; // = zero (mid-point on all edges by default)
00431     Quaternion ptOn_Edge_B; // = zero (mid-point on all edges by default)
00432 
00433     for ( unsigned i = 0; i < 3; ++i ) // foreach axis in A and B
00434     {
00435         if ( i != axisIndex_A )
00436         {
00437             double axis_An = Axis(i).Dot( normal );
00438 
00439             if ( fabs( axis_An ) > 1e-4 ) {
00440                 ptOn_Edge_A[i] = axis_An > 0 ? -HalfExtent[i] 
00441                                              :  HalfExtent[i];
00442             }
00443         }
00444         if ( i != axisIndex_B )
00445         {
00446             double axis_Bn = B.Axis(i).Dot( normal );
00447 
00448             if ( fabs( axis_Bn ) > 1e-4 ) {
00449                 ptOn_Edge_B[i] = axis_Bn > 0 ?  B.HalfExtent[i] 
00450                                              : -B.HalfExtent[i];
00451             }
00452         }
00453     }
00454 
00455     // At this moment we have a point and a direction for the colliding edges.
00456     // Now, find out the contact point i.e. the closest approach of B's line-segments.
00457     // (Note that, from here, we continue in world coordinates.)
00458     //
00459     Quaternion contactPoint_world = FindContactPointOnEdges(
00460         Body->ToWorld( ptOn_Edge_A ),   axis_A, HalfExtent[ axisIndex_A ],
00461         B.Body->ToWorld( ptOn_Edge_B ), axis_B, B.HalfExtent[ axisIndex_B ], use_A
00462     );
00463 
00464     return owner.RegisterNewContact(
00465         /* a */ Body,
00466         /* b */ B.Body,
00467         /* X */ contactPoint_world,
00468         /* N */ normal,
00469         /* d */ penetration
00470     );
00471 }
00472 
00473 /////////////////////////////////////////////////////////////////////////////////////////
00474 
00475 unsigned Cuboid::Check( CollisionResolver& owner, const Quaternion& point ) const
00476 {
00477     // Transform the point into cuboid coordinates i.e. body-fixed frame
00478     //
00479     Quaternion pointInBodySpace = Body->ToWorld.TransformInverse( point );
00480 
00481     Quaternion normal;
00482     double min_depth = Const::Max;
00483 
00484     // Check each axis, looking for the axis on which the penetration is least deep.
00485     //
00486     for ( unsigned i = 0; i < 3; ++i )
00487     {
00488         double depth = HalfExtent[i] - fabs( pointInBodySpace[i] );
00489         if ( depth < 0 ) {
00490             return 0;
00491         }
00492         else if ( depth < min_depth )
00493         {
00494             min_depth = depth;
00495             normal = pointInBodySpace[i] < 0 ? -Axis(i) : Axis(i);
00496         }
00497     }
00498 
00499     return owner.RegisterNewContact(
00500         /* a */ Body,
00501         /* b */ 0, // == scenery
00502         /* X */ point,
00503         /* N */ normal,
00504         /* d */ min_depth
00505     );
00506 }
00507 
00508 /////////////////////////////////////////////////////////////////////////////////////////
00509 
00510 unsigned Cuboid::Check( CollisionResolver& owner, const Sphere& B ) const
00511 {
00512     // Transform the center of the sphere into cuboid coordinates
00513     //
00514     Quaternion center = B.Position ();
00515     Quaternion relCenter = Body->ToWorld.TransformInverse( center );
00516 
00517     // Early out check to see if we can exclude the contact
00518     //
00519     if ( fabs( relCenter.x ) - B.Radius > HalfExtent.x ||
00520          fabs( relCenter.y ) - B.Radius > HalfExtent.y ||
00521          fabs( relCenter.z ) - B.Radius > HalfExtent.z )
00522     {
00523         return 0;
00524     }
00525 
00526     // Clamp each coordinate to the cuboid
00527     //
00528     Quaternion closestPoint( 0,
00529         Clamp( relCenter.x, HalfExtent.x ),
00530         Clamp( relCenter.y, HalfExtent.y ),
00531         Clamp( relCenter.z, HalfExtent.z )
00532     );
00533 
00534     // Check we're in contact
00535     //
00536     double distance = ( closestPoint - relCenter ).ImSquaredNorm();
00537     if ( distance > B.Radius * B.Radius ) {
00538         return 0;
00539     }
00540     distance = sqrt( distance );
00541 
00542     // New contact at the closest point in world coordinates
00543     //
00544     Quaternion closestPointWorld = Body->ToWorld( closestPoint );
00545 
00546     return owner.RegisterNewContact(
00547         /* a */ Body,
00548         /* b */ B.Body,
00549         /* X */ closestPointWorld,
00550         /* N */ ( closestPointWorld - center ).Unit (),
00551         /* d */ B.Radius - distance
00552     );
00553 }
00554 
00555 /////////////////////////////////////////////////////////////////////////////////////////
00556 
00557 unsigned Cuboid::Check( CollisionResolver& owner, const HalfSpace& plane ) const
00558 {
00559     if ( ! owner.HasSpaceForMoreContacts () ) { 
00560         return 0; // We do not have space left for new contacts
00561     }
00562     else if ( ! Intersects( plane ) ) {
00563         return 0; // No intersection between the cuboid and the half-space
00564     }
00565 
00566     // The contact point with the largest penetration
00567     // By default zero, meaning the mid-point on all edges of the cuboid.
00568     //
00569     Quaternion contactPoint; 
00570 
00571     // Find if there exist edges/faces of the cuboid parallel to the plane
00572 
00573     Quaternion axis_n( 0,
00574         Axis(0).Dot( plane.Direction ),
00575         Axis(1).Dot( plane.Direction ),
00576         Axis(2).Dot( plane.Direction )
00577     );
00578 
00579     unsigned parallelCount = 0;
00580 
00581     for ( unsigned i = 0; i < 3; ++i ) // for each axis in B
00582     {
00583         // In case of cuboid's edge that is almost parallel to the plane 
00584         // we take a mid-point of the edge.
00585         // Otherwise, if parallel, we take the vertex which is the closest.
00586         //
00587         if ( fabs( axis_n[i] ) < 1e-4 ) // almost parallel edge & plane
00588         {
00589             ++parallelCount;
00590         }
00591         else // if cuboid's edge is not almost parallel to the plane
00592         {
00593             // Select the closest vertex
00594             //
00595             contactPoint[i] = axis_n[i] < 0 ?  HalfExtent[i]
00596                                             : -HalfExtent[i];
00597         }
00598     }
00599 
00600     // In case if there are edges/faces parallel to the plane,
00601     // just register the found mid-contact point and return.
00602     //
00603     if ( parallelCount > 0 )
00604     {
00605         // Switch to world coordinates and find out penetration of the
00606         // contact point into the half-space
00607         //
00608         contactPoint = Body->ToWorld( contactPoint );
00609         double penetration = plane.Offset - contactPoint.Dot( plane.Direction );
00610 
00611         // Register a new contact at the point of contact
00612         //
00613         return owner.RegisterNewContact(
00614             /* a */ Body,
00615             /* b */ 0, // scenery
00616             /* X */ contactPoint + 0.5 * penetration * plane.Direction,
00617             /* N */ plane.Direction,
00618             /* d */ penetration
00619         );
00620     }
00621 
00622     // Proceede with the thorough investigation by scanning all vertices
00623 
00624     // Find the intersection points by checking all vertices. 
00625     // If the cuboid is resting on a plane or on an edge, it will be reported 
00626     // as four or two contact points.
00627 
00628     // Go through each combination of +/- for each half-size
00629     //
00630     unsigned contactCount = 0;
00631 
00632     for ( unsigned i = 0; i < 8 && owner.HasSpaceForMoreContacts (); ++i )
00633     {
00634         // Calculate the position of each vertex
00635         //
00636         static const double vertices[8][3] = 
00637         {
00638             { 1, 1,  1 }, { -1, 1,  1 }, { 1, -1,  1 }, { -1, -1,  1 },
00639             { 1, 1, -1 }, { -1, 1, -1 }, { 1, -1, -1 }, { -1, -1, -1 }
00640         };
00641         Quaternion vertexPos( 0, vertices[i][0], vertices[i][1], vertices[i][2] );
00642 
00643         vertexPos = vertexPos.ComponentWiseProduct( HalfExtent );
00644 
00645         vertexPos = Body->ToWorld( vertexPos );
00646 
00647         // Calculate the penetration of the vertex into the half-space
00648         //
00649         double penetration = plane.Offset - vertexPos.Dot( plane.Direction );
00650 
00651         if ( penetration >= 0 ) // In case of penetration...
00652         {
00653             // Register a new contact where the point of contact is half-way between 
00654             // the vertex and the plane
00655             //
00656             contactCount += owner.RegisterNewContact(
00657                 /* a */ Body,
00658                 /* b */ 0, // scenery
00659                 /* X */ vertexPos + 0.5 * penetration * plane.Direction,
00660                 /* N */ plane.Direction,
00661                 /* d */ penetration
00662             );
00663         }
00664     }
00665 
00666     return contactCount;
00667 }