|
World of Rigid Bodies (WoRB)
|
00001 #ifndef _WORB_COLLISION_H_INCLUDED 00002 #define _WORB_COLLISION_H_INCLUDED 00003 00004 /** 00005 * @file Collision.h 00006 * @brief Definitions for the Collision class which implements the collision 00007 * response for a single collision. 00008 * @author Mikica Kocic 00009 * @version 0.18 00010 * @date 2012-05-02 00011 * @copyright GNU Public License. 00012 */ 00013 00014 #include "RigidBody.h" 00015 00016 namespace WoRB { 00017 00018 /** Encalpsulates a collision event between two bodies; contains contact details. 00019 */ 00020 class Collision 00021 { 00022 friend class CollisionResolver; 00023 00024 public: 00025 ///////////////////////////////////////////////////////////////////////////////// 00026 /** @name Collision state variables */ 00027 /*@{*/ 00028 /** Holds the first rigid body that is involved in the collision. 00029 */ 00030 RigidBody* Body_A; 00031 00032 /** Holds the second rigid body that is involved in the collision. 00033 * It is null in case of the collision with a scenery. 00034 */ 00035 RigidBody* Body_B; 00036 00037 /** Holds the position of the contact in world frame of reference. 00038 */ 00039 Quaternion Position; 00040 00041 /** Holds the direction of the contact in world frame of reference. 00042 */ 00043 Quaternion Normal; 00044 00045 /** Holds the penetration depth at the point of contact. 00046 */ 00047 double Penetration; 00048 00049 /** Holds the position projections coefficient for this collision. 00050 */ 00051 double Restitution; 00052 00053 /** Holds the friction coefficient for this collision. 00054 */ 00055 double Friction; 00056 00057 /** Returns true if the collision is with scenery. 00058 */ 00059 bool WithScenery () const 00060 { 00061 return ! Body_B; 00062 } 00063 00064 private: 00065 /*@}*/ 00066 ///////////////////////////////////////////////////////////////////////////////// 00067 /** @name Collision response methods */ 00068 /*@{*/ 00069 /** Updates the derived quantities from the state data. 00070 * @note Should be called before the collision response algorithm executes. 00071 */ 00072 void UpdateDerivedQuantities( double h /*!< Time-step */ ) 00073 { 00074 if ( ! Body_A ) // Reverse the contact and swap the bodies 00075 { 00076 Normal = -Normal; // Reverse the contact normal 00077 00078 RigidBody* temp = Body_A; // Swap bodies 00079 Body_A = Body_B; 00080 Body_B = temp; 00081 } 00082 00083 // Calculate an set of axis at the point of contact. 00084 // 00085 FindOrthonormalBasisAtContactPoint (); 00086 00087 // Find the relative position and velocity relative to each body 00088 // 00089 RelativePosition[0] = Position - Body_A->Position; 00090 Velocity = GetRelativeVelocity( Body_A, RelativePosition[0], h ); 00091 00092 if ( Body_B ) 00093 { 00094 RelativePosition[1] = Position - Body_B->Position; 00095 Velocity -= GetRelativeVelocity( Body_B, RelativePosition[1], h ); 00096 } 00097 00098 // Calculate the desired change in velocity for the collision resolution 00099 // 00100 BouncingVelocity = GetBouncingVelocity( h ); 00101 } 00102 00103 /** Applies the linear & angular impulses needed to resolve the collision. 00104 */ 00105 void ImpulseTransfer( 00106 Quaternion V_jolt[2], //!< Returned applied velocity jolt 00107 Quaternion W_jolt[2] //!< Returned applied angular velocity jolt 00108 ); 00109 00110 /** Applies the linear position jolt needed to resolve the collision. 00111 */ 00112 void PositionProjection( 00113 Quaternion X_jolt[2], //!< Returned applied position jolt 00114 Quaternion Q_jolt[2], //!< Returned applied orientation jolt 00115 double relaxation //!< Position projections relaxation coefficient 00116 ); 00117 00118 private: 00119 /*@}*/ 00120 ///////////////////////////////////////////////////////////////////////////////// 00121 /** @name Derived quantities */ 00122 /*@{*/ 00123 /** Contains a transformation matrix from body to world frame of reference. 00124 */ 00125 QTensor ToWorld; 00126 00127 /** Holds the relative velocity v_A - v_B between bodies at the point of contact. 00128 */ 00129 Quaternion Velocity; 00130 00131 /** Holds the required change in velocity for this contact to be resolved. 00132 * The basic expression is `-( 1 + COR ) * Velocity.x` 00133 */ 00134 double BouncingVelocity; 00135 00136 /** Holds the position of the contact point in world frame, relative to the 00137 * center of each body. 00138 */ 00139 Quaternion RelativePosition[2]; 00140 /*@}*/ 00141 ///////////////////////////////////////////////////////////////////////////////// 00142 /** @name Methods that calculates derived quantities */ 00143 /*@{*/ 00144 /** Calculates the impulse needed to resolve the collision without friction. 00145 */ 00146 Quaternion GetImpulse (); 00147 00148 /** Calculates the impulse needed to resolve the collision in general case. 00149 */ 00150 Quaternion GetImpulse_IncludeFriction (); 00151 00152 /** Activates (only) inactive bodies in a collision. 00153 * Collisions with the scenery (in case where body B is null) never 00154 * cause a body to be activated. 00155 */ 00156 void ActivateInactiveBodies () 00157 { 00158 if ( Body_B && ( Body_A->IsActive ^ Body_B->IsActive ) ) 00159 { 00160 if ( Body_A->IsActive ) { 00161 Body_B->Activate (); 00162 } 00163 else { 00164 Body_A->Activate (); 00165 } 00166 } 00167 } 00168 00169 /** Calculates an orthonormal basis for the contact point. 00170 */ 00171 void FindOrthonormalBasisAtContactPoint () 00172 { 00173 Quaternion tangent_Y, tangent_Z; 00174 00175 if ( fabs( Normal.x ) > fabs( Normal.y ) ) // Z-axis is nearer to the Y axis 00176 { 00177 double length = 1.0 / sqrt( Normal.z * Normal.z + Normal.x * Normal.x ); 00178 00179 // The new X-axis is at right angles to the world Y-axis 00180 tangent_Y.x = Normal.z * length; 00181 tangent_Y.y = 0; 00182 tangent_Y.z = -Normal.x * length; 00183 00184 // The new Y-axis is at right angles to the new X- and Z- axes 00185 tangent_Z.x = Normal.y * tangent_Y.x; 00186 tangent_Z.y = Normal.z * tangent_Y.x - Normal.x * tangent_Y.z; 00187 tangent_Z.z = -Normal.y * tangent_Y.x; 00188 tangent_Z.Normalize (); 00189 } 00190 else // Z-axis is nearer to the X axis 00191 { 00192 double length = 1.0 / sqrt( Normal.z * Normal.z + Normal.y * Normal.y ); 00193 00194 // The new X-axis is at right angles to the world X-axis 00195 tangent_Y.x = 0; 00196 tangent_Y.y = -Normal.z * length; 00197 tangent_Y.z = Normal.y * length; 00198 00199 // The new Y-axis is at right angles to the new X- and Z- axes 00200 tangent_Z.x = Normal.y * tangent_Y.z - Normal.z * tangent_Y.y; 00201 tangent_Z.y = -Normal.x * tangent_Y.z; 00202 tangent_Z.z = Normal.x * tangent_Y.y; 00203 tangent_Z.Normalize (); 00204 } 00205 00206 /* Orthonormal basis is a 3x3 matrix, where each vector is a column. 00207 * The X-direction is generated from the contact normal, and the Y and Z 00208 * directions are set so they are at right angles to it. 00209 */ 00210 ToWorld.SetColumnVectors( Normal, tangent_Y, tangent_Z ); 00211 } 00212 00213 /** Gets the relative velocity for the point of contact on the given body. 00214 */ 00215 Quaternion GetRelativeVelocity( 00216 RigidBody* body, //!< The pointer to the rigid body 00217 const Quaternion& relativePosition, //!< Relative position to the contact 00218 double h //!< The last time-step 00219 ) 00220 { 00221 // Calculate the velocity of the contact point in contact coordinates 00222 // 00223 Quaternion V_world = body->Velocity 00224 + body->AngularVelocity.Cross( relativePosition ); 00225 00226 Quaternion V = ToWorld.TransformInverse( V_world ); 00227 00228 // Calculate the ammount of velocity that is due to forces without reactions, 00229 // ignoring any component of acceleration in the contact normal direction 00230 // (where only tangential/planar components are considered) 00231 // 00232 Quaternion dV_world = body->InverseMass * body->Force * h; 00233 00234 Quaternion dV = ToWorld.TransformInverse( dV_world ); 00235 00236 dV.x = 0; // consider only tangential components 00237 00238 // Add the tangential velocities (they will removed during the impulse 00239 // transfer, if there's enough friction). 00240 // 00241 return V + dV; 00242 } 00243 00244 /** Calculates the bouncing velocity required to resolve the collision. 00245 * The bouncing velocity is `-( 1 + COR ) * NormalVelocity.x`. 00246 */ 00247 double GetBouncingVelocity( double h /*!< Time-step */ ) 00248 { 00249 // Calculate the normal component of the velocity induced by the force 00250 // accumulated in the last time step. 00251 // 00252 double dV_fromForce_x = 0; 00253 if ( Body_A->IsActive ) 00254 { 00255 Quaternion last_dV = Body_A->InverseMass * Body_A->Force * h; 00256 dV_fromForce_x += last_dV.Dot( Normal ); 00257 } 00258 if ( Body_B && Body_B->IsActive ) 00259 { 00260 Quaternion last_dV = Body_B->InverseMass * Body_B->Force * h; 00261 dV_fromForce_x -= last_dV.Dot( Normal ); 00262 } 00263 00264 // Limit the restitution in case when the velocity is very low. 00265 // 00266 double COR = fabs( Velocity.x - dV_fromForce_x ) < 0.25 ? 0.0 : Restitution; 00267 00268 // The result is the bouncing-velocity, reduced for the velocity that was 00269 // induced by the force exerted in the last time-step. 00270 // 00271 return - ( 1 + COR ) * Velocity.x + COR * dV_fromForce_x; 00272 } 00273 /*@}*/ 00274 ///////////////////////////////////////////////////////////////////////////////// 00275 /** @name Miscellaneous methods */ 00276 /*@{*/ 00277 /** Displays the collision state variables on standard output. 00278 */ 00279 private: void Dump( unsigned id, double currentTime ) const; 00280 /*@}*/ 00281 ///////////////////////////////////////////////////////////////////////////////// 00282 }; 00283 } // namespace WoRB 00284 00285 #endif // _WORB_COLLISION_H_INCLUDED