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