One Function Ogre         Exploring how to start Ogre without using ExampleApplication.h or the built in Config Dialog

The Obligitory Warning

So you’re finally getting sick of “ExampleApplication.h”. It’s useful for playing with Ogre, but anything serious realistically requires a more hands on approach to setting up the engine. This is a very cut down explanation of the things that need to happen to get Ogre running. A lot of it is right out of “ExampleApplication.h”, but it’s in a more linear fashion. I only use Ogre from a Windows environment, so this tutorial is tailored to suit that. I also expect that you know how to create a new project and get Ogre linking in correctly. This tutorial accomplishes nothing that you’ll notice unless you use a debugger and step through the code as it runs. Also note, this is a prime example of sloppy coding. Variables are declared as we need them, and this whole thing is one long linear function. The point is, we’re learning, not writing Shiny New Thing 2000 and putting it in a big orange box on the shelves of the nearest Walmart. We’re slinging code as we go just to see if we can jumpstart Ogre without using any crutches. I've also never done the whole 'Wiki' thing before. Consider yourself warned.

Get your boilerplate ready

First you need to set up the project with a main.cpp or whatever you want to call it, and your WinMain entry point function all set up. For the lazy, here’s mine so you can copy/paste:

#include <Ogre.h>
  
  #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
  
  {
    using namespace Ogre; // <-- ultimate in lazy
  
    //you’ll start inserting your code here
  
    return 0;
  }

The Meaty Part



And now we're ready to begin the fun! Before we can do anything, we need to initialize Ogre, and that means creating a root singleton, so here we go! The additional parameters are entirely optional, but I put them in just incase you didn’t already know that you could change the names of the text files Ogre uses.

Root *mRoot;
  mRoot = new Root("plugins.cfg","display.cfg","log.txt");

For now, we’re going to just use the built in config dialog. Almost any commercial product would do everything in its power to avoid ever having this dialog seen by its users, but for now we’ll live with it.

if(! mRoot->showConfigDialog()){
    //gotta pick a valid renderer and hit the big Okay button man..
    return 0;
  }

And now we get to actually push the magic initialize button. It’s really not very exciting. There’s a lot of stuff going on in the background, but we don’t get to see any of it. We DO, however, get to choose our own window title!

RenderWindow* mWindow;
  mWindow = mRoot->initialise(true, "Some Window Title");

Next you have to pick a scene manager. You do need to be a little picky here. This will make or break your frames per second depending on what you’re doing. But since I have no idea what you’re planning, ST_GENERIC it is!

SceneManager* mSceneMgr;
  mSceneMgr = mRoot->getSceneManager(ST_GENERIC);

(Note: for Ogre 1.2 Dagon and up, getSceneManager has a different usage, to get the Scene Manager Pointer you must use the following. It's a subtle difference, but it won't compile otherwise. -Dragoon)

mSceneMgr = mRoot->createSceneManager(ST_GENERIC);

Now you need a camera in your scene, so go ahead and make one. We’ll also need to give it a logical start position, so we’ll do that as well.

//Oh a camera, how nice.
  Camera* mCamera;
  mCamera = mSceneMgr->createCamera("PlayerCam");
  // Position it at 500 in Z direction
  mCamera->setPosition(Vector3(0,0,500));
  // Look back along -Z
  mCamera->lookAt(Vector3(0,0,-300));
  mCamera->setNearClipDistance(5);

Okay, well we have a camera, and that’s pretty cool, but it isn’t drawing to anything yet. So to fix that, we’ll bind it to a viewport.

// Create one viewport, entire window
  Viewport* vp = mWindow->addViewport(mCamera);
  vp->setBackgroundColour(ColourValue(0,0,0));
  
  // Alter the camera aspect ratio to match the viewport
  mCamera->setAspectRatio(
  Real(vp->getActualWidth()) / Real(vp->getActualHeight()));

Now is when we take a moment to reflect on what we've accomplished so far. The end product looks like this, for all you really lazy copy/pasters:

