Skip to main content

History: Advanced Ogre Framework

Preview of version: 3

Introduction

Once upon a time, a young programmer eager to explore the world of 3D graphics, started wandering around looking for a powerful magician to help him. For a long time he crossed the country from one side to another and back again, until he finally gave up and sat down at side of a old, rugged road. He was sad, really sad...and disappointed that he hasn't been able to reach his wonderful dream.

While he was sitting there in the grass, quarreling with himself, a rather ugly, green Ogre came along the street. He was in a very good temper, whistling a song with a big grin on his face, reaching from one chest to the other. That's when he saw the poor young programmer boy. Sincerely appalled, he asked what in the world made him so sad and the boy told his whole story. But instead of becoming also sad and grieve when hearing this sorrowful story, the Ogre grinned even more and said:
"Boy, this must be your lucky day. You just met the perfect guy! I'm not as nice as the magician you dreamed of helping you, but I am way more powerful. I can render you anything and teach you all the secrets of 3D graphics! Boy, come on! Is no good waiting here when you have a whole new world in front of you!"

That said, the little programmer boy jumped up and flung his arms around the Ogre's neck and almost kissed him, but suddenly realized that...well...kissing an Ogre is not how you should behave...

By the time, the Ogre thought the young boy everything he knew, starting with the first basic tutorials and later even leaving the devil ExampleApplication.h beyond him. But at some point, this wasn't enough anymore. He wanted more:

  • A game state system
  • a graphical user interface
  • different input modes
  • scene loading and
  • manual material manipulation.


Hopefully he stared up to the Ogres face almost waiting for it telling him, that this is more than he can tell him, but the Ogre only grinned and said:
"I know the perfect secret book for you. I will totally fulfill all your wishes...It's called: The Advanced Ogre Framework!"

And not only has he qualified himself for this kind of advanced knowledge, but also you are ready to be introduced in these magic circles. Just read on...

Forum icon question2.gif If you should encounter any problems while reading, please post in the forum thread for this article.

An example that runs out-of-the-box (without any compiling) can be downloaded here from my webspace. Of course it comes with the source, the needed media as well as with a Microsoft VisualStudio solution file.


If this file shouldn't be available anymore (due to a possible restructuring of my website), just send me a PM in the forum.

Advanced Ogre Framework - Menu state
Advanced Ogre Framework - Menu state

Advanced Ogre Framework - Game state
Advanced Ogre Framework - Game state

Architecture

Well, the architecture may seem awfully complex, but it isn't that hard to get through it. I will try to explain most of it and with a little bit of patience, everyone should make their way through it.

The whole project consists of 14 header and source files, but: Don't be scared by this huge number! One of these files is the main.cpp, which is as always neither big nor complex and two others won't even be posted here as they are just the two files from the DotSceneLoader. That makes 11 more to cope with.

Another pair is the basic Ogre initialization part, similar to the BasicOgreFramework.hpp / .cpp, with almost nothing new in it and of course another pair called DemoApp.hpp / .cpp being the central organization point with just a few lines of code.

The biggest and most complex part is the remaining 7 files that form together the application state system.

Application state system

For the application state system, I basically used a variation of the approach shown here, however with some minor changes. Generally, the whole system works like this:

For each application state we want to use in our program, we create an own class inheriting AppState.hpp. So each application state has the same basic functions like enter, exit, pause, resume and update as well its own Camera and SceneManager.

The second important part of this system is the AppStateManager.hpp. Its task is to manage all the game states and connect them. To do so, the manager has a stack of active states and always executes the one on top of this stack. At any time, you can then put another state on the stack that is then used until it is popped from the stack. In this case, the manager resumes executing the state that was below the one just removed. As soon as there aren't any active states left, the manager closes the whole application.

main.cpp

Let's start with the simplest: the main.cpp. Nothing surprising here.


All that is done is to create an instance of our DemoApp class and call its startDemo() function. That's it.

Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #include "DemoApp.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" //||||||||||||||||||||||||||||||||||||||||||||||| INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT) #else int main(int argc, char **argv) #endif { try { DemoApp demo; demo.startDemo(); } catch(std::exception& e) { #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32 MessageBoxA(NULL, e.what(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL); #else fprintf(stderr, "An exception has occurred: %s\n", e.what()); #endif } return 0; } //|||||||||||||||||||||||||||||||||||||||||||||||

DotSceneLoader

As mentioned above, I won't show this code here, as it is already on the DotSceneLoader page. I just copied these two files from there:
  • DotSceneLoader.hpp
  • DotSceneLoader.cpp

AdvancedOgreFramework.hpp

This OgreFramework class is the equivalent to the equally named one of the Basic Ogre Framework. It has the needed functions to power up Ogre and offers the standard behavior such as making screenshots and toggling and filling the Ogre Debug Overlays. It contains most of the Ogre related variables:
  • Root
  • RenderWindow
  • Viewport
  • Log
  • Timer
  • InputManager / Keyboard / Mouse
  • GUI Renderer
  • GUI System


It also offers functions to handle input, but that is almost not used here, as each application state needs a different input behavior and therefore this is directly modeled in the application states. However, everything common for the whole application can be put here.

Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #ifndef OGRE_FRAMEWORK_HPP #define OGRE_FRAMEWORK_HPP //||||||||||||||||||||||||||||||||||||||||||||||| #include <OgreCamera.h> #include <OgreEntity.h> #include <OgreLogManager.h> #include <OgreOverlay.h> #include <OgreOverlayElement.h> #include <OgreOverlayManager.h> #include <OgreRoot.h> #include <OgreViewport.h> #include <OgreSceneManager.h> #include <OgreRenderWindow.h> #include <OgreConfigFile.h> #include <OISEvents.h> #include <OISInputManager.h> #include <OISKeyboard.h> #include <OISMouse.h> #include <CEGUI/CEGUI.h> #include <OgreCEGUIRenderer.h> //||||||||||||||||||||||||||||||||||||||||||||||| class OgreFramework : public Ogre::Singleton<OgreFramework>, OIS::KeyListener, OIS::MouseListener { public: OgreFramework(); ~OgreFramework(); void initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener = 0, OIS::MouseListener *pMouseListener = 0); void updateOgre(double timeSinceLastFrame); void updateStats(); bool keyPressed(const OIS::KeyEvent &keyEventRef); bool keyReleased(const OIS::KeyEvent &keyEventRef); bool mouseMoved(const OIS::MouseEvent &evt); bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id); bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id); Ogre::Root* m_pRoot; Ogre::RenderWindow* m_pRenderWnd; Ogre::Viewport* m_pViewport; Ogre::Log* m_pLog; Ogre::Timer* m_pTimer; OIS::InputManager* m_pInputMgr; OIS::Keyboard* m_pKeyboard; OIS::Mouse* m_pMouse; CEGUI::OgreCEGUIRenderer* m_pGUIRenderer; CEGUI::System* m_pGUISystem; private: OgreFramework(const OgreFramework&); OgreFramework& operator= (const OgreFramework&); Ogre::Overlay* m_pDebugOverlay; Ogre::Overlay* m_pInfoOverlay; int m_iNumScreenShots; }; //||||||||||||||||||||||||||||||||||||||||||||||| #endif //|||||||||||||||||||||||||||||||||||||||||||||||

