Basic Ogre Application        

IMPORTANT: This framework is meant to be used with old releases of Ogre. For Ogre 1.10+, rather use OgreBites::ApplicationContext.

First Steps

In this tutorial, you will create and run a barebones Ogre program. As you might guess, this program is very simple and teaches you the very basics of what you'll need to do in each Ogre program. I recommend that you go over this tutorial until you understand everything in it.

I will teach you about Ogre's core objects, but I will not teach you how to show things on the screen just yet. I believe that going that far in this tutorial would compromise the amount of space I could devote to ensuring that you first understand the essentials. N.B. This tutorial does not show you how to get entities and other things on screen, but merely shows you how to get a very basic barebones application running without the use of any frameworks.

Let's get started!

Unlike all of the other demos and tutorials out there, this one will teach you how to make an application without using the ExampleApplication framework. I believe the pre-made framework doesn't give the user a good idea of what they need to do in order to get Ogre running.

Part 1: Project Setup

The first thing you need to do is start a new project. I created a project in a subfolder (Tutorial_2) of the folder that contains our Ogre distribution (in my case, C:\Programming\Tutorials\ and \User\robo\Desktop\Tutorials\ for PC and Mac respectively).

Create that folder, and then open your IDE. Here the tutorial branches into two different sections, one to set up a project in Visual C++ and one to set up your project in XCode.

Visual C++
Click "File->New->Blank Solution". Now enter the solution's name - Ogre_Tutorial. In the Location Box, press Browse and then navigate to the folder you made for your project. Press OK. If you look at the Solution Explorer, "Solution ‘Ogre_Tutorial’ (0 projects)" should now be present.

Now we need to create our project. Right click on the "Solution ‘Ogre_Tutorial’ (0 projects)" text and select "Add->New Project". A box should pop up. In the 'Project Types' box select 'Visual C++ Projects' / 'Win32'. Select 'Win32 Project' in the right-hand pane. In the Name box, type Ogre_Tutorial. In the Location box, browse again to the C:\Programming\Tutorials\Tutorial_2 folder. Click OK. Another box should show up, with two links on the upper left side. Click the bottom link, "Application Settings". Leave the Application Type on 'Windows Application', and check the 'Empty Project' checkbox below.

Add an empty C++ file to the project called main.cpp . This file must exist before editing project properties in order for the 'C/C++' menu to appear!

Now that the project is created, we have a lot of settings to define. Click "Project->Properties" in the top toolbar. Now you need to configure some settings. The paths specified in these settings are appropriate to the configuration established in this and the previous tutorial (Newbie Tutorial 1).

Section Subsection Setting Name Value
C/C++ General Additional Include Directories "C:/Ogre_Tutorials/ogrenew/OgreMain/include"
C/C++ Code Generation Runtime Library Multithreaded Debug DLL
C/C++ Precompiled Headers Create/Use Precompiled Header Not Using Precompiled Headers
Linker General Additional Library Directories "C:/Ogre_Tutorials/ogrenew/OgreMain/lib/Debug"
Linker Input Additional Dependencies OgreMain_d.lib


If you subsequently wish to compile your code in Release Mode instead of Debug: change the 'Configuration' listbox at the top of the Properties Pages to 'Release'; input the above settings again in the Properties Pages, except for: in C/C++'s 'Code Generation' section, for 'Runtime Library' change 'Multithreaded Debug DLL' to 'Multithreaded DLL'; in Linker's 'Input' section, change OgreMain_d.lib to OgreMain.lib; change Linker's 'Additional Library Directories' filepath to end in 'Release' instead of 'Debug'.

Part 2: The Code

