Main.cpp

Go to the documentation of this file.
00001 /* -*- c++ -*- */
00002 /////////////////////////////////////////////////////////////////////////////
00003 //
00004 // Main.cpp -- Copyright (c) 2007 David Henry
00005 // last modification: may. 7, 2007
00006 //
00007 // This code is licenced under the MIT license.
00008 //
00009 // This software is provided "as is" without express or implied
00010 // warranties. You may freely copy and compile this source into
00011 // applications you distribute provided that the copyright text
00012 // below is included in the resulting source code.
00013 //
00014 // Main file of the MD3 Player loader.
00015 //
00016 /////////////////////////////////////////////////////////////////////////////
00017 
00018 #include <iostream>
00019 #include <iomanip>
00020 #include <sstream>
00021 #include <cstdarg>
00022 #include <vector>
00023 #include <GL/glew.h>
00024 #include <GL/glut.h>
00025 
00026 #include "md3-model/Md3Player.h"
00027 #include "md3-model/TextureManager.h"
00028 
00029 using std::vector;
00030 using std::cout;
00031 using std::cerr;
00032 using std::endl;
00033 
00034 // Keyboard
00035 struct keyboard_input_t
00036 {
00037   unsigned char keymap[256];
00038   int special[256];
00039   int modifiers;
00040 
00041 } keyboard;
00042 
00043 // Mouse
00044 struct mouse_input_t
00045 {
00046   int buttons[3];
00047   int x, y;
00048 
00049 } mouse;
00050 
00051 // Timer
00052 struct glut_timer_t
00053 {
00054   double current_time;
00055   double last_time;
00056 
00057 } timer;
00058 
00059 // Camera
00060 struct Vector_3d
00061 {
00062   float x, y, z;
00063 
00064 } rot, eye;
00065 
00066 const string animStrings[kMaxAnimations] = {
00067   "Both death 1", "Both dead 1", "Both death 2", "Both dead 2",
00068   "Both death 3", "Both dead 3", "Torso gesture", "Torso attack",
00069   "Torso attack 2", "Torso drop", "Torso raise", "Torso stand",
00070   "Torso stand 2", "Legs walk crouch", "Legs walk", "Legs run",
00071   "Legs back", "Legs swim", "Legs jump", "Legs land", "Legs jump B",
00072   "Legs land B", "Legs idle", "Legs idle crouch", "Legs turn"
00073 };
00074 
00075 Md3Player *player = NULL;
00076 Md3Weapon *weapon = NULL;
00077 
00078 bool bTextured = true;
00079 bool bLightGL = true;
00080 bool bAnimated = true;
00081 bool bWeapon = false;
00082 
00083 int verbose = 2;
00084 int fps = 0;
00085 
00086 vector<string> skinList;
00087 
00088 
00089 // --------------------------------------------------------------------------
00090 // animMenu & skinMenu
00091 //
00092 // GLUT menu callback functions. Handle the menus. Select the skin
00093 // to use and the animation to play.
00094 // --------------------------------------------------------------------------
00095 
00096 static void
00097 animMenu (int item)
00098 {
00099   player->setAnimation (static_cast<Md3PlayerAnimType>(item));
00100 
00101   glutPostRedisplay ();
00102 }
00103 
00104 
00105 static void
00106 skinMenu (int item)
00107 {
00108   player->setSkin (skinList[item]);
00109 
00110   glutPostRedisplay ();
00111 }
00112 
00113 
00114 // -------------------------------------------------------------------------
00115 // buildSkinMenu
00116 //
00117 // Create GLUT menu for skin selection.
00118 // -------------------------------------------------------------------------
00119 
00120 int
00121 buildSkinMenu (const Md3Player::SkinMap &skinMap)
00122 {
00123   string skinName;
00124   int i = 0;
00125 
00126   Md3Player::SkinMap::const_iterator itor;
00127   for (itor = skinMap.begin (); itor != skinMap.end (); ++itor)
00128     skinList.push_back (itor->first);
00129 
00130   sort (skinList.begin (), skinList.end ());
00131 
00132   int menuId = glutCreateMenu (skinMenu);
00133 
00134   vector<string>::iterator it;
00135   for (it = skinList.begin (); it != skinList.end (); ++it)
00136     {
00137       skinName.assign (*it, it->find_last_of ('/') + 1, it->length ());
00138       glutAddMenuEntry (skinName.c_str (), i++);
00139     }
00140 
00141   return menuId;
00142 }
00143 
00144 
00145 // -------------------------------------------------------------------------
00146 // buildAnimMenu
00147 //
00148 // Create GLUT menu for animation selection.
00149 // -------------------------------------------------------------------------
00150 
00151 static int
00152 buildAnimMenu (int start, int end)
00153 {
00154   int menuId = glutCreateMenu (animMenu);
00155 
00156   for (int i = start; i < end; ++i)
00157     glutAddMenuEntry (animStrings[i].c_str (), i);
00158 
00159   return menuId;
00160 }
00161 
00162 
00163 // -------------------------------------------------------------------------
00164 // shutdownApp
00165 //
00166 // Application termination.
00167 // -------------------------------------------------------------------------
00168 
00169 static void
00170 shutdownApp ()
00171 {
00172   Texture2DManager::kill ();
00173 
00174   delete player;
00175   delete weapon;
00176 }
00177 
00178 
00179 // -------------------------------------------------------------------------
00180 // init
00181 //
00182 // Application initialization.  Setup keyboard input, mouse input,
00183 // timer, camera and OpenGL.
00184 // -------------------------------------------------------------------------
00185 
00186 static void
00187 init (const string &playerpath, const string &weaponpath)
00188 {
00189   //
00190   // GLEW Initialization
00191   //
00192 
00193   GLenum err = glewInit ();
00194   if (GLEW_OK != err)
00195     {
00196       // Problem: glewInit failed, something is seriously wrong.
00197       cerr << "Error: " << glewGetErrorString (err) << endl;
00198       shutdownApp ();
00199     }
00200 
00201   //
00202   // Application initialization
00203   //
00204 
00205   // Initialize keyboard
00206   memset (keyboard.keymap, 0, 256);
00207   memset (keyboard.special, 0, 256);
00208 
00209   // Inititialize mouse
00210   mouse.buttons[GLUT_LEFT_BUTTON] = GLUT_UP;
00211   mouse.buttons[GLUT_MIDDLE_BUTTON] = GLUT_UP;
00212   mouse.buttons[GLUT_RIGHT_BUTTON] = GLUT_UP;
00213   mouse.x = 0;
00214   mouse.y = 0;
00215 
00216   // Initialize timer
00217   timer.current_time = 0;
00218   timer.last_time = 0;
00219 
00220   // Initialize camera input
00221   rot.x = 0.0f;   eye.x = 0.0f;
00222   rot.y = 0.0f;   eye.y = 0.0f;
00223   rot.z = 0.0f;   eye.z = 8.0f;
00224 
00225   // Get base dir for player if a *.md3 file is given
00226   string playerdir (playerpath);
00227   if (playerdir.find (".md3") == playerdir.length () - 4)
00228 #ifdef _WIN32
00229     playerdir.assign (playerdir, 0, playerdir.find_last_of ('\\'));
00230 #else
00231     playerdir.assign (playerdir, 0, playerdir.find_last_of ('/'));
00232 #endif
00233 
00234   // Initialize MD3 player
00235   try
00236     {
00237       /*
00238       lower = new Md3Model ("models/players/harley/lower.md3");
00239       upper = new Md3Model ("models/players/harley/upper.md3");
00240       head = new Md3Model ("models/players/harley/head.md3");
00241 
00242       lower->link ("tag_torso", upper);
00243       upper->link ("tag_head", head);
00244 
00245       lower->setScale (0.1f);
00246       upper->setScale (0.1f);
00247       head->setScale (0.1f);
00248       */
00249 
00250       // Load MD3 player
00251       player = new Md3Player (playerdir);
00252       player->setScale (0.1f);
00253       player->setAnimation (kTorsoStand);
00254       player->setAnimation (kLegsIdle);
00255     }
00256   catch (std::runtime_error &err)
00257     {
00258       cerr << "Error: failed to load " << playerdir << endl;
00259       cerr << "Reason: " << err.what () << endl;
00260       exit (-1);
00261     }
00262 
00263   try
00264     {
00265       // Load weapon if a path is given
00266       if (!weaponpath.empty ())
00267         {
00268           weapon = new Md3Weapon (weaponpath);
00269           weapon->setScale (0.1f);
00270           player->linkWeapon (weapon);
00271           bWeapon = true;
00272         }
00273     }
00274   catch (std::runtime_error &err)
00275     {
00276       cerr << "Error: failed to load " << weaponpath << endl;
00277       cerr << "Reason: " << err.what () << endl;
00278     }
00279 
00280   //
00281   // Create GLUT menus
00282   //
00283 
00284   int deathAnimMenuId = buildAnimMenu (kBothDeath1, kTorsoGesture);
00285   int torsoAnimMenuId = buildAnimMenu (kTorsoGesture, kLegsWalkCr);
00286   int legsAnimMenuId = buildAnimMenu (kLegsWalkCr, kMaxAnimations);
00287   int skinMenuId = buildSkinMenu (player->skins ());
00288 
00289   glutCreateMenu (NULL);
00290   glutAddSubMenu ("Death animation", deathAnimMenuId);
00291   glutAddSubMenu ("Torso animation", torsoAnimMenuId);
00292   glutAddSubMenu ("Legs animation", legsAnimMenuId);
00293   glutAddSubMenu ("Skins", skinMenuId);
00294   glutAttachMenu (GLUT_RIGHT_BUTTON);
00295 
00296   //
00297   // Initialize OpenGL
00298   //
00299 
00300   glClearColor (0.5f, 0.5f, 0.5f, 1.0f);
00301   glShadeModel (GL_SMOOTH);
00302 
00303   glEnable (GL_DEPTH_TEST);
00304   glEnable (GL_TEXTURE_2D);
00305   glEnable (GL_CULL_FACE);
00306   glEnable (GL_LIGHTING);
00307   glEnable (GL_LIGHT0);
00308 
00309   glCullFace (GL_BACK);
00310 }
00311 
00312 
00313 // -------------------------------------------------------------------------
00314 // reshape
00315 //
00316 // OpenGL window resizing.  Update the viewport and the projection matrix.
00317 // -------------------------------------------------------------------------
00318 
00319 static void
00320 reshape (int w, int h)
00321 {
00322   if (h == 0)
00323     h = 1;
00324 
00325   glViewport (0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h));
00326 
00327   glMatrixMode (GL_PROJECTION);
00328   glLoadIdentity ();
00329   gluPerspective (45.0, w / static_cast<GLfloat>(h), 0.1, 1000.0);
00330 
00331   glMatrixMode (GL_MODELVIEW);
00332   glLoadIdentity ();
00333 
00334   glutPostRedisplay ();
00335 }
00336 
00337 
00338 // -------------------------------------------------------------------------
00339 // updateTimer
00340 //
00341 // Update the timer.
00342 // -------------------------------------------------------------------------
00343 
00344 static void
00345 updateTimer (struct glut_timer_t *t)
00346 {
00347   t->last_time = t->current_time;
00348   t->current_time = glutGet (GLUT_ELAPSED_TIME) * 0.001f;
00349 }
00350 
00351 
00352 // -------------------------------------------------------------------------
00353 // handleKeyboard
00354 //
00355 // Keyboard input handling.  Handle here continuous actions when a key
00356 // is pressed (like moving the camera).
00357 // -------------------------------------------------------------------------
00358 
00359 static void
00360 handleKeyboard (struct keyboard_input_t *k)
00361 {
00362   /*
00363   if (k->keymap['yourkey'])
00364     do_something ();
00365   */
00366 }
00367 
00368 
00369 // -------------------------------------------------------------------------
00370 // begin2D
00371 //
00372 // Enter into 2D mode.
00373 // -------------------------------------------------------------------------
00374 
00375 static void
00376 begin2D ()
00377 {
00378   int width = glutGet (GLUT_WINDOW_WIDTH);
00379   int height = glutGet (GLUT_WINDOW_HEIGHT);
00380 
00381   glMatrixMode (GL_PROJECTION);
00382   glPushMatrix ();
00383 
00384   glLoadIdentity ();
00385   glOrtho (0, width, 0, height, -1.0f, 1.0f);
00386 
00387   glMatrixMode (GL_MODELVIEW);
00388   glLoadIdentity ();
00389 }
00390 
00391 
00392 // -------------------------------------------------------------------------
00393 // end2D
00394 //
00395 // Return from 2D mode.
00396 // -------------------------------------------------------------------------
00397 
00398 static void
00399 end2D ()
00400 {
00401   glMatrixMode (GL_PROJECTION);
00402   glPopMatrix ();
00403   glMatrixMode (GL_MODELVIEW);
00404 }
00405 
00406 
00407 // -------------------------------------------------------------------------
00408 // glPrintf
00409 //
00410 // printf-like function for OpenGL.
00411 // -------------------------------------------------------------------------
00412 
00413 static int
00414 glPrintf (const char *format, ...)
00415 {
00416   char buffer[1024];
00417   std::va_list arg;
00418   int ret;
00419 
00420   // Format the text
00421   va_start (arg, format);
00422     ret = std::vsnprintf (buffer, sizeof (buffer), format, arg);
00423   va_end (arg);
00424 
00425   // Print it
00426   for (unsigned int i = 0; i < strlen (buffer); ++i)
00427     glutBitmapCharacter (GLUT_BITMAP_HELVETICA_12, buffer[i]);
00428 
00429   return ret;
00430 }
00431 
00432 
00433 // -------------------------------------------------------------------------
00434 // gameLogic
00435 //
00436 // Perform application logic.
00437 // -------------------------------------------------------------------------
00438 
00439 static void
00440 gameLogic ()
00441 {
00442   // Calculate frame per seconds
00443   static double current_time = 0;
00444   static double last_time = 0;
00445   static int n = 0;
00446 
00447   n++;
00448   current_time = timer.current_time;
00449 
00450   if ((current_time - last_time) >= 1.0)
00451     {
00452       fps = n;
00453       n = 0;
00454       last_time = current_time;
00455     }
00456 
00457   // Animate player
00458   if (bAnimated)
00459     {
00460       double dt = timer.current_time - timer.last_time;
00461       player->animate (dt);
00462     }
00463 }
00464 
00465 
00466 // -------------------------------------------------------------------------
00467 // draw3D
00468 //
00469 // Render the 3D part of the scene.
00470 // -------------------------------------------------------------------------
00471 
00472 static void
00473 draw3D ()
00474 {
00475   // Clear window
00476   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00477   glLoadIdentity ();
00478 
00479   // Perform camera transformations
00480   glTranslated (-eye.x, -eye.y, -eye.z);
00481   glRotated (rot.x, 1.0f, 0.0f, 0.0f);
00482   glRotated (rot.y, 0.0f, 1.0f, 0.0f);
00483   glRotated (rot.z, 0.0f, 0.0f, 1.0f);
00484 
00485   glEnable (GL_DEPTH_TEST);
00486 
00487   if (bLightGL)
00488     glEnable (GL_LIGHTING);
00489 
00490   if (bTextured)
00491     glEnable (GL_TEXTURE_2D);
00492 
00493   // Draw objects
00494   player->draw ();
00495 
00496   glDisable (GL_LIGHTING);
00497   glDisable (GL_TEXTURE_2D);
00498   glDisable (GL_DEPTH_TEST);
00499 }
00500 
00501 
00502 // -------------------------------------------------------------------------
00503 // draw2D
00504 //
00505 // Render the 2D part of the scene.
00506 // -------------------------------------------------------------------------
00507 
00508 static void
00509 draw2D ()
00510 {
00511   begin2D ();
00512 
00513   glColor3f (1.0f, 1.0f, 1.0f);
00514 
00515   if (verbose > 0)
00516     {
00517       glRasterPos2i (10, 10);
00518       glPrintf ("FPS: %i", fps);
00519     }
00520 
00521   if (verbose > 1)
00522     {
00523       glRasterPos2i (10, glutGet (GLUT_WINDOW_HEIGHT) - 20);
00524       glPrintf ("Rendering player: \"%s\"", player->name ().c_str ());
00525 
00526       glRasterPos2i (10, glutGet (GLUT_WINDOW_HEIGHT) - 35);
00527       glPrintf ("Current skin: \"%s\"", player->skinName ().c_str ());
00528 
00529       if (weapon)
00530         {
00531           glRasterPos2i (10, glutGet (GLUT_WINDOW_HEIGHT) - 50);
00532           glPrintf ("Rendering weapon: \"%s\"", weapon->name ().c_str ());
00533 
00534           if (!bWeapon)
00535             glPrintf (" (hiden)");
00536         }
00537     }
00538 
00539   end2D ();
00540 }
00541 
00542 
00543 // -------------------------------------------------------------------------
00544 // display
00545 //
00546 // Render the main scene to the screen.
00547 // -------------------------------------------------------------------------
00548 
00549 static void
00550 display ()
00551 {
00552   gameLogic ();
00553 
00554   draw3D ();
00555 
00556   draw2D ();
00557 
00558   glutSwapBuffers ();
00559 }
00560 
00561 
00562 // -------------------------------------------------------------------------
00563 // keyPress
00564 //
00565 // Key press glut callback function.  Called when user press a key.
00566 // -------------------------------------------------------------------------
00567 
00568 static void
00569 keyPress (unsigned char key, int x, int y)
00570 {
00571   // Update key state
00572   keyboard.keymap[key] = 1;
00573 
00574   //
00575   // Handle here ponctual actions when
00576   // a key is pressed (like toggle ligthing)
00577   //
00578 
00579   // Escape
00580   if (key == 27)
00581     exit (0);
00582 
00583   if (key == 'a' || key == 'A')
00584     bAnimated = !bAnimated;
00585 
00586   if (key == 'l' || key == 'L')
00587     bLightGL = !bLightGL;
00588 
00589   if (key == 'p' || key == 'P')
00590     {
00591       bWeapon = !bWeapon;
00592 
00593       if (bWeapon && weapon)
00594         player->linkWeapon (weapon);
00595       else
00596         player->unlinkWeapon ();
00597     }
00598 
00599   if (key == 's' || key == 'S')
00600     glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
00601 
00602   if (key == 't' || key == 'T')
00603     bTextured = !bTextured;
00604 
00605   if (key == 'v' || key == 'V')
00606     verbose = (verbose + 1) % 3;
00607 
00608   if (key == 'w' || key == 'W')
00609     glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
00610 
00611   glutPostRedisplay ();
00612 }
00613 
00614 
00615 // -------------------------------------------------------------------------
00616 // keyUp
00617 //
00618 // Key up glut callback function.  Called when user release a key.
00619 // -------------------------------------------------------------------------
00620 
00621 static void
00622 keyUp (unsigned char key, int x, int y)
00623 {
00624   keyboard.keymap[key] = 0;
00625 }
00626 
00627 
00628 // -------------------------------------------------------------------------
00629 // specialKeyPress
00630 //
00631 // Key press glut callback function.  Called when user press a special key.
00632 // -------------------------------------------------------------------------
00633 
00634 static void
00635 specialKeyPress (int key, int x, int y)
00636 {
00637   keyboard.special[key] = 1;
00638 }
00639 
00640 
00641 // -------------------------------------------------------------------------
00642 // specialKeyUp
00643 //
00644 // Key up glut callback function.  Called when user release a special key.
00645 // -------------------------------------------------------------------------
00646 
00647 static void
00648 specialKeyUp (int key, int x, int y)
00649 {
00650   keyboard.special[key] = 0;
00651 }
00652 
00653 
00654 // -------------------------------------------------------------------------
00655 // mouseMotion
00656 //
00657 // Mouse motion glut callback function.  Called when the user move the
00658 // mouse. Update the camera.
00659 // -------------------------------------------------------------------------
00660 
00661 static void
00662 mouseMotion (int x, int y)
00663 {
00664   if (mouse.buttons[GLUT_MIDDLE_BUTTON] == GLUT_DOWN)
00665     {
00666       // Zoom
00667       eye.z += (x - mouse.x) * 0.1;
00668     }
00669   else if (mouse.buttons[GLUT_LEFT_BUTTON] == GLUT_DOWN)
00670     {
00671       if (keyboard.modifiers & GLUT_ACTIVE_SHIFT)
00672         {
00673           // Translation
00674           eye.x -= (x - mouse.x) * 0.02;
00675           eye.y += (y - mouse.y) * 0.02;
00676         }
00677       else
00678         {
00679           // Rotation
00680           rot.x += (y - mouse.y);
00681           rot.y += (x - mouse.x);
00682         }
00683     }
00684 
00685   mouse.x = x;
00686   mouse.y = y;
00687 
00688   glutPostRedisplay ();
00689 }
00690 
00691 
00692 // -------------------------------------------------------------------------
00693 // mouseButton
00694 //
00695 // Mouse button press glut callback function.  Called when the user
00696 // press a mouse button. Update mouse state and keyboard modifiers.
00697 // -------------------------------------------------------------------------
00698 
00699 static void
00700 mouseButton (int button, int state, int x, int y)
00701 {
00702   // Update key modifiers
00703   keyboard.modifiers = glutGetModifiers ();
00704 
00705   // Update mouse state
00706   mouse.buttons[button] = state;
00707   mouse.x = x;
00708   mouse.y = y;
00709 }
00710 
00711 
00712 // -------------------------------------------------------------------------
00713 // idleVisible
00714 //
00715 // Idle glut callback function.  Continuously called. Perform background
00716 // processing tasks.
00717 // -------------------------------------------------------------------------
00718 
00719 static void
00720 idleVisible ()
00721 {
00722   // Update the timer
00723   updateTimer (&timer);
00724 
00725   // Handle keyboard input
00726   handleKeyboard (&keyboard);
00727 
00728   if (bAnimated)
00729     glutPostRedisplay ();
00730 }
00731 
00732 
00733 // -------------------------------------------------------------------------
00734 // windowStatus
00735 //
00736 // Window status glut callback function.  Called when the status of
00737 // the window changes.
00738 // -------------------------------------------------------------------------
00739 
00740 static void
00741 windowStatus (int state)
00742 {
00743   // Disable rendering and/or animation when the
00744   // window is not visible
00745   if ((state != GLUT_HIDDEN) &&
00746       (state != GLUT_FULLY_COVERED))
00747     {
00748       glutIdleFunc (idleVisible);
00749     }
00750   else
00751     {
00752       glutIdleFunc (NULL);
00753     }
00754 }
00755 
00756 
00757 // -------------------------------------------------------------------------
00758 // main
00759 //
00760 // Application main entry point.
00761 // -------------------------------------------------------------------------
00762 
00763 int
00764 main (int argc, char *argv[])
00765 {
00766   // Initialize GLUT
00767   glutInit (&argc, argv);
00768 
00769   if (argc < 2)
00770     {
00771       cerr << "usage: " << argv[0] << " <player path> [<weapon path>]" << endl;
00772       return -1;
00773     }
00774 
00775   // create an OpenGL window
00776   glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
00777   glutInitWindowSize (640, 480);
00778   glutCreateWindow ("Quake 3's MD3 Model Viewer");
00779 
00780   // Initialize application
00781   atexit (shutdownApp);
00782   init (argv[1], (argc > 2) ? argv[2] : string ());
00783 
00784   // Setup glut callback functions
00785   glutReshapeFunc (reshape);
00786   glutDisplayFunc (display);
00787   glutKeyboardFunc (keyPress);
00788   glutKeyboardUpFunc (keyUp);
00789   glutSpecialFunc (specialKeyPress);
00790   glutSpecialUpFunc (specialKeyUp);
00791   glutMotionFunc (mouseMotion);
00792   glutMouseFunc (mouseButton);
00793   glutWindowStatusFunc (windowStatus);
00794   glutIdleFunc (idleVisible);
00795 
00796   // Enter the main loop
00797   glutMainLoop ();
00798 
00799   return 0;
00800 }