AdvancedOgreFramework.cpp

* First line: Singleton is initialized
  • OgreFramework(): Constructor
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #include "AdvancedOgreFramework.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| using namespace Ogre; //||||||||||||||||||||||||||||||||||||||||||||||| template<> OgreFramework* Ogre::Singleton<OgreFramework>::ms_Singleton = 0; //||||||||||||||||||||||||||||||||||||||||||||||| OgreFramework::OgreFramework() { m_iNumScreenShots = 0; m_pRoot = 0; m_pRenderWnd = 0; m_pViewport = 0; m_pLog = 0; m_pTimer = 0; m_pInputMgr = 0; m_pKeyboard = 0; m_pMouse = 0; m_pDebugOverlay = 0; m_pInfoOverlay = 0; }

* ~OgreFramework(): Destructor, clearing up
Copy to clipboard
OgreFramework::~OgreFramework() { if(m_pInputMgr) { delete m_pKeyboard; delete m_pMouse; OIS::InputManager::destroyInputSystem(m_pInputMgr); } delete m_pRoot; }

* initOgre(): Powers up Ogre with the following steps:
  1. create the log manager
  2. create the Root
  3. create the RenderWindow and the Viewport
  4. power up OIS
  5. if there was no MouseListener or KeyboardListener passed as a parameter, use the ones from this class, otherwise the passed ones (however you still can use both, by calling the OgreFramework class input functions when you handle input elsewhere)
  6. load resources
  7. start Timer
  8. set up the GUI Renderer and the GUI System and load all needed layouts
  9. create and show the debug overlay


Note: After running this function you still won't see anything on the screen as there is no Camera and no SceneManager. Those are members of the individual application states!

Copy to clipboard
void OgreFramework::initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener, OIS::MouseListener *pMouseListener) { Ogre::LogManager* logMgr = new Ogre::LogManager(); m_pLog = Ogre::LogManager::getSingleton().createLog("OgreLogfile.log", true, true, false); m_pLog->setDebugOutputEnabled(true); m_pRoot = new Ogre::Root(); m_pRoot->showConfigDialog(); m_pRenderWnd = m_pRoot->initialise(true, wndTitle); m_pViewport = m_pRenderWnd->addViewport(0); m_pViewport->setBackgroundColour(ColourValue(0.8, 0.8, 0.8, 1.0)); m_pViewport->setCamera(0); unsigned long hWnd = 0; OIS::ParamList paramList; m_pRenderWnd->getCustomAttribute("WINDOW", &hWnd); paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toString(hWnd))); m_pInputMgr = OIS::InputManager::createInputSystem(paramList); m_pKeyboard = static_cast<OIS::Keyboard*>(m_pInputMgr->createInputObject(OIS::OISKeyboard, true)); m_pMouse = static_cast<OIS::Mouse*>(m_pInputMgr->createInputObject(OIS::OISMouse, true)); m_pMouse->getMouseState().height = m_pRenderWnd->getHeight(); m_pMouse->getMouseState().width = m_pRenderWnd->getWidth(); if(pKeyListener == 0) m_pKeyboard->setEventCallback(this); else m_pKeyboard->setEventCallback(pKeyListener); if(pMouseListener == 0) m_pMouse->setEventCallback(this); else m_pMouse->setEventCallback(pMouseListener); Ogre::String secName, typeName, archName; Ogre::ConfigFile cf; cf.load("resources.cfg"); Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); while (seci.hasMoreElements()) { secName = seci.peekNextKey(); Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); Ogre::ConfigFile::SettingsMultiMap::iterator i; for (i = settings->begin(); i != settings->end(); ++i) { typeName = i->first; archName = i->second; Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName); } } Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5); Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); m_pTimer = new Ogre::Timer(); m_pTimer->reset(); m_pGUIRenderer = new CEGUI::OgreCEGUIRenderer(m_pRenderWnd, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, 0); m_pGUISystem = new CEGUI::System(m_pGUIRenderer); CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme"); CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"AdvancedOgreFramework.layout"); CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"AdvancedOgreFramework_Game.layout"); m_pDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay"); m_pDebugOverlay->show(); m_pRenderWnd->setActive(true); }

* keyPressed(): Handles to buffered input, common for the whole application
  • keyReleased(): same as above
  • mouseMoved(): same as above
  • mousePressed(): same as above
  • mouseReleased(): same as above
