/*! -------------------------------------------------------------------- \file game.cpp \brief Encapsulate a game \author Roy Thompson \date 2002-11-20 // ------------------------------------------------------------------*/ #ifndef IGNORE_DEPEND #include #include #include #include #include #include #endif #if HAVE_PIC_LIB #include "pic.h" #endif #include "game.h" #include "camera.h" #include "world.h" #include "texturemap.h" #include "input.h" #include "sound.h" //! default window title const char* Game::WINDOW_TITLE = "Galaxy Wars"; //! instance pointer for callbacks Game* Game::s_pGame = NULL; //! snapshot default filename const char* SNAPSHOT_FILENAME = "snapshot.jpg"; //! helper function to read strings from a stream inline bool READ_STRING(istream &is, char* pString) { is >> pString; // ignore commented lines /* if (pString[0] == '-' && pString[1] == '-') while (pString[0] != '\n') { cout << pString[0] << endl; is >> pString; } */ return is.good(); } //--------------------------------------------------------------------- // Game // // Method: Constructor // //--------------------------------------------------------------------- Game::Game() { // initialize instance pointer for callbacks s_pGame = this; _mainWindow = -1; _iWindowWidth = 0; _iWindowHeight = 0; _iHalfHeight = 0; _timeElapsed = 0; memset(_bKeyPressed, 0, sizeof(_bKeyPressed)); memset(_bSpecPressed, 0, sizeof(_bSpecPressed)); _lastTime = 0.0; // _gameStarted = false; } //--------------------------------------------------------------------- // Game // // Method: Destructor // //--------------------------------------------------------------------- Game::~Game() { if (_mainWindow > 0) glutDestroyWindow(_mainWindow); } //--------------------------------------------------------------------- // Game // Method: Init // //! set up game and initialize data structures // //--------------------------------------------------------------------- bool Game::Init(const char* levelFilename, bool soundOn) { // parse level description file if (!_ReadLevel(levelFilename)) { cerr << "Game::Init() - ERROR loading level description." << endl; return false; } // initalize game window bool bError = false; if (!_InitWindow()) { cerr << "Game: ERROR initializing window" << endl; bError = true; } // initalize world data if (!theWorld.Init(_skyboxPath)) { cerr << "Game: ERROR initializing world" << endl; bError = true; } if (!theSound.Init(soundOn, _musicFile)) { cerr << "Game: ERROR initializing sound" << endl; bError = true; } // initalize popup menu to appear on mouse "right-click" // _InitPopupMenu(); theWorld.NewLevel(0); return (!bError); } //--------------------------------------------------------------------- // Game // Method: Run // //! game main processing loop // //--------------------------------------------------------------------- void Game::Run() { // enter main loop glutMainLoop(); } //--------------------------------------------------------------------- // Game // Method: _ReadLevel // //! read level description file // //--------------------------------------------------------------------- bool Game::_ReadLevel(const char* levelFilename) { cerr << "Reading level description file '" << levelFilename << "'..." << endl; // open file ifstream is(levelFilename); if (!is.good()) { cerr << "Game::ReadLevel() - ERROR opening file." << endl; return false; } // read skybox info cerr << " skybox path: "; if (!READ_STRING(is, _skyboxPath)) { cerr << "ERROR" << endl; return false; } cerr << _skyboxPath << endl; // read skybox info cerr << "Music MP3 File: "; if (!READ_STRING(is, _musicFile)) { cerr << "ERROR" << endl; return false; } cerr << _musicFile << endl; theWorld._ReadLevel(is); return true; } //--------------------------------------------------------------------- // Game // Method: _InitWindow // //! initialize main window // //--------------------------------------------------------------------- bool Game::_InitWindow() { // set main window glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); glutInitWindowSize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); _mainWindow = glutCreateWindow(WINDOW_TITLE); // glClearColor(0, 0, 1, 0); glEnable(GL_DEPTH_TEST); // set up the primary callbacks glutDisplayFunc(_DisplayCB); glutIdleFunc(_IdleCB); glutReshapeFunc(_ReshapeCB); #if 1 // keyboard input callbacks glutKeyboardFunc(_KeyDownCB); glutKeyboardUpFunc(_KeyUpCB); glutSpecialFunc(_SpecialDownCB); glutSpecialUpFunc(_SpecialUpCB); glutIgnoreKeyRepeat(GL_TRUE); // input callbacks glutMotionFunc(theInput.MouseMotionCallback); // mouse passive motion callback glutPassiveMotionFunc(theInput.MousePassiveMotionCallback); glutMouseFunc(theInput.MouseButtonCallback); #else glutKeyboardFunc(Input::KeyDownCallback); glutKeyboardUpFunc(Input::KeyUpCallback); glutSpecialFunc(Input::KeySpecialDownCallback); glutSpecialUpFunc(Input::KeySpecialUpCallback); // glutIgnoreKeyRepeat(GL_TRUE); #endif return true; } //--------------------------------------------------------------------- // Game // // Method: _InitPopupMenu // //! initialize the popup menu // //--------------------------------------------------------------------- bool Game::_InitPopupMenu() { glutCreateMenu(_MenuCB); // about info glutAddMenuEntry(" # lab2 : Two-Player Race Game", 0); glutAddMenuEntry(" # 15-493 : October 2002", 0); glutAddMenuEntry(" # Author: James Kuffner", 0); glutAddMenuEntry("", 0); // menu items glutAddMenuEntry("Capture Screen Snapshot (c)", 'c'); // ADD MORE MENU ITEMS HERE // (associate keys with menu items and process in keyboard callback) glutAddMenuEntry("", 0); glutAddMenuEntry("Quit (q)", 'q'); // activate the popup menu on a right-click of the mouse glutAttachMenu(GLUT_RIGHT_BUTTON); return true; } //--------------------------------------------------------------------- // Game // Method: _UpdateState // //! update game state for single game loop iteration // //--------------------------------------------------------------------- void Game::_UpdateState() { // time elapsed since last call int msec = glutGet(GLUT_ELAPSED_TIME); _timeElapsed = (msec * 0.001) - _lastTime; _lastTime = msec * 0.001; // update the world state theWorld.UpdateState(_timeElapsed); // update the input state theInput.UpdateState(); // redraw the views glutPostRedisplay(); } //--------------------------------------------------------------------- // Game // Method: _ReshapeWindow // //! reshape the main window // //--------------------------------------------------------------------- void Game::_ReshapeWindow(int w, int h) { if (w <= MIN_WINDOW_SIZE || h <= MIN_WINDOW_SIZE) { cerr << "Game::Reshape - bad window size: " << w << ", " << h << endl; return; } // reset window size _iWindowWidth = w; _iWindowHeight = h; // aspect ratio for each horizontal split-screen window float aspect = ((GLfloat)w / (GLfloat)_iWindowHeight); glViewport(0,0,w,h); theCamera.SetAspectRatio(aspect); theCamera.SetGLViewTransforms(); } // save images to file void CaptureImage(int num) { char filename[512]; // CHANGE STUDENT BELOW TO YOUR USER NAME sprintf(filename, "/usr/tmp/rst%03d.jpg", num); Pic *in = NULL; if (filename == NULL) { cerr << "Camera::imageCapture : Tried to save into NULL filename\n"; } int m_windowWidth = Game::DEFAULT_WINDOW_WIDTH; int m_windowHeight = Game::DEFAULT_WINDOW_HEIGHT; // Allocate a picture buffer in = pic_alloc(m_windowWidth, m_windowHeight, 3, NULL); for (int i=m_windowHeight-1; i>=0; i--) { glReadPixels(0, m_windowHeight-1-i, m_windowWidth, 1, GL_RGB, GL_UNSIGNED_BYTE, &in->pix[i*in->nx*in->bpp]); } if (!jpeg_write((char *)filename, in)) { cerr << "Camera::imageCapture : Error in saving " << filename << endl; } pic_free(in); } static void timerCallback(int value) { CaptureImage(value); if (value < 300) glutTimerFunc(42, timerCallback, value+1); } //--------------------------------------------------------------------- // Game // Method: _ProcessKeyDown // //! process keyboard down events // //--------------------------------------------------------------------- void Game::_ProcessKeyDown(unsigned char key, int w, int h) { switch (key) { case 27: case 'q': exit(0); break; case 'c': cerr << "Saving screen snapshot to: " << SNAPSHOT_FILENAME << endl; _CaptureImage(SNAPSHOT_FILENAME); break; case 'm': // start saving a movie glutTimerFunc(42, timerCallback, 0); break; default: // store which key is pressed _bKeyPressed[(int)key] = true; } } void Game::_ProcessSpecialDown(int key, int x, int y) { // cout << "Special pressed" << endl; switch (key) { case GLUT_KEY_UP: _bSpecPressed[KEY_UP] = true; break; case GLUT_KEY_DOWN: _bSpecPressed[KEY_DOWN] = true; break; case GLUT_KEY_LEFT: _bSpecPressed[KEY_LEFT] = true; break; case GLUT_KEY_RIGHT: _bSpecPressed[KEY_RIGHT] = true; break; } } void Game::_ProcessSpecialUp(int key, int x, int y) { switch (key) { case GLUT_KEY_UP: _bSpecPressed[KEY_UP] = false; break; case GLUT_KEY_DOWN: _bSpecPressed[KEY_DOWN] = false; break; case GLUT_KEY_LEFT: _bSpecPressed[KEY_LEFT] = false; break; case GLUT_KEY_RIGHT: _bSpecPressed[KEY_RIGHT] = false; break; } } //--------------------------------------------------------------------- // Game // Method: _ProcessKeyUp // //! process keyboard up events // //--------------------------------------------------------------------- void Game::_ProcessKeyUp(unsigned char key, int w, int h) { // cerr << "Key UP: " << key << endl; _bKeyPressed[(int)key] = false; } //--------------------------------------------------------------------- // Game // Method: _CaptureImage // // !takes a snapshot of the current window and saves it into // filename in jpg format. returns true if saved successfully, // false otherwise. // // NOTE: when saving image files, try to use a local path, // such as /usr/tmp instead of your andrew space, for better speed. //--------------------------------------------------------------------- bool Game::_CaptureImage(const char *filename) { #if HAVE_PIC_LIB if (filename == NULL) { cerr << "Game::_CaptureImage : Tried to save into NULL filename\n"; return false; } // allocate a buffer Pic *in = pic_alloc(_iWindowWidth, _iWindowHeight, 3, NULL); // read the framebuffer data for (int i = _iWindowHeight; i >= 0; i--) { glReadPixels(0, _iWindowHeight-1-i, _iWindowWidth, 1, GL_RGB, GL_UNSIGNED_BYTE, &in->pix[i*in->nx*in->bpp]); } // write the result bool bResult = jpeg_write((char *)filename, in); pic_free(in); if (!bResult) cerr << "Game::_CaptureImage : Error writing image " << filename << endl; return bResult; #else cerr << "Game::_CaptureImage() needs 'libpicio'" << endl; return false; #endif // HAVE_PIC_LIB } //--------------------------------------------------------------------- // Game // Method: _Render // //! render world contents to window // //--------------------------------------------------------------------- void Game::_Render() { // NOTE: if you use a skybox, there is no need to clear the color buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); theCamera.SetGLViewTransforms(); theWorld.Render(); glutSwapBuffers(); }