#include <iostream>
 
 #include "Ogre.h"
 #include <OIS/OIS.h>
 
 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
 #include <macUtils.h>
 #endif
 
 int main()
 {
 	std::string resourcePath;
 	
        #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
            resourcePath = Ogre::macBundlePath() + "/Contents/Resources/";
        #else
  	    resourcePath = "";
        #endif
 	
  	Ogre::Root* root = new Ogre::Root(resourcePath + "plugins.cfg", resourcePath + "ogre.cfg", "Ogre.log");
 	
  	if (!root->showConfigDialog())
 		return -1;
  	
  	Ogre::ConfigFile cf;
  	cf.load(resourcePath + "resources.cfg");
  	
 	// Go through all sections & settings in the file
 	Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
 	
 	Ogre::String secName, typeName, archName;
 	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;
 			
 			#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
                            if (!Ogre::StringUtil::startsWith(archName, "/", false)) // only adjust relative dirs
                                archName = Ogre::String(Ogre::macBundlePath() + "/" + archName);
 			#endif
  			
 			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
 		}
 	}
 	
 	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
 	
 	Ogre::RenderWindow* window = root->initialise(true);
 	Ogre::SceneManager* smgr = root->createSceneManager(Ogre::ST_GENERIC);
 	
 	//Input Stuff
 	size_t windowHnd = 0;
 	window->getCustomAttribute("WINDOW", &windowHnd);
 	OIS::InputManager* im = OIS::InputManager::createInputSystem(windowHnd);
 	OIS::Keyboard* keyboard = static_cast<OIS::Keyboard*>(im->createInputObject(OIS::OISKeyboard, true));
 	
 	while (1)
 	{
 		Ogre::WindowEventUtilities::messagePump();
 		
 		keyboard->capture();
 		
 		if (keyboard->isKeyDown(OIS::KC_ESCAPE))
 			break;
 		
 		if(root->renderOneFrame() == false)
 			break;
 	}
 	
 	im->destroyInputObject(keyboard);
 	im->destroyInputSystem(im);
 	im = 0;
 	
 	delete root;
 	return 0;
 }

Part 3: Code Breakdown

Alright, let's break down this code step by step to see just what's going on.

#include <iostream>
 
 #include "Ogre.h"
 #include <OIS/OIS.h>
 
 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
 #include <macUtils.h>
 #endif

Pretty basic setup here, we have to include "Ogre.h" and <OIS/OIS.h> to use Ogre and OIS, and iostream is there for some basic output stuff. Finally, in order to support OS X and iPhones, we have to load up the macUtils.h file (included in Ogre) in order to do some processing stuff later on in the tutorial. (NB: this code is cross platform and works on both Windows and OS X. It has not been tested for use in Linux)

int main()
 {
 	std::string resourcePath;
 	
     #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
         resourcePath = Ogre::macBundlePath() + "/Contents/Resources/";
     #else
 	 resourcePath = "";
     #endif

Now we start our main function. Before we get into any of the nitty gritty Ogre stuff, we need one housekeeping variable: resourcePath. This variable is only used on Macs to compensate for the way that app bundles are structured. If you're not looking to make OSX apps, you can ignore this variable. We set this variable to the app path plus /Contents/Resources/. If you're familiar with how apps work on OS X and in XCode, all of your data is stored within the app bundle, so there's no root folder like in Windows and Linux. Thus, we need to tell Ogre where to look for resources later on.

Ogre::Root* root = new Ogre::Root(resourcePath + "plugins.cfg", resourcePath + "ogre.cfg", "Ogre.log"); 
	
 if (!root->showConfigDialog())
     return -1;

Now we create and initialize our Ogre::Root. Ogre::Root is the core of the Ogre engine. All of what Ogre does is initiated through the Root. Needless to say, it's an important thing. In order to create it, we create a pointer to an Ogre::Root object and initialize it. The constructor for Root takes three parameters, a plugins.cfg file, a ogre.cfg file, and a log file. The plugins.cfg file lists the different plugins for Ogre to load when the Root is initialized. My plugins.cfg file looks like this:

# Defines plugins to load
 
 # Define plugin folder
 PluginFolder=
 
 # Define plugins
 Plugin=RenderSystem_GL
 Plugin=Plugin_CgProgramManager
 Plugin=Plugin_OctreeSceneManager

The ogre.cfg file manages the device settings. Luckily, we won't have to manual enter this, the config dialog will take care of setting it up for us. Finally, the ogre.log file is a file which saves the log from the last run of ogre. It's very helpful for debugging and troubleshooting. The next line (root->showConfigDialog()) shows the configuration dialog, which will allow you to set various device settings, such as fullscreen, vsync, and the like.

// Go through all sections & settings in the file
 Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
 	
 Ogre::String secName, typeName, archName;
 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;
 		
 		#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
                     if (!Ogre::StringUtil::startsWith(archName, "/", false)) // only adjust relative dirs
                     archName = Ogre::String(Ogre::macBundlePath() + "/" + archName);
  		#endif
 			
 		Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
 	}
 }
 	
 Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

