Advanced Ogre Framework         A young Ogre programmers Advanced Framework
Print



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:

Quote:
"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 taught 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:

Quote:
"I know the perfect secret book for you. It 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...

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

 

help If you should encounter any problems while reading, please post in the forum thread for this article(external link).

 

Note: An older version of the framework made by a forum user for Ogre 1.7.1 and CEGUI 0.7.1 can be found here(external link).

 

Image
Advanced Ogre Framework - Menu state
Image
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 17 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 RapidXML DotSceneLoader. That makes 14 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 10 files that form together the application state system. But since htere are three different application states in this demo framework, many code parts are redundant and just copied from one state to the other. So the actual amount a "unique code lines to understand" isn't that high!

 

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.

//|||||||||||||||||||||||||||||||||||||||||||||||
 
#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
{
	DemoApp demo;
	try	
	{
		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 RapidXML DotSceneLoader page. I just copied these two files from there:

  • DotSceneLoader.hpp
  • DotSceneLoader.cpp

In order to make them work you also need to include rapidxml.hpp (more information of the DotSceneLoader page).

 

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
  • SDKTrays Manager

 
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.

//|||||||||||||||||||||||||||||||||||||||||||||||
 
#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 <SdkTrays.h>
 
//|||||||||||||||||||||||||||||||||||||||||||||||
 
class OgreFramework : public Ogre::Singleton<OgreFramework>, OIS::KeyListener, OIS::MouseListener
{
public:
	OgreFramework();
	~OgreFramework();
 
	bool initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener = 0, OIS::MouseListener *pMouseListener = 0);
	void updateOgre(double timeSinceLastFrame);
 
	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;
 
        OgreBites::SdkTrayManager*	        m_pTrayMgr;
 
private:
	OgreFramework(const OgreFramework&);
	OgreFramework& operator= (const OgreFramework&);
};
 
//|||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
//|||||||||||||||||||||||||||||||||||||||||||||||

 

AdvancedOgreFramework.cpp

  • First line: Singleton is initialized
  • OgreFramework(): Constructor
//|||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AdvancedOgreFramework.hpp"
 
//|||||||||||||||||||||||||||||||||||||||||||||||
 
using namespace Ogre;
 
//|||||||||||||||||||||||||||||||||||||||||||||||
 
template<> OgreFramework* Ogre::Singleton<OgreFramework>::ms_Singleton = 0;
 
//|||||||||||||||||||||||||||||||||||||||||||||||
 
OgreFramework::OgreFramework()
{
    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_pTrayMgr			= 0;
}
 
//|||||||||||||||||||||||||||||||||||||||||||||||

 

  • ~OgreFramework(): Destructor, clearing up
OgreFramework::~OgreFramework()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE...");
    if(m_pTrayMgr)              delete m_pTrayMgr;
    if(m_pInputMgr)		OIS::InputManager::destroyInputSystem(m_pInputMgr);
    if(m_pRoot)			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 SDKTrayManager
  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!

bool 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();
 
    if(!m_pRoot->showConfigDialog())
        return false;
    m_pRenderWnd = m_pRoot->initialise(true, wndTitle);
 
    m_pViewport = m_pRenderWnd->addViewport(0);
    m_pViewport->setBackgroundColour(ColourValue(0.5f, 0.5f, 0.5f, 1.0f));
 
    m_pViewport->setCamera(0);
 
    size_t 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_pTrayMgr = new OgreBites::SdkTrayManager("AOFTrayMgr", m_pRenderWnd, m_pMouse, 0);
 
    m_pTimer = new Ogre::Timer();
    m_pTimer->reset();
 
    m_pRenderWnd->setActive(true);
 
    return 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
bool OgreFramework::keyPressed(const OIS::KeyEvent &keyEventRef)
{
    if(m_pKeyboard->isKeyDown(OIS::KC_SYSRQ))
    {
        m_pRenderWnd->writeContentsToTimestampedFile("AOF_Screenshot_", ".jpg");
        return true;
    }
 
    if(m_pKeyboard->isKeyDown(OIS::KC_O))
    {
        if(m_pTrayMgr->isLogoVisible())
        {
            m_pTrayMgr->hideFrameStats();
            m_pTrayMgr->hideLogo();
        }
        else
        {
            m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
            m_pTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
        }
    }
 
    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...nothing. In this application all updates are taken care of by the application states, but if there would be some central Ogre update task, the would go in here.
void OgreFramework::updateOgre(double timeSinceLastFrame)
{
}

 



Contributors to this page: Hazzr762 points  , Spacegaier6073 points  , Slappy251 points  , progman118 points  , jacmoe180265 points  , fsxfreak260 points  and DarkAnt347 points  .
Page last modified on Tuesday 10 of September, 2013 16:04:13 UTC by Hazzr762 points .


The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.