#include <Ogre.h>
  
  #define WIN32_LEAN_AND_MEAN
  #include "windows.h"
  
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  
  INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
  {
  
    using namespace Ogre; // <-- ultimate in lazy
    Root *mRoot;
    mRoot = new Root("plugins.cfg","display.cfg","log.txt");
  
  
    if(! mRoot->showConfigDialog()){
      //gotta pick a valid renderer and hit the big Okay button man..
      return 0;
    }
    RenderWindow* mWindow;
    mWindow = mRoot->initialise(true,"Some Window Title");
 
 
    SceneManager* mSceneMgr;
    mSceneMgr = mRoot->getSceneManager(ST_GENERIC);
    // mSceneMgr = mRoot->createSceneManager(ST_GENERIC); // for OGRE 1.2 Dagon
  
    Camera* mCamera;
    mCamera = mSceneMgr->createCamera("PlayerCam");
    // Position it at 500 in Z direction
    mCamera->setPosition(Vector3(0,0,500));
    // Look back along -Z
    mCamera->lookAt(Vector3(0,0,-300));
    mCamera->setNearClipDistance(5);
  
    // Create one viewport, entire window
    Viewport* vp = mWindow->addViewport(mCamera);
    vp->setBackgroundColour(ColourValue(0,0,0));
  
    // Alter the camera aspect ratio to match the viewport
    mCamera->setAspectRatio(
    Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
  
  
    /*
    If you really wanted to, you could call this:
    mRoot->startRendering();
    But all that would accomplish is a window that you can only close by hitting the big X in the 
    corner, and even then the application behind it will run headless until you kill the
    process. Not what we're after, but if you did actually have a
    working FrameListener set up as well, this is where you would call the startRendering method.
    */
 
    return 0;
  }
  
  #ifdef __cplusplus
  }
  #endif

Amazingly enough, this madness compiles, links, and runs. But it doesn’t really do anything, and since this isn’t a tutorial on how to make pretty objects float all over the screen, it never will. We have an app that initializes Ogre, sets up a scene and a camera, and then promptly quits. Perfect!

From Bad to Worse

That whole config dialog is nice and all, but our project isn’t called Ogre now is it? Let’s just get rid of the whole thing and do it ourselves.

For this tutorial, we’re going to use DX9, and that’s all! Nothing less will do, because we’re elite game programmers and we can make “outrageous system requirements”. (Like having the latest version of DirectX is that outrageous…) We’re also going to hard-code a starting screen resolution of 800x600 in 16-bit color. You could start in a higher resolution, since you do have access to enumerate all available video modes and make sure that the video card supports it, but you can’t always check to see if their monitor can support it! 800x600 is a safe bet that it will work, and that will at least get the user into the game so they can change it to something they prefer.

You know those 4 lines that look like this:

if(! mRoot->showConfigDialog()){
    //gotta pick a valid renderer and hit the big Okay button man..
    return 0;
  }

Delete them. We’re going to replace them with some different looking code.

RenderSystemList *rsList = mRoot->getAvailableRenderers();
     int c = 0;
     RenderSystem *selectedRenderSystem = 0;
 
     do {
         if( c == (int) rsList->size())
             return 0;
         selectedRenderSystem = rsList->at(c);
         c++;
     } while( selectedRenderSystem->getName().compare("Direct3D9 Rendering Subsystem") != 0 );

All the above code does is sift through the available rendering subsystems and picks out exactly the one we want, which happens to be Direct-X 9’s Direct3D renderer.

NOTE: "Direct3D9 Rendering SubSystem" may not work. In particular, the string was compared with a case-sensitive compare() function and required "SubSystem" to be "Subsystem". It may have worked the other way in a previous iteration. — jhammer

So now that we have it:

//we found it, we might as well use it!
  mRoot->setRenderSystem(selectedRenderSystem);

Now we need to pick a resolution and tell it not to use fullscreen. Windowed mode is the absolutely most important part here. (Windowed mode is your friend, especially when you’re trying to debug something.) So let’s add these lines to our code:

selectedRenderSystem->setConfigOption("Full Screen","No");
  selectedRenderSystem->setConfigOption("Video Mode","800 x 600 @ 16-bit colour");

Okay, now you're probably wondering how I knew those two little lines would work. Time to explore. If you don’t know what std::map is, and what it does/eats/etc, stop reading here go look it up on Google, and come back when you’re done. You’re going to at least want to know what it basically does. When you’re done with that, add this mess to your code:

//retrieve the config option map
  ConfigOptionMap comap = selectedRenderSystem->getConfigOptions();
 
  //and now we need to run through all of it
  ConfigOptionMap::const_iterator start = comap.begin();
  ConfigOptionMap::const_iterator end = comap.end();
  while(start != end){
    String OptionName = start->first;
    String CurrentValue = start->second.currentValue;
    StringVector PossibleValues = start->second.possibleValues;
    int c=0;
    while (c < (int) PossibleValues.size()){
      String OneValue = PossibleValues.at(c);
      c++;
    }
    start++;
  }

