Making a 3D Game With Ogre - Opening a Window

Dec 18th, 2009 by mcasperson

This tutorial series shows you how to make a game with the Ogre 3D engine. In this tutorial we initialise the Ogre engine and open a window.

DOWNLOAD THE DEMO AND CODE FOR LINUX

DOWNLOAD THE DEMO AND CODE FOR WINDOWS

RETURN TO THE TUTORIAL INDEX

Ogre is arguably one of the top free 3D engines available. It has been used as the basis of a number of successful commercial games, has a huge and active community, and includes a number of cutting edge features. In this tutorial series we will see how to create a simple shoot'em'up using the Ogre 3D engine. But before we can start making the game, we need to get some of the basics done, like initialise the Ogre engine and open a window on the screen. 

EngineManager.h

/*
 *   OgreEngineManager.h
 *
 *   Created on: 18/12/2009
 *      Author: Matthew Casperson
 */

#ifndef OGREENGINEMANAGER_H_
#define OGREENGINEMANAGER_H_

#include "memory"
#include "string"
#include "map"

#include "OGRE/Ogre.h"
#include "OIS/OIS.h"

#include "ResourceLocationDefinition.h"

// define this to make life a little easier
#define ENGINEMANAGER OgreEngineManager::Instance()

// All OGRE objects are in the Ogre namespace.
using namespace Ogre;

/**
 Manages the OGRE engine.
*/
class OgreEngineManager :
 public WindowEventListener,
 public FrameListener
{
public:
 /**
  Destructor
 */
 ~OgreEngineManager();
 /**
  Singelton access
  @return The single instance of this class
 */
 static OgreEngineManager & Instance()
 {
  static OgreEngineManager instance;
  return instance;
 }
 /**
  Initialise the engine.
  @param pluginFileName Location for the plugin config file
  @param configFileName Location for the video config file
  @param logFileName Location for the log file
 */
 bool Startup(const std::string pluginFileName, 
 const std::string configFileName, const std::string logFileName);
 /**
  Initialise the engine.
 */
 void Shutdown();
 /**
  Shutdown OIS input services
 */
 void ShutdownInput();
 /**
  Gets the render window
  @return The render window
 */
 RenderWindow * GetRenderWindow() const
 {
  return window;
 }
 /**
  Gets the Ogre root
  @return The Ogre root
 */
 Root * GetRoot() const
 {
  return root.get();
 }
 /**
  Gets the OIS input manager
  @return The OIS inoput manager
 */
 OIS::InputManager* GetInputManager() const
 {
  return mInputManager;
 }
 /**
  Gets the OIS keyboard
  @return The OIS keyboard
 */
 OIS::Keyboard* GetKeyboard() const
 {
  return mKeyboard;
 }
 /**
  Gets the OIS mouse
  @return The OIS mouse
 */
 OIS::Mouse* GetMouse() const
 {
  return mMouse;
 }
 /**
  Starts the rendering loop. This function will not exit until
  the rendering loop is stopped.
 */
 void StartRenderLoop();
 /**
  Stops the rendering loop
 */
 void StopRenderLoop() {engineManagerRunning = false;}
 /**
  Adds a resource location to be loaded. Must be called before Startup is called.
 */
 void AddNewResourceLocation(const ResourceLocationDefinition& definition) 
 {resourceLocationDefinitionVector.push_back(definition);}
 /**
  Called when the window is closed.
 */
 void windowClosed(RenderWindow* rw);
 /**
  Called once per frame.
 */
 bool frameStarted(const FrameEvent& evt);

protected:
 /**
  Constructor. Initialises variables.
 */
 OgreEngineManager();
 /**
  Load resources from config file.
 */
 void SetupResources();
 /**
  Display config dialog box to prompt for graphics options.
 */
 bool Configure();
 /**
  Setup input devices.
 */
 void SetupInputDevices();
 /// OGRE Root
 std::auto_ptr     root;
 /// Default OGRE Camera
 Camera*        genericCamera;
 /// OGRE RenderWIndow
 RenderWindow*       window;
 /// flag indicating if the rendering loop is still running
 bool         engineManagerRunning;
 /// resource locations
 ResourceLocationDefinitionVector  resourceLocationDefinitionVector;
 //OIS Input devices
 OIS::InputManager*      mInputManager;
 OIS::Mouse*          mMouse;
 OIS::Keyboard*       mKeyboard;
};