Copy to clipboard
bool OgreFramework::keyPressed(const OIS::KeyEvent &keyEventRef) { if(m_pKeyboard->isKeyDown(OIS::KC_SYSRQ)) { std::ostringstream ss; ss << "screenshot_" << ++m_iNumScreenShots << ".png"; m_pRenderWnd->writeContentsToFile(ss.str()); return true; } if(m_pKeyboard->isKeyDown(OIS::KC_O)) { if(m_pDebugOverlay) { if(!m_pDebugOverlay->isVisible()) m_pDebugOverlay->show(); else m_pDebugOverlay->hide(); } } return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool OgreFramework::keyReleased(const OIS::KeyEvent &keyEventRef) { return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool OgreFramework::mouseMoved(const OIS::MouseEvent &evt) { return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool OgreFramework::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id) { return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool OgreFramework::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id) { return true; }

* updateOgre(): Is called once per frame by the AppStateManager to update everything directly related to Ogre, in our case only the statistics
  • updateStats(): Updates the statistics (FPS, triangle count, batch count, ...)
Copy to clipboard
void OgreFramework::updateOgre(double timeSinceLastFrame) { updateStats(); } //||||||||||||||||||||||||||||||||||||||||||||||| void OgreFramework::updateStats() { static String currFps = "Current FPS: "; static String avgFps = "Average FPS: "; static String bestFps = "Best FPS: "; static String worstFps = "Worst FPS: "; static String tris = "Triangle Count: "; static String batches = "Batch Count: "; OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps"); OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps"); OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps"); OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps"); const RenderTarget::FrameStats& stats = m_pRenderWnd->getStatistics(); guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS)); guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS)); guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS) +" "+StringConverter::toString(stats.bestFrameTime)+" ms"); guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS) +" "+StringConverter::toString(stats.worstFrameTime)+" ms"); OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris"); guiTris->setCaption(tris + StringConverter::toString(stats.triangleCount)); OverlayElement* guiBatches = OverlayManager::getSingleton().getOverlayElement("Core/NumBatches"); guiBatches->setCaption(batches + StringConverter::toString(stats.batchCount)); OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText"); guiDbg->setCaption(""); }

AppState.hpp

In this file, two classes are defined:
  • AppStateListener and
  • AppState


The first class will later be inherited by the application state manager, but has to be defined here due to design reasons.

  • AppstateListener(): Constructor
  • ~AppStateListner(): Destructor
  • manageGameState(): Function to later add a new state to the manager
  • changeAppState(): Exits the current app state and starts the one specified as the parameter
  • pushAppState(): Puts a new app state on the active state stack that will then be excecuted
  • popGameState(): Removes the top active state from the stack, which results in returning to the one below
  • shutdown(): Well, guess what happens here...


The second class is the app state blueprint from which each actual application state will inherit:

  • some functions to enter, exit, pause, resume and update the state
  • some functions to call other states (orders the manager to start them)
  • a pointer to the manager (which is also a AppStateListener)
  • own Camera and SceneManager


The last part of this file is a big #define statement. It defines the makro DECLARE_APPSTATE_CLASS via that you can later create the game states.

Note: The backslashes there are very important as they tell the compiler to take those lines as one big one. Otherwise you would get compiler errors...

Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #ifndef APP_STATE_HPP #define APP_STATE_HPP //||||||||||||||||||||||||||||||||||||||||||||||| #include "AdvancedOgreFramework.hpp" class AppState; //||||||||||||||||||||||||||||||||||||||||||||||| class AppStateListener { public: AppStateListener(void){}; virtual void manageAppState(Ogre::String stateName, AppState* state) = 0; virtual AppState* findByName(Ogre::String stateName) = 0; virtual void changeAppState(AppState *state) = 0; virtual bool pushAppState(AppState* state) = 0; virtual void popAppState() = 0; virtual void shutdown() = 0; }; //||||||||||||||||||||||||||||||||||||||||||||||| class AppState : public OIS::KeyListener, public OIS::MouseListener { public: static void create(AppStateListener* parent, const Ogre::String name){}; void destroy(){delete this;} virtual void enter(void) = 0; virtual void exit(void) = 0; virtual bool pause(void){return false;} virtual void resume(void){}; virtual void update(double timeSinceLastFrame) = 0; protected: AppState(void){}; virtual ~AppState(void){}; AppState* findByName(Ogre::String stateName){return m_pParent->findByName(stateName);} void changeAppState(AppState* state){m_pParent->changeAppState(state);} bool pushAppState(AppState* state){return m_pParent->pushAppState(state);} void popAppState(void){m_pParent->popAppState();} void shutdown(void){m_pParent->shutdown();} AppStateListener* m_pParent; Ogre::Camera* m_pCamera; Ogre::SceneManager* m_pSceneMgr; }; //||||||||||||||||||||||||||||||||||||||||||||||| #define DECLARE_APPSTATE_CLASS(T) static void create(AppStateListener* parent, const Ogre::String name) { T* myAppState = new T(); myAppState->m_pParent = parent; parent->manageAppState(name, myAppState); } //||||||||||||||||||||||||||||||||||||||||||||||| #endif //|||||||||||||||||||||||||||||||||||||||||||||||

AppStateManager.hpp

The class AppStateManager inherits from the class AppStateListener shown above and mainly implements its abstract methods. Furthermore it contains:
  • a std::vector for all existing states (m_States)
  • a std::vector for the active states, so the stack of those states currently is use (m_ActiveStateStack)
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #ifndef APP_STATE_MANAGER_HPP #define APP_STATE_MANAGER_HPP //||||||||||||||||||||||||||||||||||||||||||||||| #include "AppState.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| class AppStateManager : public AppStateListener { public: typedef struct { Ogre::String name; AppState* state; } state_info; AppStateManager(); ~AppStateManager(); void manageAppState(Ogre::String stateName, AppState* state); AppState* findByName(Ogre::String stateName); void start(AppState* state); void changeAppState(AppState* state); bool pushAppState(AppState* state); void popAppState(void); void shutdown(void); protected: void init(AppState *state); std::vector<AppState*> m_ActiveStateStack; std::vector<state_info> m_States; bool m_bShutdown; }; //||||||||||||||||||||||||||||||||||||||||||||||| #endif //|||||||||||||||||||||||||||||||||||||||||||||||

AppStateManager.cpp

* AppStateManager(): Constructor, just setting the shutdown indicator
  • ~AppStateManager(): Destructor, exiting all active application states and emptying the std::vectors
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #include "AppStateManager.hpp" #include <OgreWindowEventUtilities.h> //||||||||||||||||||||||||||||||||||||||||||||||| AppStateManager::AppStateManager() { m_bShutdown = false; } //||||||||||||||||||||||||||||||||||||||||||||||| AppStateManager::~AppStateManager() { while(!m_ActiveStateStack.empty()) { m_ActiveStateStack.back()->exit(); m_ActiveStateStack.pop_back(); } while(!m_States.empty()) { m_States.pop_back(); } }

* manageAppState(): Called from within the state creation macro and sets some information of the new state, as well as pushing it on the active states stack
Copy to clipboard
void AppStateManager::manageAppState(Ogre::String stateName, AppState* state) { try { state_info new_state_info; new_state_info.name = stateName; new_state_info.state = state; m_States.push_back(new_state_info); } catch(std::exception& e) { delete state; throw Ogre::Exception(Ogre::Exception::ERR_INTERNAL_ERROR, "Error while trying to manage a new AppState\n" + Ogre::String(e.what()), "AppStateManager.cpp (39)"); } }

* findByName(): Returns a pointer to the state with the respective name
Copy to clipboard
AppState* AppStateManager::findByName(Ogre::String stateName) { std::vector<state_info>::iterator itr; for(itr=m_States.begin();itr!=m_States.end();itr++) { if(itr->name==stateName) return itr->state; } return 0; }

* start(): Main loop of the application that does the following steps:
  1. change to the state specified
  2. start loop
  3. capture keyboard and mouse input
  4. update the current state (the top most of the stack)
  5. call the OgreFramework class to update and render
Copy to clipboard
void AppStateManager::start(AppState* state) { changeAppState(state); double timeSinceLastFrame = 1; double startTime = 0; while(!m_bShutdown) { if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true; #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 Ogre::WindowEventUtilities::messagePump(); #endif if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive()) { startTime = OgreFramework::getSingletonPtr()->m_pTimer->getMillisecondsCPU(); OgreFramework::getSingletonPtr()->m_pKeyboard->capture(); OgreFramework::getSingletonPtr()->m_pMouse->capture(); m_ActiveStateStack.back()->update(timeSinceLastFrame); OgreFramework::getSingletonPtr()->updateOgre(timeSinceLastFrame); OgreFramework::getSingletonPtr()->m_pRoot->renderOneFrame(); timeSinceLastFrame = OgreFramework::getSingletonPtr()->m_pTimer->getMillisecondsCPU() - startTime; } else { Sleep(1000); } } OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit"); OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE..."); }

* changeAppState(): Exits the current state (if there is any) and starts the new specified one
Copy to clipboard
void AppStateManager::changeAppState(AppState* state) { if(!m_ActiveStateStack.empty()) { m_ActiveStateStack.back()->exit(); m_ActiveStateStack.pop_back(); } m_ActiveStateStack.push_back(state); init(state); m_ActiveStateStack.back()->enter(); }

* pushAppState(): Puts an new state on the top of the stack and starts it
Copy to clipboard
bool AppStateManager::pushAppState(AppState* state) { if(!m_ActiveStateStack.empty()) { if(!m_ActiveStateStack.back()->pause()) return false; } m_ActiveStateStack.push_back(state); init(state); m_ActiveStateStack.back()->enter(); return true; }

* popAppState(): Removes the top most state and resumes the one below if there is one, otherwise shutdown the application
Copy to clipboard
void AppStateManager::popAppState(void) { if(!m_ActiveStateStack.empty()) { m_ActiveStateStack.back()->exit(); m_ActiveStateStack.pop_back(); } if(!m_ActiveStateStack.empty()) { init(m_ActiveStateStack.back()); m_ActiveStateStack.back()->resume(); } else shutdown(); }

* shutdown(): Exits the application
Copy to clipboard
void AppStateManager::shutdown() { m_bShutdown=true; }

* init(): Initializes a new state and links the input callback on it, as well as resetting the Ogre statistics (FPS, triangle count, batch count, ...)
Copy to clipboard
void AppStateManager::init(AppState* state) { OgreFramework::getSingletonPtr()->m_pKeyboard->setEventCallback(state); OgreFramework::getSingletonPtr()->m_pMouse->setEventCallback(state); OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics(); }

The MenuState class is one of the actual AppState class implementations. By inheriting from AppState it is assured that all states have the same common functions as enter(), exit(), pause(), resume() or update().

  • DECLARE_APPSTATE_CLASS(MenuState): This line calls the macro defined in AppState.hpp, making this class a valid application state
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #ifndef MENU_STATE_HPP #define MENU_STATE_HPP //||||||||||||||||||||||||||||||||||||||||||||||| #include "AppState.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| class MenuState : public AppState { public: DECLARE_APPSTATE_CLASS(MenuState) void enter(); void createScene(); void exit(); bool pause(); void resume(); bool keyPressed(const OIS::KeyEvent &keyEventRef); bool keyReleased(const OIS::KeyEvent &keyEventRef); bool mouseMoved(const OIS::MouseEvent &evt); bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id); bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id); bool onExitButton(const CEGUI::EventArgs &args); bool onEnterButton(const CEGUI::EventArgs &args); void update(double timeSinceLastFrame); private: bool m_bQuit; }; //||||||||||||||||||||||||||||||||||||||||||||||| #endif //|||||||||||||||||||||||||||||||||||||||||||||||

* enter(): Always called when starting this state and does the following steps:
  1. create SceneManager
  2. create a Camera
  3. set input callbacks
  4. tell the GUI which SceneManager to use
  5. set the mouse cursor image
  6. tell the GUI System which Window to render (AOF_GUI)
  7. get the buttons from the GUI and set the respective click handlers
  8. call createScene() to fill the scene with content
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #include "MenuState.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| using namespace Ogre; //||||||||||||||||||||||||||||||||||||||||||||||| void MenuState::enter() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering MenuState..."); m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "MenuSceneMgr"); m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.7, 0.7, 0.7)); m_pCamera = m_pSceneMgr->createCamera("MenuCam"); m_pCamera->setPosition(Vector3(0, 25, -50)); m_pCamera->lookAt(Vector3(0, 0, 0)); m_pCamera->setNearClipDistance(1); m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) / Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight())); OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera); OgreFramework::getSingletonPtr()->m_pKeyboard->setEventCallback(this); OgreFramework::getSingletonPtr()->m_pMouse->setEventCallback(this); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(m_pSceneMgr); OgreFramework::getSingletonPtr()->m_pGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow"); CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow"); const OIS::MouseState state = OgreFramework::getSingletonPtr()->m_pMouse->getMouseState(); CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition(); CEGUI::System::getSingleton().injectMouseMove(state.X.abs-mousePos.d_x,state.Y.abs-mousePos.d_y); CEGUI::Window* pMainWnd = CEGUI::WindowManager::getSingleton().getWindow("AOF_GUI"); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(pMainWnd); CEGUI::PushButton* button = (CEGUI::PushButton*)pMainWnd->getChild("ExitButton"); button->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&MenuState::onExitButton, this)); button = (CEGUI::PushButton*)pMainWnd->getChild("EnterButton"); button->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&MenuState::onEnterButton, this)); m_bQuit = false; createScene(); }

* pause(): Pauses the state and therefore detaches the current GUISheet and the current target scene manager from the GUI, so that these values can be set in the next state that is now active
Copy to clipboard
bool MenuState::pause() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Pausing MenuState..."); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(0); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(0); return true; }

* resume(): Resumes the state after it has been paused, therefore reapply the Camera, the SceneManager and the GUISheet
Copy to clipboard
void MenuState::resume() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Resuming MenuState..."); OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(m_pSceneMgr); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(CEGUI::WindowManager::getSingleton().getWindow("AOF_GUI")); m_bQuit = false; }

* createScene(): Fill the scene with content (no content in the MenuState here)
  • exit(): Similar to the pause() function, but also destroys the Camera and the SceneManager
Copy to clipboard
void MenuState::createScene() { } //||||||||||||||||||||||||||||||||||||||||||||||| void MenuState::exit() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving MenuState..."); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(0); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(0); m_pSceneMgr->destroyCamera(m_pCamera); if(m_pSceneMgr) OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr); }

Click handlers forthe GUI buttons that are were connected to the respective CEGUI::Button in enter()
  • onExitButton(): Called when the user wants to exit
  • onEnterButton(): Called when the user wants to enter the game state
Copy to clipboard
bool MenuState::onExitButton(const CEGUI::EventArgs &args) { m_bQuit = true; return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool MenuState::onEnterButton(const CEGUI::EventArgs &args) { this->pushAppState(findByName("GameState")); return true; }

Input handling functions:
  • keyPressed(): Quits on Escape and forwards the unhandled input to the OgreFramework class
  • keyReleased(): Only forwards to the OgreFramework class
  • mouseMoved(): Injects the mouse movements to the GUI
  • mousePressed(): Injects the mouse clicks to the GUI
  • mouseReleased(): Injects the mouse clicks to the GUI
Copy to clipboard
bool MenuState::keyPressed(const OIS::KeyEvent &keyEventRef) { if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_ESCAPE)) { m_bQuit = true; return true; } OgreFramework::getSingletonPtr()->keyPressed(keyEventRef); return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool MenuState::keyReleased(const OIS::KeyEvent &keyEventRef) { OgreFramework::getSingletonPtr()->keyReleased(keyEventRef); return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool MenuState::mouseMoved(const OIS::MouseEvent &evt) { OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseWheelChange(evt.state.Z.rel); OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseMove(evt.state.X.rel, evt.state.Y.rel); return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool MenuState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id) { if(id == OIS::MB_Left) OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseButtonDown(CEGUI::LeftButton); return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool MenuState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id) { if(id == OIS::MB_Left) OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseButtonUp(CEGUI::LeftButton); return true; }

* update(): Checks whether the state is to be quit and removes itself from the active state stack if requested (which indirectly calls the AppStateManager)
Copy to clipboard
void MenuState::update(double timeSinceLastFrame) { if(m_bQuit == true) { this->popAppState(); return; } }

GameState.hpp

The second actual implemented application state, also inheriting from AppState. It is basically the same as MenuState.hpp with some more variables.
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #ifndef PLAY_STATE_HPP #define PLAY_STATE_HPP //||||||||||||||||||||||||||||||||||||||||||||||| #include "AppState.hpp" #include "DotSceneLoader.hpp" #include <OgreSubEntity.h> #include <OgreMaterialManager.h> //||||||||||||||||||||||||||||||||||||||||||||||| enum QueryFlags { OGRE_HEAD_MASK = 1<<0, CUBE_MASK = 1<<1 }; //||||||||||||||||||||||||||||||||||||||||||||||| class GameState : public AppState { public: GameState(); DECLARE_APPSTATE_CLASS(GameState) void enter(); void createScene(); void exit(); bool pause(){return true;} void resume(){}; void moveCamera(); void getInput(); bool keyPressed(const OIS::KeyEvent &keyEventRef); bool keyReleased(const OIS::KeyEvent &keyEventRef); bool mouseMoved(const OIS::MouseEvent &arg); bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id); bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id); void onLeftPressed(const OIS::MouseEvent &evt); bool onExitButtonGame(const CEGUI::EventArgs &args); void update(double timeSinceLastFrame); void setBufferedMode(); void setUnbufferedMode(); private: Ogre::SceneNode* m_pOgreHeadNode; Ogre::Entity* m_pOgreHeadEntity; Ogre::MaterialPtr m_pOgreHeadMat; Ogre::MaterialPtr m_pOgreHeadMatHigh; bool m_bQuit; Ogre::Vector3 m_TranslateVector; Ogre::Real m_MoveSpeed; Ogre::Degree m_RotateSpeed; float m_MoveScale; Ogre::Degree m_RotScale; Ogre::RaySceneQuery* m_pRSQ; Ogre::SceneNode* m_pCurrentObject; Ogre::Entity* m_pCurrentEntity; bool m_bLMouseDown, m_bRMouseDown; bool m_bChatMode; CEGUI::Window* m_pMainWnd; CEGUI::Window* m_pChatWnd; }; //||||||||||||||||||||||||||||||||||||||||||||||| #endif //|||||||||||||||||||||||||||||||||||||||||||||||

GameState.cpp

* GameState(): Constructor
  • enter(): Almost the same as the enter() function of the MenuState.hpp except that:
  1. a Ray is created with a QueryMask defined in GameState.hpp
  2. setUnbufferedMode() is called, setting some captions
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #include "GameState.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| using namespace Ogre; //||||||||||||||||||||||||||||||||||||||||||||||| GameState::GameState() { m_MoveSpeed = 0.1; m_RotateSpeed = 0.3; m_pCurrentObject = 0; } //||||||||||||||||||||||||||||||||||||||||||||||| void GameState::enter() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering GameState..."); m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "GameSceneMgr"); m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.7, 0.7, 0.7)); m_pRSQ = m_pSceneMgr->createRayQuery(Ray()); m_pRSQ->setQueryMask(OGRE_HEAD_MASK); m_pCamera = m_pSceneMgr->createCamera("GameCamera"); m_pCamera->setPosition(Vector3(5, 60, 60)); m_pCamera->lookAt(Vector3(5, 20, 0)); m_pCamera->setNearClipDistance(5); m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) / Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight())); OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera); OgreFramework::getSingletonPtr()->m_pKeyboard->setEventCallback(this); OgreFramework::getSingletonPtr()->m_pMouse->setEventCallback(this); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(m_pSceneMgr); OgreFramework::getSingletonPtr()->m_pGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow"); CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow"); const OIS::MouseState state = OgreFramework::getSingletonPtr()->m_pMouse->getMouseState(); CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition(); CEGUI::System::getSingleton().injectMouseMove(state.X.abs-mousePos.d_x,state.Y.abs-mousePos.d_y); m_pMainWnd = CEGUI::WindowManager::getSingleton().getWindow("AOF_GUI_GAME"); m_pChatWnd = CEGUI::WindowManager::getSingleton().getWindow("ChatWnd"); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(m_pMainWnd); CEGUI::PushButton* pExitButton = (CEGUI::PushButton*)m_pMainWnd->getChild("ExitButton_Game"); pExitButton->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&GameState::onExitButtonGame, this)); m_bLMouseDown = m_bRMouseDown = false; m_bQuit = false; m_bChatMode = false; setUnbufferedMode(); createScene(); }

* pause(): Pauses the state and therefore detaches the current GUISheet and the current target scene manager from the GUI, so that these values can be set in the next state that is now active
Copy to clipboard
bool GameState::pause() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Pausing GameState..."); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(0); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(0); return true; }

* resume(): Resumes the state after it has been paused, therefore reapply the Camera, the SceneManager and the GUISheet
Copy to clipboard
void GameState::resume() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Resuming GameState..."); OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(m_pSceneMgr); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(CEGUI::WindowManager::getSingleton().getWindow("AOF_GUI_GAME")); m_bQuit = false; }

* exit(): Similar to the pause() function, but also destroys the Camera and the SceneManager
Copy to clipboard
void GameState::exit() { OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving GameState..."); OgreFramework::getSingletonPtr()->m_pGUISystem->setGUISheet(0); OgreFramework::getSingletonPtr()->m_pGUIRenderer->setTargetSceneManager(0); m_pSceneMgr->destroyCamera(m_pCamera); m_pSceneMgr->destroyQuery(m_pRSQ); if(m_pSceneMgr) OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr); }

* createScene(): Fills the scene with content:
  1. set the SkyBox
  2. create a Light
  3. instantiate the DotSceneLoader
  4. load a DotScene (CubeScene.xml) from the ResourceGroup General
  5. retrieve the Entities from the DotScene and set the QueryMaskFlag (so that they can't be selected with the mouse)
  6. put the OgerHead into the scene and also set a QueryMaskFlag, so that it actually can be selected with the mouse
  7. get the current material from the head, clone it and change the cloned one to red
Copy to clipboard
void GameState::createScene() { m_pSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox"); m_pSceneMgr->createLight("Light")->setPosition(75,75,75); Ogre::DotSceneLoader* pDotSceneLoader = new Ogre::DotSceneLoader(); pDotSceneLoader->parseDotScene("CubeScene.xml", "General", m_pSceneMgr, m_pSceneMgr->getRootSceneNode()); m_pSceneMgr->getEntity("Cube01")->setQueryFlags(CUBE_MASK); m_pSceneMgr->getEntity("Cube02")->setQueryFlags(CUBE_MASK); m_pSceneMgr->getEntity("Cube03")->setQueryFlags(CUBE_MASK); m_pOgreHeadEntity = m_pSceneMgr->createEntity("Cube", "ogrehead.mesh"); m_pOgreHeadEntity->setQueryFlags(OGRE_HEAD_MASK); m_pOgreHeadNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("CubeNode"); m_pOgreHeadNode->attachObject(m_pOgreHeadEntity); m_pOgreHeadNode->setPosition(Vector3(0, 0, -25)); m_pOgreHeadMat = m_pOgreHeadEntity->getSubEntity(1)->getMaterial(); m_pOgreHeadMatHigh = m_pOgreHeadMat->clone("OgreHeadMatHigh"); m_pOgreHeadMatHigh->getTechnique(0)->getPass(0)->setAmbient(1, 0, 0); m_pOgreHeadMatHigh->getTechnique(0)->getPass(0)->setDiffuse(1, 0, 0, 0); }

* keyPressed(): Handles the buffered input:
  1. if we are in chat mode (can be toggled via Tab) then all the key hits have to be injected as they should be processed by the GUI
  2. check for Escape, Tab to switch input modes and Return / Enter when we are in chat mode in order to send the typed in text to the message box
  3. if we a) are not in the chat mode or b) we are in chat mode, but O was not pressed, we can pass the unhandled input to the OgreFramework class (without this check, e.g. typing in an O in chat mode would also be passed to the OgreFramework resulting in also toggling the Overlays for each typed O)
  • keyReleased(): Just passes the input on to the OgreFramework class
Copy to clipboard
bool GameState::keyPressed(const OIS::KeyEvent &keyEventRef) { if(m_bChatMode == true) { OgreFramework::getSingletonPtr()->m_pGUISystem->injectKeyDown(keyEventRef.key); OgreFramework::getSingletonPtr()->m_pGUISystem->injectChar(keyEventRef.text); } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_ESCAPE)) { m_bQuit = true; return true; } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_TAB)) { m_bChatMode = !m_bChatMode; if(m_bChatMode) setBufferedMode(); else setUnbufferedMode(); return true; } if(m_bChatMode && OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_RETURN) || OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_NUMPADENTER)) { CEGUI::Editbox* pChatInputBox = (CEGUI::Editbox*)m_pChatWnd->getChild("ChatInputBox"); CEGUI::MultiLineEditbox *pChatContentBox = (CEGUI::MultiLineEditbox*)m_pChatWnd->getChild("ChatContentBox"); pChatContentBox->setText(pChatContentBox->getText() + pChatInputBox->getText() + "\n"); pChatInputBox->setText(""); pChatContentBox->setCaratIndex(pChatContentBox->getText().size()); pChatContentBox->ensureCaratIsVisible(); } if(!m_bChatMode || (m_bChatMode && !OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_O))) OgreFramework::getSingletonPtr()->keyPressed(keyEventRef); return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool GameState::keyReleased(const OIS::KeyEvent &keyEventRef) { OgreFramework::getSingletonPtr()->keyReleased(keyEventRef); return true; }

