World of Rigid Bodies (WoRB)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
PositionProjections.cpp
Go to the documentation of this file.
00001 /**
00002  *  @file      PositionProjections.cpp
00003  *  @brief     Implementation of the position projections algorithm.
00004  *  @author    Mikica Kocic
00005  *  @version   0.19
00006  *  @date      2012-05-11
00007  *  @copyright GNU Public License.
00008  */
00009 
00010 #include "WoRB.h"
00011 
00012 using namespace WoRB;
00013 
00014 /////////////////////////////////////////////////////////////////////////////////////////
00015 // Resolves collisions in the system using the position projection method.
00016 //
00017 void CollisionResolver::PositionProjections( unsigned maxIterations, double eps )
00018 {
00019     if ( CollisionCount == 0 ) {
00020         return; // Nothing to do
00021     }
00022 
00023     // Setup default parameters
00024     //
00025     if ( maxIterations == 0 ) {
00026         maxIterations = 8 * CollisionCount;
00027     }
00028     if ( eps == 0 ) {
00029         eps = 1e-2;
00030     }
00031 
00032     // Performing position projections, until there are no contacts with notable 
00033     // penetrations found.
00034     //
00035     for ( unsigned iteration = 0; iteration < maxIterations; ++iteration )
00036     {
00037         // Find the contact with the largest penetration
00038         //
00039         Collision* contact = FindLargestPenetration( eps );
00040         if ( ! contact ) {
00041             break; // Done, if there are no penetrations
00042         }
00043 
00044         // Activate bodies participating in the collision that are lying inactive
00045         // @fixme (disabled since impulse transfer should wake bodies)
00046         contact->ActivateInactiveBodies ();
00047 
00048         // Calculate and apply position/orientation jolt that resolve the penetration
00049         // 
00050         Quaternion X_jolt[2], Q_jolt[2];
00051         contact->PositionProjection( X_jolt, Q_jolt, Relaxation );
00052 
00053         // However, the resolution may have changed the penetration of other
00054         // bodies, so we need to update affected collision data.
00055         //
00056         RigidBody** bodies_in_this_contact = &contact->Body_A;
00057         for ( unsigned i = 0; i < CollisionCount; ++i )
00058         {
00059             Collision& c_aff = Collisions[i];
00060             RigidBody** b_aff = &c_aff.Body_A;
00061 
00062             for ( unsigned a = 0; a < 2; ++a )  // For each body in scanned contacts
00063             {
00064                 for ( unsigned b = 0; b < 2; ++b ) // For each body in this contact
00065                 {
00066                     if ( b_aff[a] && b_aff[a] == bodies_in_this_contact[b] )
00067                     {
00068                         // dX = X_j + ( Q_j x R )
00069                         Quaternion deltaPosition = 
00070                             X_jolt[b] + Q_jolt[b].Cross( c_aff.RelativePosition[a] );
00071 
00072                         // The sign of the change is positive if we're dealing with 
00073                         // body B and negative otherwise, as the position resolution
00074                         // should be _subtracted_.
00075                         //
00076                         double dP_n = deltaPosition.Dot( c_aff.Normal );
00077                         c_aff.Penetration += a ? dP_n : -dP_n;
00078                     }
00079                 }
00080             }
00081         }
00082     }
00083 }
00084 
00085 /////////////////////////////////////////////////////////////////////////////////////////
00086 // Position projection for the single collision
00087 //
00088 void Collision::PositionProjection(
00089         Quaternion X_jolt[2], // applied position change
00090         Quaternion Q_jolt[2], // applied orientation change
00091         double relaxation     // Position projections relaxation coefficient
00092     )
00093 {
00094     RigidBody** Body = &Body_A;
00095 
00096     // We need to work out the inertia of each object in the direction
00097     // of the contact normal, due to angular inertia only.
00098     //
00099     double inverseTotalInertia = 0;
00100     QTensor inverse_I_world[2];
00101     double inverseAngInertia[2];
00102 
00103     for ( unsigned i = 0; i < 2; ++i ) 
00104     {
00105         if ( ! Body[i] ) {
00106             continue;
00107         }
00108 
00109         inverse_I_world[i] = Body[i]->InverseInertiaWorld;
00110 
00111         // Calculate the angular component of total inertia:
00112         // angI = ( ( I_world^-1 • ( r × N ) ) × r ) • N
00113         //
00114         inverseAngInertia[i] = 
00115             ( inverse_I_world[i] * RelativePosition[i].Cross( Normal ) )
00116             .Cross( RelativePosition[i] )
00117             .Dot( Normal );
00118 
00119         // The total inertia is sum of its linear and angular components:
00120         // 1/m_tot = 1/m_linear + 1/m_angular;
00121         //
00122         inverseTotalInertia += Body[i]->InverseMass + inverseAngInertia[i];
00123     }
00124 
00125     // Loop through again calculating and applying the changes
00126     //
00127     for ( unsigned i = 0; i < 2; ++i ) 
00128     {
00129         if ( ! Body[i]) {
00130             continue;
00131         }
00132 
00133         // Calculate required linear (delta_X) and angular movements (delta_Q) 
00134         // in proportion to the two mass and angular inertias
00135         //
00136         double penetration = i == 0 ? Penetration : -Penetration;
00137         if ( 0.0 < relaxation && relaxation <= 1.0 ) {
00138             penetration *= ( 1 - relaxation );
00139         }
00140         double delta_X = penetration * ( Body[i]->InverseMass / inverseTotalInertia );
00141         double delta_Q = penetration * ( inverseAngInertia[i] / inverseTotalInertia );
00142 
00143         // Limit the angular jolt to avoid angular projections that are too great 
00144         // (case when mass is large and inertia tensor is small).
00145         {
00146             Quaternion angularProjection = 
00147                 RelativePosition[i] - Normal * RelativePosition[i].Dot( Normal );
00148 
00149             const double Q_limit = 0.3;
00150             double max_Q = Q_limit * angularProjection.ImNorm ();
00151 
00152             if ( delta_Q < -max_Q )
00153             {
00154                 delta_X = ( delta_X + delta_Q ) + max_Q;
00155                 delta_Q = -max_Q;
00156             }
00157             else if ( delta_Q > max_Q )
00158             {
00159                 delta_X = ( delta_X + delta_Q ) - max_Q;
00160                 delta_Q = max_Q;
00161             }
00162         }
00163 
00164         // Calculate and apply the position jolt from the required linear movement
00165         // delta_X, which is a simple linear movement along the contact normal.
00166         //
00167         X_jolt[i] = Normal * delta_X;
00168         Body[i]->Position += X_jolt[i];
00169 
00170         // Calculate the required angular jolt to achieve angular movement delta_Q
00171         //
00172         if ( delta_Q == 0 )  // No angular movement = no orientation jolt
00173         {
00174             Q_jolt[i]= 0;
00175         }
00176         else // a bit more complicated orientation jolt
00177         { 
00178             //                  I_world^-1 • ( r × N ) 
00179             // Q_jolt = ---------------------------------------- * delta_Q
00180             //           ( ( I_world^-1 • ( r × N ) ) × r ) • N
00181             //
00182             Q_jolt[i] = inverse_I_world[i] 
00183                       * RelativePosition[i].Cross( Normal ) 
00184                       * ( delta_Q / inverseAngInertia[i] );
00185 
00186             Body[i]->Orientation += 0.5 * Q_jolt[i] * Body[i]->Orientation;
00187 
00188             // Now, normalize orientation and recalculate transformation matrix
00189             //
00190             Body[i]->CalculateDerivedQuantities (); 
00191         }
00192     }
00193 }