src/object/physics.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *  Wormux is a convivial mass murder game.
00003  *  Copyright (C) 2001-2004 Lawrence Azzoug.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00018  ******************************************************************************
00019  * Abstract class used for physical object (object with a size, mass,
00020  * etc.). This object can have differents state : ready, is moving, or ghost
00021  * (is outside of the world).
00022  *
00023  * You can : make the object move (with collision test), change state, etc.
00024  * If the object go outside of the world, it become a ghost.
00025  *****************************************************************************/
00026 
00027 #include "../object/physics.h"
00028 #include <stdlib.h>
00029 #include <iostream>
00030 #include "../game/config.h"
00031 #include "../game/game_mode.h"
00032 #include "../game/time.h"
00033 #include "../tool/debug.h"
00034 #include "../tool/math_tools.h"
00035 #include "../map/wind.h"
00036 
00037 // Physical constants
00038 const double STOP_REBOUND_LIMIT = 0.5 ;
00039 const double AIR_RESISTANCE_FACTOR = 40.0 ;
00040 const double PHYS_DELTA_T = 0.02 ;         // Physical simulation time step
00041 const double PENDULUM_REBOUND_FACTOR = 0.8 ;
00042 
00043 Physics::Physics ()
00044 {
00045   m_extern_force.Clear();
00046   m_pos_x.Clear();
00047   m_pos_y.Clear();
00048 
00049   m_fix_point_gnd.Clear();
00050   m_fix_point_dxy.Clear();
00051   m_rope_angle.Clear();
00052   m_rope_length.Clear();
00053 
00054   m_rope_elasticity = 10.0 ;
00055   m_elasticity_damping = 0.20 ;
00056   m_balancing_damping = 0.40 ;
00057 
00058   m_motion_type = NoMotion ;
00059   m_elasticity_off = true;
00060 
00061   m_last_move = Time::GetInstance()->Read() ;
00062 }
00063 
00064 void Physics::ResetConstants()
00065 {
00066   // Load the constants (mass, air_resistance...) into the object
00067   *((ObjectConfig*)this) = m_cfg;
00068 }
00069 
00070 Physics::~Physics () {}
00071 
00072 //---------------------------------------------------------------------------//
00073 //--                         Class Parameters SET/GET                      --//
00074 //---------------------------------------------------------------------------//
00075 
00076 // Set / Get positions
00077 
00078 void Physics::SetPhysXY(double x, double y)
00079 {
00080   m_pos_x.x0 = x;
00081   m_pos_y.x0 = y;
00082 }
00083 
00084 void Physics::SetPhysXY(const Point2d &position){
00085         SetPhysXY(position.x, position.y);
00086 }
00087 
00088 double Physics::GetPhysX() const
00089 {
00090   return m_pos_x.x0;
00091 }
00092 
00093 double Physics::GetPhysY() const
00094 {
00095   return m_pos_y.x0;
00096 }
00097 
00098 Point2d Physics::GetPos() const{
00099         return Point2d( m_pos_x.x0, m_pos_y.x0);
00100 }
00101 
00102 void Physics::SetPhysSize (double width, double height)
00103 {
00104   m_phys_width = width ;
00105   m_phys_height = height ;
00106 }
00107 
00108 // Set positions
00109 void Physics::SetMass(double mass)
00110 {
00111   m_mass = mass ;
00112 }
00113 
00114 // Set the wind factor
00115 void Physics::SetWindFactor (double factor)
00116 {
00117   m_wind_factor = factor;
00118 }
00119 
00120 // Set the air resist factor
00121 void Physics::SetAirResistFactor (double factor)
00122 {
00123   m_air_resist_factor = factor;
00124 }
00125 
00126 // Set the wind factor
00127 void Physics::SetGravityFactor (double factor)
00128 {
00129   m_gravity_factor = factor;
00130 }
00131 
00132 void Physics::SetSpeed (double length, double angle)
00133 {
00134   Point2d vector( length*cos(angle), length*sin(angle) );
00135   SetSpeedXY(vector);
00136 }
00137 
00138 void Physics::SetSpeedXY (Point2d vector)
00139 {
00140   if (EgalZero(vector.x)) vector.x = 0;
00141   if (EgalZero(vector.y)) vector.y = 0;
00142   bool was_moving = IsMoving();
00143 
00144   m_pos_x.x1 = vector.x ;
00145   m_pos_y.x1 = vector.y ;
00146   m_motion_type = FreeFall ;
00147 
00148   if (!was_moving && IsMoving()) StartMoving();
00149 }
00150 
00151 void Physics::AddSpeed (double length, double angle)
00152 {
00153   Point2d vector( length*cos(angle), length*sin(angle) );
00154   AddSpeedXY (vector);
00155 }
00156 
00157 void Physics::AddSpeedXY (Point2d vector)
00158 {
00159   if (EgalZero(vector.x)) vector.x = 0;
00160   if (EgalZero(vector.y)) vector.y = 0;
00161   bool was_moving = IsMoving();
00162 
00163   m_pos_x.x1 += vector.x ;
00164   m_pos_y.x1 += vector.y ;
00165   m_motion_type = FreeFall ;
00166 
00167   if (!was_moving && IsMoving()) StartMoving();
00168 }
00169 
00170 void Physics::GetSpeed(double &norm, double &angle) const
00171 {
00172   Point2d speed ;
00173 
00174   switch (m_motion_type) {
00175     case FreeFall:
00176       GetSpeedXY(speed);
00177       norm = speed.Norm();
00178       angle = speed.ComputeAngle();
00179       break ;
00180 
00181     case Pendulum:
00182       // Transform angular speed to linear speed.
00183 
00184       norm = fabs(m_rope_length.x0 * m_rope_angle.x1);
00185 
00186       if (m_rope_angle.x1 > 0)
00187         angle = fabs(m_rope_angle.x0) ;
00188       else
00189         angle = fabs(m_rope_angle.x0) - M_PI ;
00190 
00191       if (m_rope_angle.x0 < 0)
00192         angle = -angle ;
00193       break;
00194 
00195     case NoMotion:
00196       norm = 0.0;
00197       angle = 0.0;
00198       break;
00199 
00200     default:
00201       assert(false);
00202       break ;
00203   }
00204 }
00205 
00206 void Physics::GetSpeedXY(Point2d &vector) const
00207 {
00208   if(!IsMoving())
00209     {
00210       vector.Clear();
00211       return;
00212     }
00213   vector.SetValues(m_pos_x.x1, m_pos_y.x1);
00214 }
00215 
00216 Point2d Physics::GetSpeed() const
00217 {
00218   Point2d tmp;
00219   GetSpeedXY(tmp);
00220   return tmp;
00221 }
00222 
00223 double Physics::GetAngularSpeed() const
00224 {
00225   return m_rope_angle.x1 ;
00226 }
00227 
00228 double Physics::GetSpeedAngle() const
00229 {
00230   double angle ;
00231   Point2d speed ;
00232 
00233   GetSpeedXY(speed);
00234   angle = speed.ComputeAngle();
00235 
00236   return angle ;
00237 }
00238 
00239 void Physics::SetExternForce (double length, double angle)
00240 {
00241   Point2d vector(length*cos(angle), length*sin(angle));
00242 
00243   SetExternForceXY(vector);
00244 }
00245 
00246 void Physics::SetExternForceXY (Point2d vector)
00247 {
00248   bool was_moving = IsMoving();
00249 
00250   m_extern_force.SetValues(vector);
00251 
00252   if (!was_moving && IsMoving())
00253     StartMoving();
00254 }
00255 
00256 // Set fixation point positions
00257 void Physics::SetPhysFixationPointXY(double g_x, double g_y, double dx,
00258                                      double dy)
00259 {
00260   double fix_point_x, fix_point_y ;
00261   double old_length ;
00262 
00263   Point2d V ;
00264   m_fix_point_gnd.x = g_x ;
00265   m_fix_point_gnd.y = g_y ;
00266   m_fix_point_dxy.x = dx ;
00267   m_fix_point_dxy.y = dy ;
00268 
00269   //  printf ("Fixation (%f,%f) dxy(%f,%f)\n",  g_x, g_y, dx, dy);
00270 
00271   fix_point_x = m_pos_x.x0 + dx ;
00272   fix_point_y = m_pos_y.x0 + dy ;
00273 
00274   old_length = m_rope_length.x0 ;
00275   m_rope_length.x0 = Point2d(fix_point_x,fix_point_y).Distance( Point2d(g_x,g_y) );
00276 
00277   if (m_motion_type == Pendulum)
00278     {
00279       // We was already fixed. By changing the fixation point, we have
00280       // to recompute the angular speed depending of the new rope length.
00281       m_rope_angle.x1 = m_rope_angle.x1 * old_length / m_rope_length.x0 ;
00282     }
00283   else
00284     {
00285       // We switch from a regular move to a pendulum move.
00286       // Compute the initial angle
00287       V.x = fix_point_x - g_x ;
00288       V.y = fix_point_y - g_y ;
00289       m_rope_angle.x0 = M_PI_2 - V.ComputeAngle() ;
00290 
00291       // Convert the linear speed to angular speed.
00292       m_rope_angle.x1 = (m_pos_x.x1 * cos(m_rope_angle.x0) +
00293                          m_pos_y.x1 * sin(m_rope_angle.x0) ) / m_rope_length.x0;
00294 
00295       // Reset the angular acceleration.
00296       m_rope_angle.x2 = 0 ;
00297 
00298       bool was_moving = IsMoving();
00299       m_motion_type = Pendulum ;
00300       if (!was_moving && IsMoving()) StartMoving();
00301     }
00302 }
00303 
00304 void Physics::UnsetPhysFixationPoint()
00305 {
00306   double speed_norm, angle ;
00307 
00308   GetSpeed (speed_norm, angle);
00309 
00310   angle = -angle ;
00311 
00312   SetSpeed(speed_norm, angle);
00313 
00314   m_pos_x.x2 = 0 ;
00315   m_pos_y.x2 = 0 ;
00316 
00317   m_rope_angle.Clear();
00318   m_rope_length.Clear();
00319 
00320   m_motion_type = FreeFall ;
00321 }
00322 
00323 void Physics::ChangePhysRopeSize(double dl)
00324 {
00325   if ((dl < 0) && (m_rope_length.x0 < 0.5))
00326     return ;
00327 
00328   bool was_moving = IsMoving();
00329 
00330   m_rope_length.x0 += dl ;
00331 
00332   // Recompute angular speed depending on the new rope length.
00333   m_rope_angle.x1 = m_rope_angle.x1 * (m_rope_length.x0 - dl) / m_rope_length.x0 ;
00334 
00335   if (!was_moving && IsMoving()) StartMoving();
00336 }
00337 
00338 double Physics::GetRopeAngle()
00339 {
00340   return m_rope_angle.x0 ;
00341 }
00342 
00343 double Physics::GetRopeLength()
00344 {
00345   return m_rope_length.x0;
00346 }
00347 
00348 //---------------------------------------------------------------------------//
00349 //--                            Physical Simulation                        --//
00350 //---------------------------------------------------------------------------//
00351 
00352 void Physics::StartMoving()
00353 {
00354   m_last_move = Time::GetInstance()->Read();
00355 
00356   if (m_motion_type == NoMotion)
00357     m_motion_type = FreeFall ;
00358 
00359   MSG_DEBUG ("physic.physic", "Start moving.");
00360 }
00361 
00362 void Physics::StopMoving()
00363 {
00364   if (IsMoving()) MSG_DEBUG ("physic.physic", "End of a movement...");
00365   // Always called by PhysicalObj::StopMoving
00366   m_pos_x.x1 = 0 ;
00367   m_pos_x.x2 = 0 ;
00368   m_pos_y.x1 = 0 ;
00369   m_pos_y.x2 = 0 ;
00370   if (m_motion_type != Pendulum)
00371     m_motion_type = NoMotion ;
00372 
00373   m_extern_force.Clear();
00374 }
00375 
00376 bool Physics::IsMoving() const
00377 {
00378   return ( (!EgalZero(m_pos_x.x1)) ||
00379            (!EgalZero(m_pos_y.x1)) ||
00380            (!m_extern_force.IsNull() ) ||
00381            (m_motion_type != NoMotion) ) ;
00382 //         (m_motion_type == Pendulum) ) ;
00383 }
00384 
00385 bool Physics::IsFalling() const
00386 {
00387   return ( ( m_motion_type == FreeFall ) &&
00388            ( m_pos_y.x1 > 0.1) );
00389 }
00390 
00391 // Compute the next position of the object during a pendulum motion.
00392 void Physics::ComputePendulumNextXY (double delta_t)
00393 {
00394   //  double l0 = 5.0 ;
00395 
00396   //  printf ("Physics::ComputePendulumNextXY - Angle %f\n", m_rope_angle.x0);
00397 
00398   // Elactic rope length equation
00399   // l" + D.l' + (k/m - a'^2).l = g.cos a + k/m . l0
00400 
00401 //   ComputeOneEulerStep(m_rope_length,
00402 //                    /* a */ 1,
00403 //                    /* b */ m_elasticity_damping,
00404 //                    /* c */ m_rope_elasticity / m_mass - m_rope_angle.x1 * m_rope_angle.x1,
00405 //                    /* d */ game_mode.gravity * cos (m_rope_angle.x0) + m_rope_elasticity/m_mass * l0,
00406 //                       delta_t);
00407 
00408   // Pendulum motion equation (angle equation)
00409   // a'' + (D + 2.l'/l).a' = -g/l.sin a + F/l.cos a
00410   m_rope_angle.ComputeOneEulerStep(
00411                       /* a */ 1,
00412                       /* b */ m_balancing_damping + 2 * m_rope_length.x1 / m_rope_length.x0,
00413                       /* c */ 0,
00414                       /* d */ -GameMode::GetInstance()->gravity / m_rope_length.x0 * sin (m_rope_angle.x0)
00415                               +m_extern_force.x / m_rope_length.x0 * cos (m_rope_angle.x0),
00416                       delta_t);
00417 
00418   double x = m_fix_point_gnd.x - m_fix_point_dxy.x
00419              + m_rope_length.x0 * sin(m_rope_angle.x0);
00420   double y = m_fix_point_gnd.y - m_fix_point_dxy.y
00421              + m_rope_length.x0 * cos(m_rope_angle.x0);
00422 
00423   //  printf ("Physics::ComputePendulumNextXY - Angle(%f,%f,%f)\n",
00424   //      m_rope_angle.x0, m_rope_angle.x1, m_rope_angle.x2);
00425 
00426   SetPhysXY(x,y);
00427 }
00428 
00429 // Compute the next position of the object during a free fall.
00430 void Physics::ComputeFallNextXY (double delta_t)
00431 {
00432   double speed_norm, speed_angle ;
00433   double air_resistance_factor ;
00434 
00435   double weight_force ;
00436   double wind_force ;
00437 
00438   // Free fall motion equation
00439   // m.g + wind -k.v = m.a
00440 
00441   // Weight force = m * g
00442 
00443   // printf ("av : (%5f,%5f) - (%5f,%5f) - (%5f,%5f)\n", m_pos_x.x0,
00444   //      m_pos_y.x0, m_pos_x.x1, m_pos_y.x1, m_pos_x.x2, m_pos_y.x2);
00445 
00446   weight_force = GameMode::GetInstance()->gravity * m_gravity_factor * m_mass ;
00447 
00448   // Wind force
00449 
00450   wind_force = wind.GetStrength() * m_wind_factor ;
00451 
00452   // Air resistanceance factor
00453 
00454   GetSpeed(speed_norm, speed_angle);
00455 
00456   air_resistance_factor = AIR_RESISTANCE_FACTOR * m_air_resist_factor ;
00457 
00458   MSG_DEBUG( "physic.fall", "Fall %s; mass %5f, weight %5f, wind %5f, air %5f", typeid(*this).name(), m_mass, weight_force,wind_force, air_resistance_factor);
00459 
00460   // Equation on X axys : m.x'' + k.x' = wind
00461   m_pos_x.ComputeOneEulerStep(m_mass, air_resistance_factor, 0,
00462                       wind_force + m_extern_force.x, delta_t);
00463 
00464   // Equation on Y axys : m.y'' + k.y' = m.g
00465   m_pos_y.ComputeOneEulerStep(m_mass, air_resistance_factor, 0,
00466                       weight_force + m_extern_force.y, delta_t);
00467 
00468 
00469     // printf ("F : Pd(%5f) EF(%5f)\n", weight_force, m_extern_force.y);
00470 
00471    // printf ("ap : (%5f,%5f) - (%5f,%5f) - (%5f,%5f)\n", m_pos_x.x0,
00472   //      m_pos_y.x0, m_pos_x.x1, m_pos_y.x1, m_pos_x.x2, m_pos_y.x2);
00473 }
00474 
00475 // Compute the position of the object at current time.
00476 Point2d Physics::ComputeNextXY(double delta_t){
00477   if (m_motion_type == FreeFall)
00478     ComputeFallNextXY(delta_t);
00479 
00480   if (m_motion_type == Pendulum)
00481     ComputePendulumNextXY(delta_t);
00482 
00483   m_last_move = Time::GetInstance()->Read() ;
00484 
00485   return Point2d(m_pos_x.x0, m_pos_y.x0);
00486 }
00487 
00488 void Physics::RunPhysicalEngine()
00489 {
00490   double step_t, delta_t = (Time::GetInstance()->Read() - m_last_move) / 1000.0;
00491   Point2d oldPos;
00492   Point2d newPos;
00493 
00494   step_t = PHYS_DELTA_T;
00495 
00496   //  printf ("Delta_t = %f (last %f - current %f)\n", delta_t, m_last_move/1000.0,
00497   //      global_time.Read()/1000.0);
00498 
00499   // Compute object move for each physical engine time step.
00500 
00501   while (delta_t > 0.0){
00502     if (delta_t < PHYS_DELTA_T)
00503       step_t = delta_t ;
00504 
00505     oldPos = GetPos();
00506 
00507     newPos = ComputeNextXY(step_t);
00508 
00509     if( newPos != oldPos)  {
00510       // The object has moved. Notify the son class.
00511       MSG_DEBUG( "physic.move", "Move %s (%f, %f) -> (%f, %f)", typeid(*this).name(), oldPos.x, oldPos.y, newPos.x, newPos.y);
00512       NotifyMove(oldPos, newPos);
00513     }
00514 
00515     delta_t -= PHYS_DELTA_T ;
00516   }
00517 
00518   return;
00519 }
00520 
00521 /* contact_angle is the angle of the surface we are rebounding on */
00522 void Physics::Rebound(Point2d contactPos, double contact_angle)
00523 {
00524   double norme, angle;
00525 
00526   // Get norm and angle of the object speed vector.
00527   GetSpeed(norme, angle);
00528 
00529   switch (m_motion_type) {
00530   case FreeFall :
00531     if (m_rebounding)
00532     {
00533       // Compute rebound angle.
00534       /* if no tangent rebound in the opposit direction */
00535       if(contact_angle == NAN)
00536         angle = angle + M_PI ;
00537       else
00538         angle =  M_PI - angle -2.0 *  contact_angle;
00539 
00540       // Apply rebound factor to the object speed.
00541       norme = norme * m_rebound_factor;
00542 
00543       // Apply the new speed to the object.
00544       SetSpeed(norme, angle);
00545 
00546       // Check if we should stop rebounding.
00547       if (norme < STOP_REBOUND_LIMIT){
00548         StopMoving();
00549         return;
00550       }
00551       SignalRebound();
00552     }
00553     else
00554       StopMoving();
00555     break;
00556 
00557   case Pendulum:
00558     {
00559       Point2d V ;
00560 
00561       // Recompute new angle.
00562       V.x = m_pos_x.x0 + m_fix_point_dxy.x - m_fix_point_gnd.x;
00563       V.y = m_pos_y.x0 + m_fix_point_dxy.y - m_fix_point_gnd.y;
00564 
00565       m_rope_angle.x0 = M_PI_2 - V.ComputeAngle();
00566 
00567       // Convert the linear speed of the rebound to angular speed.
00568       V.x = PENDULUM_REBOUND_FACTOR * norme * cos(angle);
00569       V.y = PENDULUM_REBOUND_FACTOR * norme * sin(angle);
00570 
00571       angle = angle + M_PI;
00572 
00573       m_rope_angle.x1 = (norme * cos(angle) * cos(m_rope_angle.x0) +
00574                          norme * sin(angle) * sin(m_rope_angle.x0) ) / m_rope_length.x0;
00575 
00576       m_rope_angle.x2 = 0;
00577       m_extern_force.Clear();
00578     }
00579     break ;
00580 
00581   default:
00582     break ;
00583   }
00584 
00585 }
00586 
00587 void Physics::SignalGhostState(bool)  {}
00588 void Physics::SignalDeath() {}
00589 void Physics::SignalDrowning() {}
00590 void Physics::SignalRebound() {}

Generated on Mon Jan 1 13:10:59 2007 for Wormux by  doxygen 1.4.7