* mouseMoved(): Handle the mouse movement:
  1. inject the movement to the GUI
  2. if the right mouse button is pressed, manipulate the camera
Copy to clipboard
bool GameState::mouseMoved(const OIS::MouseEvent &evt) { OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseWheelChange(evt.state.Z.rel); OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseMove(evt.state.X.rel, evt.state.Y.rel); if(m_bRMouseDown) { m_pCamera->yaw(Degree(evt.state.X.rel * -0.1)); m_pCamera->pitch(Degree(evt.state.Y.rel * -0.1)); } return true; }

* mousePressed(): Checks which button was pressed and sets the internal bool values as well as injecting the left click to the GUI and calls the function onLeftPressed() for object selection with the mouse
  • mouseReleased(): The same as above, but for the key release
Copy to clipboard
bool GameState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id) { if(id == OIS::MB_Left) { onLeftPressed(evt); m_bLMouseDown = true; OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseButtonDown(CEGUI::LeftButton); } else if(id == OIS::MB_Right) { m_bRMouseDown = true; } return true; } //||||||||||||||||||||||||||||||||||||||||||||||| bool GameState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id) { if(id == OIS::MB_Left) { m_bLMouseDown = false; OgreFramework::getSingletonPtr()->m_pGUISystem->injectMouseButtonUp(CEGUI::LeftButton); } else if(id == OIS::MB_Right) { m_bRMouseDown = false; } return true; }

