|
World of Rigid Bodies (WoRB)
|
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 }