www.pudn.com > RCApp-src.zip > Engine.cpp
/* RedEye Project (http://members.ozemail.com.au/~ndmcevoy/) Copyright (C) 2003 Nick McEvoy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ----------------------------------------------------------------- Commented for use with Doxygen (http://www.doxygen.org) ----------------------------------------------------------------- */ /*! \file Engine.cpp * \brief Game engine. * * This file contains the game engine. */ // GLUT includes #include// PLIB include #include // ODE includes #include "ode/common.h" #include "ode/contact.h" #include "ode/objects.h" #include "ode/geom.h" // RedEye includes #include "Engine.h" #include "ModelManager.h" #include "PlayerManager.h" #include "EntityManager.h" #include "EnvironmentManager.h" #include "MaterialManager.h" #include "SoundManager.h" #include "FPSCounter.h" #include "Status.h" #include "GUI.h" #include "Collision.h" //! The one and only instance reGameEngine* reGameEngine::mpInstance = NULL; //! Called by dSpaceCollide when two objects in space are potentially colliding static void reODENearCallback(void* pData, dGeomID o1, dGeomID o2) { ((reGameEngine*)pData)->CollisionEventHandler(o1, o2); } reGameEngine::reGameEngine() { reGetSoundManager(); reGetMaterialManager(); reGetModelManager(); reGetPlayerManager(); reGetEntityManager(); reGetEnvironmentManager(); reGetFPSCounter(); reGetGameGUI(); msLevelFile[0] = 0; meGameMode = eGameInit; mbTextureOn = true; mbWireFrameOn = false; //meShadingMode = GL_SMOOTH; mbGravityOn = false; mfGravityValue = 1.0f; mODEWorldId = NULL; mODESpaceId = NULL; mODEJointGroupId = NULL; miLastUpdateTimeMs = 0; } reGameEngine::~reGameEngine() { delete reGetGameGUI(); delete reGetFPSCounter(); delete reGetPlayerManager(); delete reGetEntityManager(); delete reGetEnvironmentManager(); delete reGetModelManager(); delete reGetMaterialManager(); delete reGetSoundManager(); if (mODEJointGroupId) { dJointGroupDestroy(mODEJointGroupId); mODEJointGroupId = NULL; } if (mODESpaceId) { dSpaceDestroy(mODESpaceId); mODESpaceId = NULL; } if (mODEWorldId) { dWorldDestroy(mODEWorldId); mODEWorldId = NULL; } mpInstance = NULL; } reGameEngine* reGameEngine::GetInstance() { if (mpInstance == NULL) { mpInstance = new reGameEngine; } return mpInstance; } void reGameEngine::Init() { // Open config file if (mXMLConfig.LoadFile("data/config.xml")) { // Get data TiXmlNode* pNode = mXMLConfig.FirstChild("Config"); SetFullscreen(reGetXMLChildValue(pNode, "Fullscreen", false)); reGetSoundManager()->SetMusicOn(reGetXMLChildValue(pNode, "MusicOn", false)); reGetSoundManager()->SetSoundFxOn(reGetXMLChildValue(pNode, "SoundFxOn", false)); } else // warning { ulSetError(UL_FATAL, "Init() Could not open %s.", "data/config.xml"); exit(0); } // Show load level dialog box reGetGameGUI()->ShowLoadLevel(); } const static double WORLD_STEP_TIME_SEC = 0.05; static float fTimeToMakeUpSec = 0; void reGameEngine::Update() { int iCurrentTimeMs = glutGet(GLUT_ELAPSED_TIME); int iDeltaTimeMS = iCurrentTimeMs - miLastUpdateTimeMs; miLastUpdateTimeMs = iCurrentTimeMs; // Update sound reGetSoundManager()->Update(); if (meGameMode != eGameRunning) return; // Make sure time step is not too large if (iDeltaTimeMS > 500) iDeltaTimeMS = 500; fTimeToMakeUpSec += iDeltaTimeMS/1000.0f; int iNumSteps = (int) (fTimeToMakeUpSec / WORLD_STEP_TIME_SEC); //printf("fTimeToMakeUpSec %.2f iNumSteps %d\n", fTimeToMakeUpSec, iNumSteps); for (int i = 0; i < iNumSteps; ++i) { // Update player, entities, environment & camera reGetPlayerManager()->Update(WORLD_STEP_TIME_SEC); reGetEntityManager()->Update(WORLD_STEP_TIME_SEC); reGetEnvironmentManager()->Update(WORLD_STEP_TIME_SEC); mCamera.Update(WORLD_STEP_TIME_SEC); // Check collision of ODE body against PLIB world reGetPlayerManager()->SpaceCollide(); reGetEntityManager()->SpaceCollide(); // Check collisions, step and remove contacts in the ODE physics engine dSpaceCollide(mODESpaceId, this, &reODENearCallback); dWorldStep(mODEWorldId, WORLD_STEP_TIME_SEC); dJointGroupEmpty(mODEJointGroupId); } fTimeToMakeUpSec -= WORLD_STEP_TIME_SEC * iNumSteps; /* // // OLD CODE ... todo: remove // // Make sure time step is not too small or large if (iDeltaTimeMS < 10) iDeltaTimeMS = 10; else if (iDeltaTimeMS > 50) iDeltaTimeMS = 50; //else if (iDeltaTimeMS > 200) // iDeltaTimeMS = 200; // Update player, entities, environment & camera float fDeltaSec = iDeltaTimeMS/1000.0f; reGetPlayerManager()->Update(fDeltaSec); reGetEntityManager()->Update(fDeltaSec); reGetEnvironmentManager()->Update(fDeltaSec); mCamera.Update(fDeltaSec); for (int i = 0; i <= 1; i++)//call world step a few times !??? { // Check collision of ODE body against PLIB world reGetPlayerManager()->SpaceCollide(); reGetEntityManager()->SpaceCollide(); // Check collisions, step and remove contacts in the ODE physics engine dSpaceCollide(mODESpaceId, this, &reODENearCallback); dReal StepSize = iDeltaTimeMS/1000.0f; printf("StepSize %.2f\n", StepSize); dWorldStep(mODEWorldId, StepSize); dJointGroupEmpty(mODEJointGroupId); }*/ } void reGameEngine::Display() { // Clear the screen glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Calculate FPS reGetFPSCounter()->Display(); //! Position camera mCamera.Display(); // Draw world //glEnable(GL_FOG); reGetEnvironmentManager()->PreDraw(); ssgCullAndDraw(&mWorld); reGetEnvironmentManager()->PostDraw(); //glDisable(GL_FOG); // Debug text char sDebugText[RE_GUI_STRING_MAX]; sprintf(sDebugText, "fps: %.0f\n" "gravity: %s\n", reGetFPSCounter()->GetFPS(), mbGravityOn ? "on" : "off"); reGetGameGUI()->SetText(reGameGUI::eDebugText, sDebugText, true); // Display GUI reGetGameGUI()->Display(); // Display glutPostRedisplay(); glutSwapBuffers(); } void reGameEngine::Reshape(int w, int h) { // Reshape GUI reGetGameGUI()->Reshape(w, h); // Set viewport glViewport(0, 0, w, h); } void reGameEngine::LoadLevel(const char* sLevelDir) { if (meGameMode == eGameInit || meGameMode == eGameStopped) { if (strcmp(msLevelFile, sLevelDir) != 0) { // Destroy models if (meGameMode != eGameInit) ChangeGameMode(eGameInit); strcpy(msLevelFile, sLevelDir); } ChangeGameMode(eGameLoading); } } void reGameEngine::SetGamePaused(bool bPaused) { if (bPaused && meGameMode == eGameRunning) ChangeGameMode(eGamePaused); else if (!bPaused && meGameMode == eGamePaused) ChangeGameMode(eGameRunning); } bool reGameEngine::ChangeGameMode(eGameMode eMode) { reGetGameGUI()->SetTextOff(); switch (eMode) { case eGameInit: meGameMode = eMode; // Destroy everything reGetPlayerManager()->DestroyLevel(); reGetEntityManager()->DestroyLevel(); reGetEnvironmentManager()->DestroyLevel(); reGetModelManager()->DestroyDatabase(); reGetMaterialManager()->DestroyDatabase(); reGetSoundManager()->DestroyDatabase(); if (mODEJointGroupId) { dJointGroupDestroy(mODEJointGroupId); mODEJointGroupId = NULL; } if (mODESpaceId) { dSpaceDestroy(mODESpaceId); mODESpaceId = NULL; } if (mODEWorldId) { dWorldDestroy(mODEWorldId); mODEWorldId = NULL; } mbGravityOn = false; reGetGameGUI()->ShowLoadLevel(); break; case eGameStopped: meGameMode = eMode; // Stop the music reGetSoundManager()->SetMusicOn(false); // Destroy everything (except models) reGetPlayerManager()->DestroyLevel(); reGetEntityManager()->DestroyLevel(); reGetEnvironmentManager()->DestroyLevel(); if (mODEJointGroupId) { dJointGroupDestroy(mODEJointGroupId); mODEJointGroupId = NULL; } if (mODESpaceId) { dSpaceDestroy(mODESpaceId); mODESpaceId = NULL; } if (mODEWorldId) { dWorldDestroy(mODEWorldId); mODEWorldId = NULL; } mbGravityOn = false; break; case eGameLoading: if (meGameMode == eGameInit || meGameMode == eGameStopped) { if (!mXMLLevel.LoadFile(GetLevelFile())) return false; // Get level setup TiXmlNode* pNode = GetXMLLevelNode(); pNode = pNode->FirstChild("Setup"); const char* psMusicTrack = reGetXMLChildValue(pNode, "MusicTrack", ""); mbGravityOn = reGetXMLChildValue(pNode, "GravityOn", false); mfGravityValue = reGetXMLChildValue(pNode, "GravityValue", 0); // Set mode bool bLoadDB = false; if (meGameMode == eGameInit) bLoadDB = true; meGameMode = eMode; reGetGameGUI()->SetText(reGameGUI::eNotifyText, RE_LOADING_TEXT, true); // Create the ODE physics engine mODEWorldId = dWorldCreate(); mODESpaceId = dSimpleSpaceCreate(); mODEJointGroupId = dJointGroupCreate(1000000); // Create database if (bLoadDB) { reGetSoundManager()->CreateDatabase(); reGetSoundManager()->ChangeTrack(psMusicTrack); /* Frantic attempt to keep sound going during level switch. Set the safety margin up to two seconds and update as often as possible. */ reGetSoundManager()->SetSafetyMargin(2.0f); reGetSoundManager()->Update(); reGetMaterialManager()->CreateDatabase(); reGetModelManager()->CreateDatabase(); } // Create level reGetEnvironmentManager()->CreateLevel(); reGetEntityManager()->CreateLevel(); reGetPlayerManager()->CreateLevel(); // Set back to default reGetSoundManager()->SetSafetyMargin(); reGetSoundManager()->Update(); ChangeGameMode(eGameRunning); // Turn gravity on/off if (mbGravityOn) dWorldSetGravity(mODEWorldId, 0, 0, mfGravityValue); else dWorldSetGravity(mODEWorldId, 0, 0, 0); } break; case eGameRunning: if (meGameMode == eGamePaused || meGameMode == eGameLoading) { meGameMode = eMode; } break; case eGamePaused: if (meGameMode == eGameRunning) { meGameMode = eMode; reGetGameGUI()->SetText(reGameGUI::eNotifyText, RE_PAUSE_TEXT, true); } break; default: break; }; return true; } void reGameEngine::SetFullscreen(bool bFull) { if (bFull) glutFullScreen(); // todo: else unset ??? } void reGameEngine::Keyboard(unsigned char key, int x, int y, bool bKeyDown) { // Notify GUI reGetGameGUI()->Keyboard(key, x, y, bKeyDown); // Is the game paused ? if (bKeyDown && meGameMode == eGamePaused) { ChangeGameMode(eGameRunning); return; } switch (key) { case 'r': // Restart game case 'R': if (bKeyDown) { ChangeGameMode(eGameStopped); ChangeGameMode(eGameLoading); } break; case 'p': // Pause game case 'P': if (bKeyDown) { if (meGameMode == eGameRunning) ChangeGameMode(eGamePaused); else if (meGameMode == eGamePaused) ChangeGameMode(eGameRunning); } break; #if _DEBUG // just here for quick exit :) case 'q': // Quit game case 'Q': exit(0); #endif case 27: // ESC key if (bKeyDown) { if (meGameMode == eGameRunning || meGameMode == eGamePaused) { ChangeGameMode(eGameStopped); reGetGameGUI()->ShowLoadLevel(); } } break; case 'h': // Display help case 'H': if (bKeyDown) reGetGameGUI()->ShowHelp(); break; case 'f': // Set fullscreen (todo: toggle?) case 'F': SetFullscreen(true); break; /*case 's': // Toggle shading model case 'S': { if (bKeyDown) { meShadingMode = (meShadingMode == GL_SMOOTH ? GL_FLAT : GL_SMOOTH); glShadeModel(meShadingMode); } } break;*/ case 'w': // Toggle wireframe mode case 'W': { if (bKeyDown && meGameMode == eGameRunning) { mbWireFrameOn = !mbWireFrameOn; if (mbWireFrameOn) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } break; case 'g': // Toggle gravity case 'G': { if (bKeyDown && meGameMode == eGameRunning) { mbGravityOn = !mbGravityOn; if (mbGravityOn) dWorldSetGravity(mODEWorldId, 0, 0, mfGravityValue); else dWorldSetGravity(mODEWorldId, 0, 0, 0); } } break; default: break; } // Notify player reGetPlayerManager()->Keyboard(key, x, y, bKeyDown); } void reGameEngine::KeyboardSpecial(int key, int x, int y, bool bKeyDown) { // Notify GUI reGetGameGUI()->KeyboardSpecial(key, x, y, bKeyDown); // Is the game paused ? if (bKeyDown && meGameMode == eGamePaused) { ChangeGameMode(eGameRunning); return; } switch (key) { case GLUT_KEY_F1: // First person view if (bKeyDown) mCamera.SetCameraMode(reGameCamera::eFirstPersonView); break; case GLUT_KEY_F2: // External attached view if (bKeyDown) mCamera.SetCameraMode(reGameCamera::eExternalAttachedView); break; case GLUT_KEY_F3: // External detached view if (bKeyDown) mCamera.SetCameraMode(reGameCamera::eExternalDetachedView); break; case GLUT_KEY_F4: // External tracking view if (bKeyDown) mCamera.SetCameraMode(reGameCamera::eExternalTrackingView); break; case GLUT_KEY_F5: break; case GLUT_KEY_F6: break; case GLUT_KEY_F7: break; case GLUT_KEY_F8: break; case GLUT_KEY_F9: break; case GLUT_KEY_F10: break; case GLUT_KEY_F11: break; case GLUT_KEY_F12: break; case GLUT_KEY_LEFT: // Player (or external view) roll left if (glutGetModifiers() & GLUT_ACTIVE_SHIFT) mCamera.SetViewLeft(bKeyDown); else mCamera.SetViewLeft(false); break; case GLUT_KEY_UP: // Player (or external view) pitch up if (glutGetModifiers() & GLUT_ACTIVE_SHIFT) mCamera.SetViewUp(bKeyDown); else mCamera.SetViewUp(false); break; case GLUT_KEY_RIGHT: // Player (or external view) roll right if (glutGetModifiers() & GLUT_ACTIVE_SHIFT) mCamera.SetViewRight(bKeyDown); else mCamera.SetViewRight(false); break; case GLUT_KEY_DOWN: // Player (or external view) pitch down if (glutGetModifiers() & GLUT_ACTIVE_SHIFT) mCamera.SetViewDown(bKeyDown); else mCamera.SetViewDown(false); break; case GLUT_KEY_PAGE_UP: // Player (or external view) zoom in mCamera.SetViewZoomIn(bKeyDown); break; case GLUT_KEY_PAGE_DOWN: // Player (or external view) zoom out mCamera.SetViewZoomOut(bKeyDown); break; case GLUT_KEY_HOME: // Player (or external view) reset // todo break; case GLUT_KEY_END: break; case GLUT_KEY_INSERT: break; default: break; } // Notify player reGetPlayerManager()->KeyboardSpecial(key, x, y, bKeyDown); } void reGameEngine::MouseFunc(int button, int state, int x, int y) { // Notify GUI reGetGameGUI()->MouseFunc(button, state, x, y); } void reGameEngine::MotionFunc(int x, int y) { // Notify GUI reGetGameGUI()->MotionFunc(x, y); } void reGameEngine::PassiveMotionFunc(int x, int y) { // Notify GUI reGetGameGUI()->PassiveMotionFunc(x, y); } void reGameEngine::CollisionEventHandler(dGeomID o1, dGeomID o2) { dBodyID b1 = dGeomGetBody(o1); dBodyID b2 = dGeomGetBody(o2); // Exit without doing anything if the two bodies are connected by a joint if (b1 && b2 && dAreConnected (b1,b2)) return; int i; dContact contact[3]; // up to 3 contacts per box for (i = 0; i < 3; i++) { contact[i].surface.mode = dContactBounce; //dContactMu2; contact[i].surface.mu = dInfinity; contact[i].surface.mu2 = 0; contact[i].surface.bounce = 0.5; contact[i].surface.bounce_vel = 0.1; } if (int numc = dCollide (o1, o2, 3, &contact[0].geom, sizeof(dContact))) { reGameEntity* pEntity1 = (reGameEntity*)dGeomGetData(o1); reGameEntity* pEntity2 = (reGameEntity*)dGeomGetData(o2); /*if (pEntity1->GetEntityType() != eWeapon && pEntity2->GetEntityType() != eWeapon) { // Object-object collision for (i = 0; i < numc; i++) { dJointID c = dJointCreateContact( mODEWorldId, mODEJointGroupId, contact + i); dJointAttach(c, b1, b2); } } else // Weapon-object collision { reGameEntity* pWeaponEntity = NULL; reGameEntity* pGameEntity = NULL; if (pEntity1->GetEntityType() == eWeapon && pEntity2->GetEntityType() != eWeapon) { pWeaponEntity = pEntity1; pGameEntity = pEntity2; } else if (pEntity1->GetEntityType() != eWeapon && pEntity2->GetEntityType() == eWeapon) { pWeaponEntity = pEntity1; pGameEntity = pEntity2; } else // never should happen { return; } // Create an explosion sgCoord Pos = pWeaponEntity->GetPosition(); ssgBranch* pExplosionBranch = new ssgBranch; reExplosion* pExplosion = new reExplosion(pExplosionBranch); pExplosionBranch->addKid(pExplosion); pExplosion->Initialise(reExplosion::MakeState(), 8, 50, 1); pExplosion->SetPosition(Pos.xyz[0], Pos.xyz[1], Pos.xyz[2]); pExplosion->StartAnimation(); mExplosionList.AddTail(pExplosion); mWorld.addKid(pExplosionBranch); // Remove the weapon LISTPOSITION pos = mEntityList.GetHeadPosition(); while (pos) { if (pWeaponEntity == mEntityList.GetAt(pos)) { mWorld.removeKid(pWeaponEntity->GetRoot()); mEntityList.RemoveAt(pos); break; } mEntityList.GetNext(pos); } }*/ } }