* onLeftPressed(): Object selection:
  1. if there is a selected object (which would be stored in m_pCurrentObject) hide its bounding box now and set another material for the Ogre head (the Ogre head is the only selectable object in our scene due to the QueryMask)
  2. get the position of our mouse cursor
  3. create a Ray from the camera to this position and check for hits
  4. iterate over all hits
  5. if we hit a movable object, show its bounding box and store it into m_pCurrentObject as well as applying a different material to it
Copy to clipboard
void GameState::onLeftPressed(const OIS::MouseEvent &evt) { if(m_pCurrentObject) { m_pCurrentObject->showBoundingBox(false); m_pCurrentEntity->getSubEntity(1)->setMaterial(m_pOgreHeadMat); } CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition(); Ogre::Ray mouseRay = m_pCamera->getCameraToViewportRay(mousePos.d_x/float(evt.state.width), mousePos.d_y/float(evt.state.height)); m_pRSQ->setRay(mouseRay); m_pRSQ->setSortByDistance(true); Ogre::RaySceneQueryResult &result = m_pRSQ->execute(); Ogre::RaySceneQueryResult::iterator itr; for(itr = result.begin(); itr != result.end(); itr++) { if(itr->movable) { OgreFramework::getSingletonPtr()->m_pLog->logMessage("MovableName: " + itr->movable->getName()); m_pCurrentObject = m_pSceneMgr->getEntity(itr->movable->getName())->getParentSceneNode(); OgreFramework::getSingletonPtr()->m_pLog->logMessage("ObjName " + m_pCurrentObject->getName()); m_pCurrentObject->showBoundingBox(true); m_pCurrentEntity = m_pSceneMgr->getEntity(itr->movable->getName()); m_pCurrentEntity->getSubEntity(1)->setMaterial(m_pOgreHeadMatHigh); break; } } }

