World of Rigid Bodies (WoRB)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
WoRB_TestBed.cpp
Go to the documentation of this file.
00001 /**
00002  *  @file      WoRB_TestBed.cpp
00003  *  @brief     Implementation of the WoRB_TestBed, a GLUT based WoRB application.
00004  *  @author    Mikica Kocic
00005  *  @version   0.3
00006  *  @date      2012-05-04
00007  *  @copyright GNU Public License.
00008  */
00009 
00010 #include "WoRB.h"
00011 #include "Utilities.h"
00012 #include "WoRB_TestBed.h"
00013 
00014 #include <cstdio>
00015 #include <algorithm> // std::min
00016 
00017 using namespace WoRB;
00018 
00019 /////////////////////////////////////////////////////////////////////////////////////////
00020 // Destructor. Deallocates all objects.
00021 //
00022 WoRB_TestBed::~WoRB_TestBed ()
00023 {
00024     for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i )
00025     {
00026         delete *i;
00027     }
00028 
00029     Objects.clear ();
00030 
00031     IsInitialized = false;
00032 }
00033 
00034 /////////////////////////////////////////////////////////////////////////////////////////
00035 // Runs the simulation and rendering.
00036 //
00037 void WoRB_TestBed::Run ()
00038 {
00039     Printf( "WoRB: WoRB_TestBed: Run\n" );
00040 
00041     OnProcessData (); // Process initial simulation data
00042     
00043     glutPopWindow ();
00044 
00045     while ( IsRunning )
00046     {
00047         Simulate ();
00048         glutMainLoopEvent ();
00049     }
00050 
00051     glutDestroyWindow( WindowId );
00052     for ( unsigned i = 0; i < 10; ++i ) {
00053         glutMainLoopEvent ();
00054     }
00055 
00056     Printf( "WoRB: Destroyed GLUT window %d\n", WindowId );
00057 
00058     IsInitialized = false;
00059 }
00060 
00061 /////////////////////////////////////////////////////////////////////////////////////////
00062 // Delayed constructor
00063 //
00064 void WoRB_TestBed::Initialize ()
00065 {
00066     Printf( "WoRB: WoRB_TestBed: Initialize\n" );
00067 
00068     WindowTitle = "Lab4: World of Rigid Bodies";
00069 
00070     // Initialize all properties to default values
00071     //
00072     TestSuite            = 0;      // Start test-suite #0
00073     IsInitialized        = false;  // Not initialized yet
00074     IsRunning            = true;   // Main-loop ends, when set to false
00075     IsPaused             = false;  // Is simulation paused: no
00076     AutoPause            = false;  // Is single-step mode: no
00077     Wireframe            = false;  // Show bodies in wireframe instead of solid: no
00078     ShowBodyAxes         = true;   // Show body axes: yes
00079     ShowFloorMirror      = false;  // Show objects mirrored in the floor: no
00080     ShowContacts         = false;  // Show contact normals: no
00081     ShowTrajectories     = false;  // Show object's trajectories: no
00082     ShowStateVariables   = true;   // Show system state variables: yes
00083     ShowHelp             = true;   // Show short help: yes
00084     GridTickLength       = 1.0;    // Tick size of the grid, in meters
00085     GridTicks            = 50;     // Number of ticks in the grid
00086     TimeStep             = 0.01;   // Integrator time-step, in seconds
00087     TimeStepsPerFrame    = 1;      // Number of time-steps solved per one video frame
00088     TimeStepsPerSnapshot = 20;     // Number of time-steps per one trajectory snapshot
00089     CameraZoom           = 15.0;   // Position in m, from the coordinate system origin
00090     CameraLookAt.x       = -2.0;   // Look at x = -2 m
00091     CameraLookAt.y       = 2.0;    // Look at y = 2 m (height)
00092     CameraLookAt.z       = 0.0;    // Look at z = 0 m
00093     CameraAngle          = 55.0;   // Angle in degrees, left/right of x-axis
00094     CameraElevation      = 25.0;   // Angle in degres, up/bellow the horizon
00095     FollowObject         = 0;      // Follow the first object (with camera)
00096     LastDisplayTime      = 0.0;    // Force immediate update
00097     FinalTime            = 0.0;    // No final (when simulation ends) time
00098 }
00099 
00100 /////////////////////////////////////////////////////////////////////////////////////////
00101 // Dumps all parameter values.
00102 //
00103 void WoRB_TestBed::Dump () const
00104 {
00105     Printf( "IsInitialized        : %s\n",  IsInitialized       ? "true" : "false" );
00106     Printf( "IsRunning            : %s\n",  IsRunning           ? "true" : "false" );
00107     Printf( "IsPaused             : %s\n",  IsPaused            ? "true" : "false" );
00108     Printf( "AutoPause            : %s\n",  AutoPause           ? "true" : "false" );
00109     Printf( "Wireframe            : %s\n",  Wireframe           ? "true" : "false" );
00110     Printf( "ShowBodyAxes         : %s\n",  ShowBodyAxes        ? "true" : "false" );
00111     Printf( "ShowFloorMirror      : %s\n",  ShowFloorMirror     ? "true" : "false" );
00112     Printf( "ShowContacts         : %s\n",  ShowContacts        ? "true" : "false" );
00113     Printf( "ShowTrajectories     : %s\n",  ShowTrajectories    ? "true" : "false" );
00114     Printf( "ShowStateVariables   : %s\n",  ShowStateVariables  ? "true" : "false" );
00115     Printf( "ShowHelp             : %s\n",  ShowHelp            ? "true" : "false" );
00116 
00117     Printf( "GridTickLength       : %g m\n", GridTickLength       );
00118     Printf( "GridTicks            : %d\n",   GridTicks            );
00119 
00120     Printf( "TimeStep             : %g s\n", TimeStep             );
00121     Printf( "TimeStepsPerFrame    : %u\n",   TimeStepsPerFrame    );
00122     Printf( "TimeStepsPerSnapshot : %u\n",   TimeStepsPerSnapshot );
00123     Printf( "FinalTime            : %g s\n", FinalTime            );
00124 
00125     Printf( "FollowObject         : %lu\n",  FollowObject         );
00126     Printf( "CameraAngle          : %g°\n",  CameraAngle          );
00127     Printf( "CameraElevation      : %g°\n",  CameraElevation      );
00128     Printf( "CameraZoom           : %g m\n", CameraZoom           );
00129     Printf( "CameraLookAt         : [ %g, %g, %g ] m\n", 
00130         CameraLookAt.x, CameraLookAt.y, CameraLookAt.z );
00131 
00132     // Now display objects in the system
00133     //
00134     for ( unsigned i = 0; i < Objects.size (); ++i )
00135     {
00136         const Geometry&  g = Objects.at(i)->GetGeometry ();
00137         const RigidBody& b = Objects.at(i)->GetBody ();
00138 
00139         Printf( "\nObject %d\n", i + 1 );
00140 
00141         Printf( "Geometry         : %s\n", g.GetName () );
00142 
00143         if ( g.IsSphere () )
00144         {
00145             const Sphere& s = (const Sphere&)g;
00146             Printf( "Radius           : %g m\n", s.Radius );
00147         }
00148         else if ( g.IsCuboid () )
00149         {
00150             const Cuboid& c = (const Cuboid&)g;
00151             Printf( "Half-Extent      : [ %g, %g, %g ] m\n", 
00152                 c.HalfExtent.x, c.HalfExtent.y, c.HalfExtent.z );
00153         }
00154 
00155         Printf( "Mass             : %g kg\n", b.Mass () );
00156 
00157         Printf( "Position         : [ %g, %g, %g | %g ] m\n", 
00158             b.Position.x, b.Position.y, b.Position.z, b.Position.w );
00159 
00160         Printf( "Orientation      : [ %g, %g, %g | %g ]\n", 
00161             b.Orientation.x, b.Orientation.y, b.Orientation.z, b.Orientation.w );
00162 
00163         Printf( "Linear Momentum  : [ %g, %g, %g | %g ] kg m s^-1\n", 
00164             b.LinearMomentum.x, b.LinearMomentum.y, 
00165             b.LinearMomentum.z, b.LinearMomentum.w );
00166 
00167         Printf( "Angular Momentum : [ %g, %g, %g | %g ] kg m^2 s^-1m\n", 
00168             b.AngularMomentum.x, b.AngularMomentum.y, 
00169             b.AngularMomentum.z, b.AngularMomentum.w );
00170 
00171         Printf( "Velocity         : [ %g, %g, %g | %g ] m s^-1\n", 
00172             b.Velocity.x, b.Velocity.y, b.Velocity.z, b.Velocity.w );
00173 
00174         Printf( "Angular Velocity : [ %g, %g, %g | %g ] s^-1\n", 
00175             b.AngularVelocity.x, b.AngularVelocity.y, 
00176             b.AngularVelocity.z, b.AngularVelocity.w );
00177 
00178         Printf( "Kinetic Energy   : %g J\n", b.KineticEnergy );
00179     }
00180 }
00181 
00182 /////////////////////////////////////////////////////////////////////////////////////////
00183 // Creates the GLUT window and initializes the view.
00184 //
00185 void WoRB_TestBed::SetupAnimation ()
00186 {
00187     // Setup common execution options for GLUT: not to call exit() when finished
00188     //
00189     glutSetOption( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION );
00190 
00191     // Create GLUT window
00192     //
00193     glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
00194     glutInitWindowSize( 800, 600 );
00195     
00196     WindowId = glutCreateWindow( WindowTitle );
00197 
00198     glutForegroundWindow ();
00199 
00200     Printf( "WoRB: Created GLUT window %d\n", WindowId );
00201 
00202     // Initialize ambient light
00203     //
00204     GLfloat lightAmbient[] = { 0.5, 0.5, 0.5, 1 };
00205     glLightfv( GL_LIGHT0, GL_AMBIENT, lightAmbient );
00206 
00207     GLfloat lightDiffuse[] = { 1, 1, 1, 1 };
00208     glLightfv( GL_LIGHT0, GL_DIFFUSE, lightDiffuse );
00209 
00210     glEnable( GL_LIGHT0 );
00211 
00212     glClearColor( 1.0f, 1.0f, 1.0f, 1.0f );
00213     glEnable( GL_DEPTH_TEST );
00214     glShadeModel( GL_SMOOTH );
00215 
00216     // Finally, setup camera position
00217     //
00218     SetupProjection ();
00219 
00220     // .. and mark the instance as initialized.
00221     //
00222     IsInitialized = true;
00223 }
00224 
00225 /////////////////////////////////////////////////////////////////////////////////////////
00226 // Sets the projection characteristics of the camera.
00227 //
00228 void WoRB_TestBed::SetupProjection ()
00229 {
00230     double aspect = double( glutGet( GLUT_WINDOW_WIDTH ) ) 
00231                     / std::max( 1, glutGet( GLUT_WINDOW_HEIGHT ) );
00232 
00233     aspect = std::min( 2e3, std::max( -2e3, aspect ) );
00234 
00235     glMatrixMode( GL_PROJECTION );
00236     glLoadIdentity ();
00237     gluPerspective( 45.0, aspect, 1.0, 500.0 );
00238     glMatrixMode( GL_MODELVIEW );
00239 }
00240 
00241 /////////////////////////////////////////////////////////////////////////////////////////
00242 // Updates the current state (solves ODE) of the system.
00243 //
00244 void WoRB_TestBed::Simulate ()
00245 {
00246     // Reconfigure the test-bed, if requested
00247     //
00248     if ( TestSuite >= 0 )
00249     {
00250         ReconfigureTestBed ();
00251         TestSuite = -1; // Set the flag to 'initialized'
00252     }
00253 
00254     // Just refresh display and sleep some time, when paused
00255     //
00256     if ( IsPaused )
00257     {
00258         glutPostRedisplay ();
00259 
00260         // Just wait
00261         double durationMs = TimeStep * TimeStepsPerFrame * 1e3; // in milliseconds
00262         Pause( (unsigned long)durationMs );
00263 
00264         return;
00265     }
00266 
00267     // If not paused, solve ODE
00268     //
00269     worb.SolveODE( TimeStep );
00270 
00271     // Process data calculated during the simulation, e.g. save data.
00272     //
00273     OnProcessData ();
00274 
00275     if ( FinalTime > 0 && worb.Time >= FinalTime )
00276     {
00277         IsRunning = false;
00278     }
00279 
00280     // Take a snapshot of object's positions, when snapshot time comes
00281     //
00282     if ( worb.TimeStepCount % TimeStepsPerSnapshot == 0 && ShowTrajectories )
00283     {
00284         for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i )
00285         {
00286             if ( (*i)->ShowTrajectory ) {
00287                 TrajectoryItem ti;
00288                 ti.object = *i;
00289                 (*i)->GetBody ().ToWorld.GetGLTransform( ti.matrix );
00290                 Trajectories.push_back( ti );
00291             }
00292         }
00293     }
00294 
00295     // Animate objects, when (animation frame) time comes 
00296     //
00297     if ( worb.TimeStepCount % TimeStepsPerFrame == 0 || AutoPause )
00298     {
00299         glutPostRedisplay ();
00300     }
00301 
00302     // Clear auto-pause flag, i.e. force user to set `IsPaused = false` 
00303     // and `AutoPause = true` for the next simulation single-step.
00304     //
00305     if ( AutoPause )
00306     {
00307         AutoPause = false;
00308         IsPaused = true;
00309     }
00310 }
00311 
00312 /////////////////////////////////////////////////////////////////////////////////////////
00313 // Renders the current scene.
00314 //
00315 void WoRB_TestBed::DisplayEventHandler ()
00316 {
00317     // Adjust camera's 'look-at' position depending on the selected object to follow
00318     //
00319     if ( FollowObject < Objects.size () )
00320     {
00321          CameraLookAt = Objects.at( FollowObject )->GetBody ().Position;
00322     }
00323 
00324     // Clear the viewport and setup the camera direction
00325     //
00326     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
00327     glLoadIdentity ();
00328 
00329     gluLookAt( /*eye*/ CameraZoom, 0, 0, /*center*/ 0, 0, 0, /*up*/ 0, 1, 0 );
00330 
00331     glTranslated ( -CameraZoom,      0, 0    ); // move away = zoom out
00332     glRotated    ( -CameraElevation, 0, 0, 1 ); // z-rotate = elevation
00333     glRotated    (  CameraAngle,     0, 1, 0 ); // y-rotate = angle
00334 
00335     glTranslated ( -CameraLookAt.x, -CameraLookAt.y, -CameraLookAt.z );
00336 
00337     if ( CameraElevation >= -8.0 )
00338     {
00339         // Render objects reflected in the floor (mirror)
00340         //
00341         if ( ShowFloorMirror )
00342         {
00343             glEnable( GL_DEPTH_TEST );
00344             glEnable( GL_LIGHTING );
00345             glEnable( GL_BLEND );
00346 
00347             glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
00348             glEnable( GL_COLOR_MATERIAL );
00349 
00350             static const GLfloat lightPosition[] = { 1, -1, 0, 0 };
00351             glLightfv( GL_LIGHT0, GL_POSITION, lightPosition );
00352 
00353             // A transform matrix for rendering objects reflected in the floor
00354             //
00355             static const GLdouble floorMirrorTransform [] =
00356             {
00357                 1,  0, 0, 0,
00358                 0, -1, 0, 0,
00359                 0,  0, 1, 0,
00360                 0,  0, 0, 1
00361             };
00362 
00363             glPushMatrix();
00364             glMultMatrixd( floorMirrorTransform );
00365 
00366             for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i )
00367             {
00368                 (*i)->Render( GLUT_Renderer::FloorMirror );
00369             }
00370 
00371             glPopMatrix();
00372         }
00373 
00374         glDisable( GL_COLOR_MATERIAL );
00375         glDisable( GL_LIGHTING );
00376         glDisable( GL_DEPTH_TEST );
00377         glDisable( GL_BLEND );
00378     }
00379 
00380     // Render a tiled ground plane i.e. the xz-grid
00381     //
00382     glColor3d( 0.95, 0.95, 0.85 );
00383 
00384     glBegin( GL_LINES );
00385     for ( int x = -GridTicks; x <= GridTicks; ++x )
00386     {
00387         for ( int z = -GridTicks; z <= GridTicks; ++z ) 
00388         {
00389             glVertex3d(  x * GridTickLength, 0, -z * GridTickLength );
00390             glVertex3d(  x * GridTickLength, 0,  z * GridTickLength );
00391             glVertex3d(  x * GridTickLength, 0,  z * GridTickLength );
00392             glVertex3d( -x * GridTickLength, 0,  z * GridTickLength );
00393         }
00394     }
00395     glEnd ();
00396 
00397     // Display the main axes
00398     //
00399     RenderAxes( 10 * GridTickLength );
00400 
00401     // Render ground shadows of the objects
00402     //
00403     glEnable( GL_BLEND );
00404     glDisable( GL_DEPTH_TEST );
00405     glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
00406 
00407     glColor4d( 0.1, 0.1, 0, 0.1 ); 
00408     for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i )
00409     {
00410         (*i)->Render( GLUT_Renderer::BodyShadow );
00411     }
00412 
00413     // Render the objects themselves
00414     //
00415     glEnable( GL_LIGHTING );
00416 
00417     static const GLfloat lightPositionForMirror [] = { 1, 1, 0, 0 };
00418     glLightfv( GL_LIGHT0, GL_POSITION, lightPositionForMirror );
00419 
00420     glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
00421     glEnable( GL_COLOR_MATERIAL );
00422 
00423     if ( Wireframe ) {
00424         glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
00425     }
00426     else {
00427         glEnable( GL_DEPTH_TEST );
00428     }
00429 
00430     for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i )
00431     {
00432         (*i)->Render( GLUT_Renderer::BodyShape );
00433     }
00434 
00435     glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
00436 
00437     glDisable( GL_COLOR_MATERIAL );
00438     glDisable( GL_LIGHTING );
00439 
00440     // Render trajectories
00441     //
00442     if ( ShowTrajectories )
00443     {
00444         for ( Trajectory::iterator i = Trajectories.begin(); i != Trajectories.end(); ++i )
00445         {
00446             (*i).object->RenderWireframe( (*i).matrix );
00447         }
00448     }
00449 
00450     glDisable( GL_DEPTH_TEST );
00451     glDisable( GL_BLEND );
00452 
00453     // Render any additional information
00454 
00455     RenderDebugInfo ();
00456 
00457     // Update the display and swap double-buffers
00458     //
00459     glFlush ();
00460     glutSwapBuffers ();
00461 
00462     // Remember the current time
00463     //
00464     double currentTime = glutGet( GLUT_ELAPSED_TIME );
00465 
00466     double durationMs = TimeStep * TimeStepsPerFrame * 1e3; // in milliseconds
00467     durationMs -= currentTime - LastDisplayTime;
00468 
00469     if ( durationMs > 0 ) {
00470         Pause( (unsigned long)durationMs );
00471     }
00472 
00473     LastDisplayTime = currentTime;
00474 }
00475 
00476 /////////////////////////////////////////////////////////////////////////////////////////
00477 // Renders state variables and short help.
00478 //
00479 void WoRB_TestBed::RenderDebugInfo ()
00480 {
00481     // Draw axes of the body
00482     //
00483     if ( ShowBodyAxes )
00484     {
00485         for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i )
00486         {
00487             (*i)->Render( GLUT_Renderer::BodyAxes );
00488         }
00489     }
00490 
00491     // Display the state variables of the system
00492     //
00493     if ( ShowStateVariables )
00494     {
00495         GLOrthoScreen _inScreenCoordinates; // Establish temporary transform for text
00496 
00497         glColor3d( 0, 0, 0.7 );
00498 
00499         const double E_k = worb.TotalKineticEnergy;
00500         const double E_p = worb.TotalPotentialEnergy;
00501         const Quaternion& p_tot = worb.TotalLinearMomentum;
00502         const Quaternion& L_tot = worb.TotalAngularMomentum;
00503 
00504         int row = glutGet( GLUT_WINDOW_HEIGHT ) - 20;
00505         row = RenderPrintf( 10, row, 
00506             "N = %4lu, t = %6.3lf%s\n"
00507             "E_t/k/p %12.3lf %12.3lf %12.3lf\n"
00508             "p_tot   %12.3lf %12.3lf %12.3lf\n"
00509             "L_tot   %12.3lf %12.3lf %12.3lf",
00510             worb.TimeStepCount, worb.Time,
00511             IsPaused || AutoPause ? " (Paused)" : "",
00512             E_k + E_p, E_k, E_p,
00513             p_tot.x, p_tot.y, p_tot.z,
00514             L_tot.x, L_tot.y, L_tot.z
00515         );
00516 
00517         // Display the state variables of the rigid bodies
00518         //
00519         unsigned nShown = unsigned( Objects.size () );
00520         nShown = std::min( IsPaused || AutoPause ? 4u : 0u, nShown );
00521         for ( unsigned i = 0; i < nShown; ++i )
00522         {
00523             const RigidBody&  b = Objects.at(i)->GetBody ();
00524             const Quaternion& x = b.Position;
00525             const Quaternion& q = b.Orientation;
00526             const Quaternion& p = b.LinearMomentum;
00527             const Quaternion& L = b.AngularMomentum;
00528             const Quaternion& v = b.Velocity;
00529             const Quaternion& w = b.AngularVelocity;
00530 
00531             row = RenderPrintf( 10, row, 
00532                 "(%d)   x %12.3lf %12.3lf %12.3lf\n"
00533                 "      q %12.3lf %12.3lf %12.3lf %12.3lf\n"
00534                 "      p %12.3lf %12.3lf %12.3lf\n"
00535                 "      L %12.3lf %12.3lf %12.3lf\n"
00536                 "      v %12.3lf %12.3lf %12.3lf\n"
00537                 "      w %12.3lf %12.3lf %12.3lf",
00538                 i + 1, x.x, x.y, x.z,
00539                        q.x, q.y, q.z, q.w,
00540                        p.x, p.y, p.z,
00541                        L.x, L.y, L.z,
00542                        v.x, v.y, v.z,
00543                        w.x, w.y, w.z
00544             );
00545         }
00546     }
00547 
00548     // Display short help
00549     //
00550     if ( ShowHelp )
00551     {
00552         GLOrthoScreen _inScreenCoordinates; // Establish temporary transform for text
00553 
00554         glColor3d( 0, 0, 0 );
00555         RenderPrintf( 10, 4 * 25, 
00556             "Shortcut keys:\n"
00557             "  1, 2, ... for different simulation\n"
00558             "  (P)ause, (S)ingle-step, (Q)uit\n"
00559             "  (A)xes, (V)ariables, (C)ontacts, (T)rajectories\n"
00560             "  (W)ireframe, Floor (M)irror, (F)ullscreen"
00561         );
00562         glColor3d( 0, 0, 1 );
00563         RenderPrintf( 10, 10, 
00564             "Camera: a= %+5.1lf, e= %+5.1lf, d= %+5.1lf, at= %+5.1lf %+5.1lf %+5.1lf",
00565             CameraAngle, CameraElevation, CameraZoom,
00566             CameraLookAt.x, CameraLookAt.y,  CameraLookAt.z
00567         );
00568     }
00569 
00570     // Display contact normals. 
00571     // Green between two objects, red between objects and scenery e.g. floor.
00572     //
00573     if ( ShowContacts )
00574     {
00575         glLineWidth( 3 );
00576         glBegin( GL_LINES );
00577 
00578         for ( unsigned i = 0; i < worb.Collisions.Count (); ++i )
00579         {
00580             Quaternion pos = worb.Collisions[i].Position;
00581             Quaternion n = worb.Collisions[i].Normal;
00582             Quaternion end = pos + n;
00583 
00584             if ( worb.Collisions[i].WithScenery () ) {
00585                 glColor3d( 1, 0, 0 ); // red body
00586             } else {
00587                 glColor3d( 0, 1, 0 ); // green body
00588             }
00589 
00590             glVertex3d( pos.x, pos.y, pos.z );
00591             glVertex3d( end.x, end.y, end.z );
00592 
00593             pos = end;
00594             end = pos + n * 0.1;
00595 
00596             glColor3d( 0, 0, 1 ); // blue head
00597             glVertex3d( pos.x, pos.y, pos.z );
00598             glVertex3d( end.x, end.y, end.z );
00599         }
00600 
00601         glEnd ();
00602         glLineWidth( 1 );
00603     }
00604 }
00605 
00606 /////////////////////////////////////////////////////////////////////////////////////////
00607 // Called when GLUT detects a mouse drag.
00608 //
00609 void WoRB_TestBed::MotionEventHandler( int x, int y )
00610 {
00611     int modifiers = glutGetModifiers ();
00612 
00613     if ( modifiers == GLUT_ACTIVE_CTRL && LastMouse.state == GLUT_DOWN )
00614     {
00615         CameraZoom += 0.5 * ( y - LastMouse.y );
00616         CameraZoom = std::max( 0.5, std::min( 300.0, CameraZoom ) );
00617     }
00618     else if ( modifiers == GLUT_ACTIVE_SHIFT && LastMouse.state == GLUT_DOWN )
00619     {
00620         double k  = CameraZoom * 2e-3;
00621         double dx = k * ( x - LastMouse.x );
00622         double dy = k * ( y - LastMouse.y );
00623 
00624         double phi   = CameraAngle     * Const::Pi / 180.0;
00625         double theta = CameraElevation * Const::Pi / 180.0;
00626 
00627         CameraLookAt.y +=  dy * cos( theta );
00628         CameraLookAt.x += -dx * sin( phi ) - dy * cos( phi ) * sin( theta );
00629         CameraLookAt.z +=  dx * cos( phi ) - dy * sin( phi ) * sin( theta );
00630 
00631         if ( CameraLookAt.y < 0 ) {
00632             CameraLookAt.y = 0;
00633         }
00634     }
00635     else
00636     {
00637         CameraAngle += 0.25 * ( x - LastMouse.x );
00638         while( CameraAngle < -180.0 ) {
00639             CameraAngle += 360.0;
00640         }
00641         while ( CameraAngle > 180.0 ) {
00642             CameraAngle -= 360.0;
00643         }
00644 
00645         CameraElevation += 0.25 * ( y - LastMouse.y );
00646         CameraElevation = std::max( -20.0, std::min( 90.0, CameraElevation ) );
00647     }
00648 
00649     // Remember the current mouse position
00650     //
00651     LastMouse.x = x;
00652     LastMouse.y = y;
00653 }
00654 
00655 /////////////////////////////////////////////////////////////////////////////////////////
00656 // Called when GLUT detects a key press.
00657 //
00658 void WoRB_TestBed::KeyboardEventHandler( unsigned char key )
00659 {
00660     switch( key )
00661     {
00662         case 'A': case 'a': // Toggle displaying axes of the rigid bodies
00663             ShowBodyAxes = ! ShowBodyAxes;
00664             break;
00665 
00666         case 'C': case 'c': // Toggle displaying contact info
00667             ShowContacts = ! ShowContacts;
00668             break;
00669 
00670         case 'F': case 'f': // Toggle fullscreen mode
00671             glutFullScreenToggle ();
00672             break;
00673 
00674         case 'H': case 'h': // Toggle help mode
00675             ShowHelp = ! ShowHelp;
00676             break;
00677 
00678         case 'M': case 'm': // Toggle floor mirror
00679             ShowFloorMirror = ! ShowFloorMirror;
00680             break;
00681 
00682         case 'Q': case 'q': // Quit application
00683             IsRunning = false;
00684             break;
00685 
00686         case 'P': case 'p': case ' ': // Toggle running the simulation
00687             IsPaused = ! IsPaused;
00688             break;
00689 
00690         case 'S': case 's': case '\r': // Advance only one time-step
00691             AutoPause = true;
00692             IsPaused = false;
00693             break;
00694 
00695         case 'T': case 't': // Toggle displaying trajectories
00696             ShowTrajectories = ! ShowTrajectories;
00697             // Trajectories.clear ();
00698             break;
00699 
00700         case 'V': case 'v': // Toggle displaying state variables
00701             ShowStateVariables = ! ShowStateVariables;
00702             break;
00703 
00704         case 'W': case 'w': // Toggle wireframe mode
00705             Wireframe = ! Wireframe;
00706             break;
00707 
00708         case '1': case '2': case '3': case '4': 
00709         case '5': case '6': case '7': case '8': case '9':
00710             TestSuite = key - '1';
00711             break;
00712     }
00713 }
00714 
00715 /////////////////////////////////////////////////////////////////////////////////////////
00716 // Called when GLUT detects a function key press.
00717 //
00718 void WoRB_TestBed::SpecialKeyEventHandler( int key )
00719 {
00720     switch( key )
00721     {
00722         case GLUT_KEY_F1: // Follow body 1
00723             FollowObject = 0;
00724             break;
00725 
00726         case GLUT_KEY_F2: // Follow body 2
00727             FollowObject = 1;
00728             break;
00729 
00730         case GLUT_KEY_F3: // Follow body 3
00731             FollowObject = 2;
00732             break;
00733 
00734         case GLUT_KEY_F4: // Follow body 4
00735             FollowObject = 3;
00736             break;
00737 
00738         case GLUT_KEY_F11: // Look at coordinate origin, with offset of Oxyz bisectris
00739             FollowObject    = 0xFFFFu;
00740             CameraLookAt    = 0.0;
00741             CameraAngle     = 55.0;
00742             CameraElevation = 25.0;
00743             CameraZoom      = 20;
00744             break;
00745 
00746         case GLUT_KEY_F12: // Look at coordinate origin, from above
00747             FollowObject    = 0xFFFFu;
00748             CameraLookAt    = 0.0;
00749             CameraAngle     = 0.0;
00750             CameraElevation = 90.0;
00751             CameraZoom      = 30;
00752             break;
00753 
00754     }
00755 }
00756 
00757 /////////////////////////////////////////////////////////////////////////////////////////
00758 // Clears the current simulation data and prepares a new simulation.
00759 //
00760 void WoRB_TestBed::ClearTestBed ()
00761 {
00762     // Remove rigid body references from the WoRB solver
00763     //
00764     worb.RemoveObjects ();
00765 
00766     // Setup default parameters for collision detection/resolve algorithms
00767     //
00768     worb.Collisions.Restitution = 1;    // Coefficient of restitution
00769     worb.Collisions.Relaxation  = 0.2;  // Position projection relaxation coefficient
00770     worb.Collisions.Friction    = 0;    // Dynamic friction coefficient
00771 
00772     // Disable gravity
00773     //
00774     worb.Gravity = 0.0;
00775 
00776     // Initialize the ground plane and the walls
00777     //
00778     double boxHalfSize = GridTicks * GridTickLength;
00779     GroundPlane.Direction =  Const::Y;
00780     GroundPlane.Offset    =  0.0;
00781     BoxWall[0].Direction  =  Const::X; // left
00782     BoxWall[0].Offset     = -boxHalfSize;
00783     BoxWall[1].Direction  = -Const::X; // right
00784     BoxWall[1].Offset     = -boxHalfSize;
00785     BoxWall[2].Direction  =  Const::Z; // rear
00786     BoxWall[2].Offset     = -boxHalfSize;
00787     BoxWall[3].Direction  = -Const::Z; // front
00788     BoxWall[3].Offset     = -boxHalfSize;
00789 
00790     // Add the ground plane and the walls to our WoRB solver
00791     //
00792     worb.Add( GroundPlane );
00793 
00794     // for ( unsigned i = 0; i < 4; ++i ) {
00795     //     worb.Add( BoxWall[i] );
00796     // }
00797 
00798     // Remove existing trajectories
00799     //
00800     Trajectories.clear ();
00801     Trajectories.reserve( 10000 );
00802 
00803     // Remove existing objects
00804     //
00805     for ( RBObjects::iterator i = Objects.begin(); i != Objects.end(); ++i ) {
00806         delete *i;
00807     }
00808 
00809     Objects.clear ();
00810     Objects.reserve( 64 );
00811 }
00812 
00813 /////////////////////////////////////////////////////////////////////////////////////////
00814 // Initializes the test-bed with the default objects according to selected profile.
00815 //
00816 void WoRB_TestBed::ReconfigureTestBed ()
00817 {
00818     /////////////////////////////////////////////////////////////////////////////////////
00819 
00820     ClearTestBed ();       // Clear existing simulation, if any.
00821     LastDisplayTime = 0;   // Force display refresh
00822 
00823     if ( TestSuite >= 6 ) {
00824         return;
00825     }
00826 
00827     /////////////////////////////////////////////////////////////////////////////////////
00828 
00829     /* LAB4 */
00830 
00831     ShowBodyAxes = true;
00832 
00833     double thick = 0.01;
00834     double v = -1;
00835     double mass = 0.1;
00836     double L = 5.0;
00837 
00838     if ( TestSuite >= 1 )
00839     {
00840         thick = 0.7;
00841         v = -20;
00842         mass = 10e3;
00843     }
00844 
00845     /////////////////////////////////////////////////////////////////////////////////////
00846     // Add new objects
00847 
00848     Box* box1 = new Box(
00849         /*x=*/ SpatialVector( -L/2, 3, 0 ), 
00850         /*q=*/ Quaternion::FromAxisAngle( Const::Pi/2, 0, 1, 0 ),
00851         /*v=*/ 0.0, /*w=*/ 0.0,
00852         /*extent=*/ SpatialVector( L, thick, L/2 ), /*mass=*/ mass
00853     );
00854     Objects.push_back( box1 );
00855     worb.Add( box1 );
00856 
00857     /////////////////////////////////////////////////////////////////////////////////////
00858 
00859     Box* box2 = new Box(
00860         /*x*/ SpatialVector( L - v, 3, L/2 ), /*q=*/ Quaternion( 0, 0, 1, 0 ),
00861         /*v*/ v * Const::X, /*w=*/ 0.0,
00862         /*extent=*/ SpatialVector( L, thick, L/2 ), /*mass=*/ mass
00863     );
00864 
00865     Objects.push_back( box2 );
00866     worb.Add( box2 );
00867 
00868     /////////////////////////////////////////////////////////////////////////////////////
00869 
00870     if ( TestSuite >= 1 ) 
00871     {
00872         // box2->Orientation.w -= 1e-4;
00873         box2->Orientation.w += 1e-4;
00874 
00875         box1->RigidBody::Position.y += 1.0;
00876         box2->RigidBody::Position.y += 1.01;
00877     }
00878 
00879     /////////////////////////////////////////////////////////////////////////////////////
00880 
00881     if ( TestSuite >= 2 && TestSuite <= 3 )
00882     {
00883         for ( int i = 0; i < 30; ++i ) {
00884         Ball* ball = new Ball(
00885             RandomQuaternion( SpatialVector( 1, 3, 0 ), SpatialVector( 1, 20, 0 ) ),
00886             RandomQuaternion (), 
00887             /*v=*/ 0.0, /*w=*/ 0.0, /*r=*/ 0.5, /*mass=*/ 1e1
00888         );
00889 
00890         Objects.push_back( ball );
00891         worb.Add( ball );
00892         }
00893     }
00894 
00895     /////////////////////////////////////////////////////////////////////////////////////
00896 
00897     if ( TestSuite >= 2 )
00898     {
00899         worb.Gravity = Const::g_n; // add gravity
00900 
00901         ShowBodyAxes = false;
00902 
00903         box1->SetMass( 3 );
00904         box1->Body->Position.y = 5;
00905         box1->Body->CanBeDeactivated = true;
00906 
00907         box2->Body->Position.y = 5;
00908         box2->Body->CanBeDeactivated = true;
00909     }
00910 
00911     if ( TestSuite >= 3 )
00912     {
00913         worb.Collisions.Restitution = 0.2;
00914         worb.Collisions.Friction = 0.2;
00915     }
00916 
00917     if ( TestSuite >= 4 )
00918     {
00919         ShowBodyAxes = false;
00920 
00921         for ( int i = 0; i < 50; ++i )
00922         {
00923             Box* box;
00924             if ( TestSuite >= 5 ) {
00925                 box2->Body->Velocity *= 0.8;
00926                 box2->Body->CalculateDerivedQuantities( false );
00927 
00928                 box = new Box(
00929                     /*x*/ SpatialVector( L, i * 0.4 + 0.2, L/2 ), 
00930                     /*q*/ Quaternion( 1.0 ),
00931                     /*v*/ 0.0, /*w*/ 0.0,
00932                     /*extent=*/ SpatialVector( 2, 0.2, 2 ),
00933                     /*mass=*/ mass
00934                 );
00935             }
00936             else {
00937                 box2->Body->Velocity = 0.0;
00938                 box2->Body->CalculateDerivedQuantities( false );
00939                 worb.Collisions.Relaxation = 0.0;
00940 
00941                 box = new Box(
00942                     /*x*/ SpatialVector( L, i * 0.4 + 0.2, L/2 ), 
00943                     /*q*/ RandomQuaternion (),
00944                     /*v*/ 0.0, /*w*/ 0.0,
00945                     /*extent=*/ RandomQuaternion(
00946                         SpatialVector( 0.5, 0.5, 0.5 ), SpatialVector( 1, 2, 3 ) ),
00947                     /*mass=*/ mass
00948                 );
00949 
00950                 box->ActiveColor = RandomQuaternion ();
00951                 box->ActiveColor.A = 0.8f;
00952             }
00953             box->Body->CanBeDeactivated = true;
00954 
00955             Objects.push_back( box );
00956             worb.Add( box );
00957         }
00958     }
00959 
00960     /////////////////////////////////////////////////////////////////////////////////////
00961     // Finally, initialize ODE (calculate derived quantities for recently added objects)
00962 
00963     worb.InitializeODE ();
00964 }