Okay, that was a mouthful. Now the important part is to compile it, and step through it, looking at the values that get stored into the various variables. Basically, all this does is iterate through each config option, and then each of that option’s potential values. The important thing that we’re learning here, is what Ogre calls everything. We can see that you can’t specify a Width x Height resolution and a color bit-depth separately. They are linked, and on top of that, some of the values are a little odd. (When is the last time you ran a game at 1856x1392 in 16-bit color?) But that is what you get when you ask a video card for all supported modes. You get things that make no sense at all, but they’re supported! Regardless, anything you glean from these values can be fed into selectedRenderSystem->setConfigOption() just like we did earlier with the Full Screen option. Just remember that you need to this before you call mRoot->initialise(), or what you chose won't have an effect.

Now here’s the copy/paste code for all you ultra-lazy types:

#include <Ogre.h>
  
  #define WIN32_LEAN_AND_MEAN
  #include "windows.h"
  
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  
  INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
  {
  
  using namespace Ogre; // <-- ultimate in lazy
  Root *mRoot;
  mRoot = new Root("plugins.cfg","display.cfg","log.txt");
  
  
  RenderSystemList *rsList = mRoot->getAvailableRenderers();
  int c=0;
  bool foundit = false;
  RenderSystem *selectedRenderSystem=0;
  while(c < (int) rsList->size()){
    selectedRenderSystem = rsList->at(c);
    String rname = selectedRenderSystem->getName();
    if(rname.compare("Direct3D9 Rendering Subsystem")==0){
      foundit=true;
      break;
    }
    c++; // <-- oh how clever
  }
  if(!foundit) return 0; //we didn't find it...
  
  //we found it, we might as well use it!
  mRoot->setRenderSystem(selectedRenderSystem);
  
  selectedRenderSystem->setConfigOption("Full Screen","No");  
  selectedRenderSystem->setConfigOption("Video Mode","800 x 600 @ 16-bit colour");
  
  //retrieve the config option map
  ConfigOptionMap comap = selectedRenderSystem->getConfigOptions();
  
  //and now we need to run through all of it
  ConfigOptionMap::const_iterator start = comap.begin();
  ConfigOptionMap::const_iterator end = comap.end();
  while(start != end){
    String OptionName = start->first;
    String CurrentValue = start->second.currentValue;
    StringVector PossibleValues = start->second.possibleValues;
    int c=0;
    while (c < (int) PossibleValues.size()){
      String OneValue = PossibleValues.at(c);
      c++;
    }
    start++;
  }
  
  
  RenderWindow* mWindow;
  mWindow = mRoot->initialise(true,"Some Window Title");
  
  
  SceneManager* mSceneMgr;
  mSceneMgr = mRoot->getSceneManager(ST_GENERIC);
  // mSceneMgr = mRoot->createSceneManager(ST_GENERIC); // for OGRE 1.2 Dagon
  
  Camera* mCamera;
  mCamera = mSceneMgr->createCamera("PlayerCam");
  // Position it at 500 in Z direction
  mCamera->setPosition(Vector3(0,0,500));
  // Look back along -Z
  mCamera->lookAt(Vector3(0,0,-300));
  mCamera->setNearClipDistance(5);
  
  // Create one viewport, entire window
  Viewport* vp = mWindow->addViewport(mCamera);
  vp->setBackgroundColour(ColourValue(0,0,0));
  
  // Alter the camera aspect ratio to match the viewport
  mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
  
  /*
  If you really wanted to, you could call this:
  mRoot->startRendering();
  But all that would accomplish is a window that you can only close by hitting the big X in the 
  corner, and even then the application behind it will run headless until you kill the
  process. Not what we're after, but if you did actually have a
  working FrameListener set up as well, this is where you would call the startRendering method.
  */
  
  return 0;
  }
  
  #ifdef __cplusplus
  }
  #endif

And there you have it, my $0.02 on learning how to set up Ogre without using the ExampleApplication.h crutch, and with all the pasta you just made, you can eat for a week! =D

This is buggy and outdated- even with cleaning up there are over 150 leaks. Can anyone fix this up?

Note: memory leaks can be fixed by deleting mRoot before return 0; As an Ogre noob judging from the creation order, this is what you want before returning:

mWindow->removeViewport(vp->getZOrder());
  mSceneMgr->destroyCamera(mCamera);
  mRoot->destroySceneManager(mSceneMgr);
  mWindow->destroy();
  mRoot->shutdown();
  delete mRoot;