* onExitButton(): If the exit button was pressed, leave this application state
  • moveCamera(): Translate the camera by the vector assembled in getInput() (10 times faster if Shift is pressed)
Copy to clipboard
bool GameState::onExitButtonGame(const CEGUI::EventArgs &args) { m_bQuit = true; return true; } //||||||||||||||||||||||||||||||||||||||||||||||| void GameState::moveCamera() { if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_LSHIFT)) m_pCamera->moveRelative(m_TranslateVector); m_pCamera->moveRelative(m_TranslateVector / 10); }

* getInput(): If we are not in chat mode, we want to move the camera via the keys, so in this case assemble a translation vector that is then applied to the camera in moveCamera()
Copy to clipboard
void GameState::getInput() { if(m_bChatMode == false) { if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_A)) { m_TranslateVector.x = -m_MoveScale; } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_D)) { m_TranslateVector.x = m_MoveScale; } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_W)) { m_TranslateVector.z = -m_MoveScale; } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_S)) { m_TranslateVector.z = m_MoveScale; } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_LEFT)) { m_pCamera->yaw(m_RotScale); } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_RIGHT)) { m_pCamera->yaw(-m_RotScale); } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_UP)) { m_pCamera->pitch(m_RotScale); } if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_DOWN)) { m_pCamera->pitch(-m_RotScale); } } }