Now we get into some gritty stuff. In order for Ogre to be able to see our data, we have to tell it where the data is hiding. In order to do this, we set up a resources.cfg file. This file has a structure that helps Ogre deal with different resource groups and locations throughout your project tree. My resources.cfg file looks like this:

# Resource locations to be added to the default path
 [General]
 FileSystem=./Contents/Resources/media

There are a few things to note on this file. Comments are denoted with a # sign and are single lined. Resource groups are wrapped in [] (The [General] is the only resource group I have set up). These resource groups are useful for deferred loading. For example, you could load the General group for your main GUI and main menu data, then as you load a level, that could have its own separate resource group that could be loaded later! Cool stuff! Finally, there are two different types of file systems that Ogre can use out of the box. First off is the FileSystem type. These FileSystems are just standard folders, either relative to the working directory or as an absolute path. Second, Ogre can load standard zip archives (e.g. Zip=./archive.zip).

Once this file is loaded, Ogre does some parsing in order to separate each line into three keys, the file system name, the type of file system, and the resource group. Once this parsing is done, the resource location is added to the resource group manager. Finally, after the entire file is parsed, we initialize all the resource groups we loaded. (N.B. for more advanced projects, you won't want to do this, but instead load only your core data).

Ogre::RenderWindow* window = root->initialise(true); 
 Ogre::SceneManager* smgr = root->createSceneManager(Ogre::ST_GENERIC);

Next up, we have to create some stuff to make sure we can actually render some graphics! First of all, we have to open a render window. We do this by calling root->initalise(true). This function returns a RenderWindow* and asks if we want to autocreate our window. For the most part, we will want to create one automatically.

Second, we make a scene manager. The Scene Manager is one of critical aspects of any Ogre program. In order to render anything, we need some sort of Scene Manager to tell Ogre how to render. The scene manager takes care of most graphical aspects including culling, world geometry, and node placement. We'll delve more into this in later tutorials. For now, we choose the Generic scene manager because it's generic and suits our purposes well!

//Input Stuff
 size_t windowHnd = 0;
 window->getCustomAttribute("WINDOW", &windowHnd);
 OIS::InputManager* im = OIS::InputManager::createInputSystem(windowHnd);
 OIS::Keyboard* keyboard = static_cast<OIS::Keyboard*>(im->createInputObject(OIS::OISKeyboard, true));

Now, we need some form of input in order to tell our application when to quit. To do this, we use OIS, the input system included in Ogre. Basically, this code grabs the window handle from Ogre and then pushes it to OIS to create an input system. Finally, we create a keyboard object so we can get the state of the keyboard. For more information on using OIS, refer to the OIS Wiki pages.

In terms of initializing Ogre, we're finished! That is all you need to set up a bare-bones Ogre application! This application is pretty useless though because it doesn't actually render anything, so let's make a render loop. This loop serves as the main logic center of the application, and it's here that all of the rendering, event handling, and logic of your program will go. Let's take a look:

while (1)
 {
 	Ogre::WindowEventUtilities::messagePump();
		
 	keyboard->capture();
 		
 	if (keyboard->isKeyDown(OIS::KC_ESCAPE))
 		break;
 		
 	if(root->renderOneFrame() == false)
 		break;
 }

For starters, we create an infinite loop (while (1) { }) which will go until we break out of it. Once inside the loop, we need to do some housekeeping stuff to keep Ogre happy. Because we are not using root->startRendering(), we must do this ourselves, but fortunately it's easy. Ogre::WindowEventUtilities::messagePump() offers a cross platform way to check and pump system messages through the window. Without this line, we wouldn't be able to minimize or pause our application when we move away from the window.

Next, we capture the state of the keyboard, so we can check its state. The next line does just that, checks the keyboard state to see if the user pressed the Escape key. If they did, the loop breaks and we exit the program.

Finally, if the user didn't press escape, we render a single frame by calling root->renderOneFrame(). If this returns false, we break and quit the program.

That's it for the render loop! This is a really basic loop, but it does the trick and gets us rendering with the least amount of effort!

All that's left is to do some final housekeeping before we quit the program!

im->destroyInputObject(keyboard);
 	im->destroyInputSystem(im);
 	im = 0;
 	
 	delete root;
 	return 0;
 }

First, we destroy the keyboard object and then the input system. Finally, we delete the root object in order to shut Ogre down. Then we're done! We return 0 and the program exits!

Part 4: Conclusion

I hope that this tutorial has helped you get a grip on Ogre's design and core objects.