#endif /* OGREENGINEMANAGER_H_ */


EngineManager.cpp

The constructor is used to set all of the pointers to NULL.

/*
 *   OgreEngineManager.cpp
 *
 *   Created on: 18/12/2009
 *      Author: Matthew Casperson
 */

#include "iostream"
#include "sstream" 
#include "OgreEngineManager.h"

OgreEngineManager::OgreEngineManager() :
 root(NULL),
 genericCamera(NULL),
 window(NULL),
 engineManagerRunning(true)
{

}

The destructor does nothing.

OgreEngineManager::~OgreEngineManager()
{

}

All of the initialisation is done in a function called Startup. This is because the OgreEngineManager class is a singleton, and one of the issues with using singletons is that you can't really be sure when they will be destroyed. To get around this all the initialistion is done in the Startup function, and all of the destruction is done in a function called Shutdown. The random function will be used quite a bit in the game, so we start by seeding the random function with the current time, which is a pretty common way to generate a random seed.

bool OgreEngineManager::Startup(const std::string pluginFileName, 
 const std::string configFileName, const std::string logFileName)
{
 // seed the random number generator
 srand( (unsigned)time( NULL ) );

Here we create a new root object. This is going to be the first step in initialising the Ogre engine. The pluginFileName is the name of the text file that contains the names of the Ogre plugins that will be used. The configFileName is the name of a file that Ogre will use to save the details about the renderer and screen resolution. The logFileName is the name of a log file that Ogre will write to.

root.reset(new Root(pluginFileName, configFileName, logFileName));

This code causes Ogre to display a dialog box with the graphics options. If the player cancels out of the dialog box we return false, which will cause the application to quit.

if (!Configure()) return false;

The default number of mipmaps are specified. You can find out more information on mipmaps here.

TextureManager::getSingleton().setDefaultNumMipmaps(5);

We call the SetupInputDevices function to initialise OIS (Object-Oriented Input System), which is an external library that has become the de facto standard for accessing input devices in Ogre.

SetupInputDevices();

The Ogre resources are then loaded. The Ogre samples load their resources from a text file, but I prefer to keep these values in the source code (it's one less thing that can be modified by the end user, and so is one less thing to go wrong). For this demo we won't actually load any resources, but this will become important later on.

SetupResources();

The OgreEngineManager needs to receive notifications of two events. Since it extends the FrameListener class, The OgreEngineManagers frameStarted function is called once per frame. And by extending the WindowEventListener class we are notified of the window closing with the windowClosed function. However, these functions are not triggered automatically - we first need to register the OgreEngineManager with the appropriate objects.

root->addFrameListener(this);
 WindowEventUtilities::addWindowEventListener(window, this);

Finally we return true to indicate that Ogre has been successfully initialised.

return true;
}

The shutdown function cleans up all of the Ogre resources. We start by removing the OgreEngineManager object from the list of FrameListeners and WindowEventListeners. This is a little redundant, because destroying the root object will do this anyway, but it doesn't hurt to the through.

void OgreEngineManager::Shutdown()
{ 
 root->removeFrameListener(this);
 WindowEventUtilities::removeWindowEventListener(window, this);


The Ogre Root is then deleted.

root.reset();

And the remaining variables are set back to NULL

genericCamera = NULL; 
 window = NULL;
}


The render loop is a continuous loop that calculates the next frame and then displays it on the screen. The render loop is started with the StartRenderLoop function.

void OgreEngineManager::StartRenderLoop()
{
 if (root.get())
  root->startRendering();
}

The SetupInputDevices function initialises OIS. This code is taken from a Wiki page on OIS, which has a detailed breakdown of the initialisation process.

void OgreEngineManager::SetupInputDevices()
{
 OIS::ParamList pl;
 size_t windowHnd = 0;
 std::ostringstream windowHndStr;

 window->getCustomAttribute("WINDOW", &windowHnd;);
 windowHndStr  0 )
  mKeyboard = static_cast(mInputManager->createInputObject( OIS::OISKeyboard, false ));
 if( mInputManager->getNumberOfDevices(OIS::OISMouse) > 0 )
  mMouse = static_cast(mInputManager->createInputObject( OIS::OISMouse, false ));
}