* update(): Update the whole state:
  1. leave and remove this state from the active state stack if requested
  2. determine the move scale depending on the time passed since the last frame
  3. reset the translation vector for the camera
  4. get the unbuffered input and move the camera
Copy to clipboard
void GameState::update(double timeSinceLastFrame) { if(m_bQuit == true) { this->popAppState(); return; } m_MoveScale = m_MoveSpeed * timeSinceLastFrame; m_RotScale = m_RotateSpeed * timeSinceLastFrame; m_TranslateVector = Vector3::ZERO; getInput(); moveCamera(); }

* setBufferedMode(): Alters all needed GUI captions to fit the current input mode
  • setUnbufferedMode(): Alters all needed GUI captions to fit the current input mode
Copy to clipboard
void GameState::setBufferedMode() { CEGUI::Editbox* pModeCaption = (CEGUI::Editbox*)m_pMainWnd->getChild("ModeCaption"); pModeCaption->setText("Buffered Input Mode"); CEGUI::Editbox* pChatInputBox = (CEGUI::Editbox*)m_pChatWnd->getChild("ChatInputBox"); pChatInputBox->setText(""); pChatInputBox->activate(); pChatInputBox->captureInput(); CEGUI::MultiLineEditbox* pControlsPanel = (CEGUI::MultiLineEditbox*)m_pMainWnd->getChild("ControlsPanel"); pControlsPanel->setText("[Tab] - To switch between input modes\n\nAll keys to write in the chat box.\n\nPress [Enter] or [Return] to send message.\n\n[Print] - Take screenshot\n\n[Esc] - Quit to main menu"); } //||||||||||||||||||||||||||||||||||||||||||||||| void GameState::setUnbufferedMode() { CEGUI::Editbox* pModeCaption = (CEGUI::Editbox*)m_pMainWnd->getChild("ModeCaption"); pModeCaption->setText("Unuffered Input Mode"); CEGUI::MultiLineEditbox* pControlsPanel = (CEGUI::MultiLineEditbox*)m_pMainWnd->getChild("ControlsPanel"); pControlsPanel->setText("[Tab] - To switch between input modes\n\n[W] - Forward\n[S] - Backwards\n[A] - Left\n[D] - Right\n\nPress [Shift] to move faster\n\n[O] - Toggle Overlays\n[Print] - Take screenshot\n\n[Esc] - Quit to main menu"); }

