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