The ShutdownInput cleans up OIS. Again, refer to the Wiki page for more information on this code.

void OgreEngineManager::ShutdownInput()
{
 if( mInputManager )
 {
  if (mMouse) mInputManager->destroyInputObject( mMouse );
  if (mKeyboard) mInputManager->destroyInputObject( mKeyboard );

  OIS::InputManager::destroyInputSystem(mInputManager);
  mInputManager = NULL;
 }
}

The SetupResources function is where any Ogre resource files are loaded. These resource files are defined by the AddNewResourceLocation function, which will be used in later tutorials.

void OgreEngineManager::SetupResources()
{
 for (ResourceLocationDefinitionVector::const_iterator iter = resourceLocationDefinitionVector.begin();
  iter != resourceLocationDefinitionVector.end(); ++iter)
 {
  ResourceGroupManager::getSingleton().addResourceLocation((*iter).location, (*iter).type, (*iter).section);
 }

 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

The Configure function displays the initial dialog box where the video options are specified. If the user canceled out of the box, this function returns false. Otherwise it returns true.

bool OgreEngineManager::Configure()
{
 if (root->showConfigDialog())
 {
  window = root->initialise( true, "OGRE" );
  return true;
 }
 else
 {
  return false;
 }
}

The windowClosed function is called when the window has been closed. In this case we stop the Ogre render loop and shutdown OIS.

void OgreEngineManager::windowClosed(RenderWindow* rw)
{
 ShutdownInput();
 this->StopRenderLoop();
}

The enterFrame function is called once per frame. Here we capture any input from the mouse and keyboard, and if the escape key was pressed we exit the render loop, which exits the program.

bool OgreEngineManager::frameStarted(const FrameEvent& evt)
{
 mKeyboard->capture();
 mMouse->capture();

 if (mKeyboard->isKeyDown(OIS::KC_ESCAPE))
  this->StopRenderLoop();

 return engineManagerRunning;
}

ResourceLocationDefinition.h

The ResourceLocationDefinition class contains the information needed by Ogre to load an external resource.

#ifndef RESOURCELOCATIONDEFINITION_H_
#define RESOURCELOCATIONDEFINITION_H_

#include "string"
#include "vector"

struct ResourceLocationDefinition
{
 ResourceLocationDefinition(std::string type, std::string location, std::string section) :
  type(type),
  location(location),
  section(section)
 {

 }

 ~ResourceLocationDefinition() {}
 
 std::string type;
 std::string location;
 std::string section;
};

typedef std::vector ResourceLocationDefinitionVector;

#endif


Main.cpp

This file contains the main function, which is the entry point to the application. Here we start the OgreEngineManager, enter the render loop, and then shutdown the OgreEngineManager. The ENGINEMANAGER definition, from OgreEngineManager.h, provides a convenient way to access the OgreEngineManager singleton.

#include "OgreEngineManager.h"

#if 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
{
 if (ENGINEMANAGER.Startup(std::string("plugins.cfg"), std::string("ogre.cfg"), std::string("ogre.log")))
  ENGINEMANAGER.StartRenderLoop();

 ENGINEMANAGER.Shutdown();
}


The end result of all this code is a black window. However, with this groundwork laid we can start adding the 3D objects that will make up the game.

mcasperson

Written by mcasperson

Rate this Article:

Rating: 5.0/5 (5 votes cast)

Add new comment

* You must be logged in order to leave comments, please Sign in or join us.

Comments

, over a year ago
Report comment

I think i understand most of your code but as always, finding the right environement, and including the right libraries for the compiling an linking mess seems far more difficult than the code itself.

is there a makefile ?

if not, is there a big single file that can make a basic working app with a single compiling line like
g++ bigfile.cpp -o myapp.bin

BinaryData, over a year ago
Report comment

I’m loving the tutorials. Are you eventually going to release some with actual 3D Camera viewing? Like an MMO’s camera moving?

Related Content