DemoApp.hpp

The DemoApp class is the central application organizing location. It only has two member variables:
  • the application state manager
  • an indicator of whether the application is to be shut down or not


Note: All used game states must be included here!

Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #ifndef OGRE_DEMO_HPP #define OGRE_DEMO_HPP //||||||||||||||||||||||||||||||||||||||||||||||| #include "AdvancedOgreFramework.hpp" #include "AppStateManager.hpp" #include "MenuState.hpp" #include "GameState.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| class DemoApp { public: DemoApp(); ~DemoApp(); void startDemo(); private: AppStateManager* m_pAppStateManager; bool m_bShutdown; }; //||||||||||||||||||||||||||||||||||||||||||||||| #endif //|||||||||||||||||||||||||||||||||||||||||||||||

DemoApp.cpp

The central application file, managing everything:
  • DemoApp(): Constructor
  • ~DemoApp(): Destructor
  • startDemo(): Starts the application:
  1. fires up the OgreFramework
  2. initializes Ogre (via the OgreFramework)
  3. create an AppStateManager instance
  4. creates the applcaition states (MenuState and GameState)
  5. orders the AppStateManager to start its main loop function
Copy to clipboard
//||||||||||||||||||||||||||||||||||||||||||||||| #include "DemoApp.hpp" //||||||||||||||||||||||||||||||||||||||||||||||| DemoApp::DemoApp() { m_bShutdown = false; } //||||||||||||||||||||||||||||||||||||||||||||||| DemoApp::~DemoApp() { delete OgreFramework::getSingletonPtr(); delete m_pAppStateManager; } //||||||||||||||||||||||||||||||||||||||||||||||| void DemoApp::startDemo() { new OgreFramework(); OgreFramework::getSingletonPtr()->initOgre("AdvancedOgreFramework", 0, 0); OgreFramework::getSingletonPtr()->m_pLog->logMessage("Demo initialized!"); m_pAppStateManager = new AppStateManager(); MenuState::create(m_pAppStateManager, "MenuState"); GameState::create(m_pAppStateManager, "GameState"); m_pAppStateManager->start(m_pAppStateManager->findByName("MenuState")); } //|||||||||||||||||||||||||||||||||||||||||||||||

Notes

1. If you want to, you can declare all the Ogre members in the OgreFramework class (Root, Camera, RenderWindow, SceneManager, ...) private and implement getter methods for them. This is mostly a matter of code style and one's preferences. You could also totally remove most of them as they are also singletons (e.g. Ogre::Root, Ogre::Log) and retrieve them via the singleton functions. It's up to you!

2. If Ogre::WindowEventUtilities::messagePump() doesn't work for you, try this instead:

Copy to clipboard
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) m_bShutdown = true; else { TranslateMessage(&msg); DispatchMessage(&msg); } } } #endif


3. On Mac, the Sleep() function is written with a lower-case S at the beginning, so you either have to change this or (if you want both in your code) use this instead:

Copy to clipboard
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 Sleep(1000); #elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE sleep(1000); #endif


4. In this forum post you will find a list of all needed changes in order to get this running with XCode on Mac.

Conclusion

Fatigued but happy, the little young programmer boy closed the secret book, feeling his head humming due to all the new great knowledge. However, with a satisfied smile on his face, he promised his teacher - the friendly Ogre - to test all these new valuable insights the next day after catching a big load of sleep...

History

Information Version
Sun 27 of May, 2018 22:42 GMT-0000 paroj 86
Mon 26 of Mar, 2018 09:34 GMT-0000 paroj 85
Sun 25 of Mar, 2018 13:25 GMT-0000 paroj 84
Fri 03 of Nov, 2017 13:20 GMT-0000 spacegaier 83
Tue 10 of Sep, 2013 16:04 GMT-0000 Hazzr Minor typographical and code changes 82
Mon 09 of Sep, 2013 08:58 GMT-0000 Hazzr Typo 81
Mon 09 of Sep, 2013 08:39 GMT-0000 Hazzr Typo 80
Sat 12 of May, 2012 01:25 GMT-0000 fsxfreak 79
Sat 12 of May, 2012 01:24 GMT-0000 fsxfreak added useful camera up and down and roll controls 78
Mon 30 of Apr, 2012 06:00 GMT-0000 fsxfreak BackToGameBtn was not working before, works with one line code addition 77
Mon 16 of Apr, 2012 02:57 GMT-0000 DarkAnt Fixed minor typo. ms_Singleton was missing an underscore. 76
Sun 05 of Feb, 2012 11:05 GMT-0000 spacegaier 75
Sun 05 of Feb, 2012 11:03 GMT-0000 spacegaier added link to new BitBucket repo and did some code changes 74
Mon 01 of Aug, 2011 00:09 GMT-0000 jacmoe 73
Tue 29 of Mar, 2011 20:10 GMT-0000 spacegaier fixed possible RT error as described in the support thread on 29th March 2011 72
Thu 10 of Feb, 2011 00:07 GMT-0000 spacegaier removed note 4 since it was not important 71
Thu 10 of Feb, 2011 00:04 GMT-0000 spacegaier 70
Thu 10 of Feb, 2011 00:02 GMT-0000 spacegaier 69
Thu 10 of Feb, 2011 00:02 GMT-0000 spacegaier 68
Wed 09 of Feb, 2011 23:37 GMT-0000 spacegaier linux fix: sleep() wants seconds and not milliseconds 67
Wed 09 of Feb, 2011 23:33 GMT-0000 spacegaier little fixes 66
Wed 09 of Feb, 2011 23:33 GMT-0000 spacegaier little refinements 65
Wed 09 of Feb, 2011 23:32 GMT-0000 spacegaier added pauseAppState 64
Wed 09 of Feb, 2011 23:24 GMT-0000 spacegaier aded missing buttonHit() method 63
Wed 09 of Feb, 2011 23:23 GMT-0000 spacegaier little fixes 62