Skip to main content

History: Basic Tutorial 6

Source of version: 114

Copy to clipboard
            {TRANSCLUDE(page="tutbox")}This tutorial will __not__ use the ((Ogre Wiki Tutorial Framework)). Instead, we are going to show you how to get an application up and running without the help of the additional class. After this tutorial, you should have a basic understanding of most of the code that was hidden in the BaseApplication class.

The full source for this tutorial is ((BasicTutorial6SourceCurrent|here)).{TRANSCLUDE}
%tutorialhelp%
!Prerequisites
This tutorial assumes that you already know how to set up an Ogre project and compile it successfully. If you need help with this, then read ((Setting Up An Application)). This tutorial is also part of the ((Basic Tutorials)) series and knowledge from the previous tutorials will be assumed.

If you have been using a project created for the tutorial framework, then make sure to remove the BaseApplication files. We will not be using them for this tutorial. For instance, if you're building with CMake, then you will need to remove the lines that include those files in your CMakeLists.txt. If you're using an IDE, then you should be able to remove the files from the project just by deleting them.

{img fileId="2297" rel="box[g]"}
{maketoc}
!Getting Started
We will start from scratch with this tutorial. This code should compile successfully and exit immediately.
{CODE(caption="TutorialApplication.h" wrap="1", colors="c++")}
class TutorialApplication
{
public:
    TutorialApplication();
    virtual ~TutorialApplication();

    bool go();
};
{CODE}
{CODE(caption="TutorialApplication.cpp" wrap="1", colors="c++")}
#include "TutorialApplication.h"

#include <OgreException.h>

TutorialApplication::TutorialApplication()
{
}

TutorialApplication::~TutorialApplication()
{
}

bool TutorialApplication::go()
{
    return true;
}

// MAIN FUNCTION OMITTED
{CODE}

!The Ogre Startup Process
We are going to give a brief overview of the Ogre startup process. In the previous tutorials, we have used the ((Ogre Wiki Tutorial Framework)) to take care of a large part of this process. In this tutorial, we will build an Ogre application from scratch. This will give you greater control over how your application runs. Remember that the tutorial framework is for teaching purposes only. Your own applications should not be organized like the framework.

The basic Ogre life cycle looks like this:
# Create the Ogre::Root object
# Define the resources that Ogre will use
# Choose and set up the RenderSystem (DirectX, OpenGL, etc)
# Create the RenderWindow
# Set up any third party libraries and plugins.
# Initialise resources
# Register listener classes
# Build a scene
# Start the render loop
This tutorial will cover each of these steps in some depth. This order is not absolutely necessary. The first four steps should be done in this order, but you can experiment with the rest if it suits your application's design better. Just make sure you understand the basics of the process before you begin making any drastic changes.
!Creating the Root Object
In some ways, the Ogre::Root object will be our new BaseApplication framework. Everything that is done by the root object can be done manually. It allows us to initialize the core Ogre system with very little effort.

The first thing we'll do is add an Ogre::Root member to our class along with two Strings that will be used during setup. Add the following to the private section the TutorialApplication header:
{CODE(caption="TutorialApplication.h" wrap="1", colors="c++")}
Ogre::Root* mRoot;
Ogre::String mResourcesCfg;
Ogre::String mPluginsCfg;
{CODE}
We also need to include the header that provides Ogre::Root.
{CODE(caption="TutorialApplication.h" wrap="1", colors="c++")}
#include <OgreRoot.h>
{CODE}
After that add the initialization and cleanup to the implementation file.
{CODE(caption="TutorialApplication.cpp" wrap="1", colors="c++")}
TutorialApplication::TutorialApplication()
  : mRoot(0),
    mResourcesCfg(Ogre::StringUtil::BLANK),
    mPluginsCfg(Ogre::StringUtil::BLANK)
{
}

TutorialApplication::~TutorialApplication()
{
  delete mRoot;
}
{CODE}
Now we will construct an instance of the root object, but first we need to define the strings that identify the resource and plugin configuration files. We use the preprocessor flag {MONO()}_DEBUG{MONO} to make sure we are using the appropriate files for each build. The root object takes the plugin filename as its first parameter. Add the following to {MONO()}go{MONO}:
{CODE(wrap="1", colors="c++")}
#ifdef _DEBUG
  mResourcesCfg = "resources_d.cfg";
  mPluginsCfg = "plugins_d.cfg";
#else
  mResourcesCfg = "resources.cfg";
  mPluginsCfg = "plugins.cfg";
#endif

  mRoot = new Ogre::Root(mPluginsCfg);
{CODE}
The constructor for {MONO()}Ogre::Root{MONO} takes 3 parameters:
{FANCYTABLE()} Parameter: | Default Value:
{MONO()}Ogre::String pluginFileName{MONO} | "plugins.cfg"
{MONO()}Ogre::String configFileName{MONO} | "ogre.cfg"
{MONO()}Ogre::String logFileName{MONO} | "Ogre.log"{FANCYTABLE}
We have overriden the default value for the plugin config file so that it will switch to the debug version when built in debug mode using a precompiled Ogre SDK.

Compile and run your application. It still looks like it does nothing, but if you look in your 'bin' directory, you'll find a new 'Ogre.log' file that should have recorded a large amount of information about Ogre starting up and loading plugins. We haven't gone anywhere yet, but the motor is humming...
!Setting Up Resources
We are now going to set up our applications resources. You should open up your project's 'resources.cfg' file. If you're using the SDK, then this will be in your 'bin/release' or 'bin/debug' directory. If you built Ogre from source, then most likely this file will be in the 'bin' directory right next to your executable.

The 'resources.cfg' file lets Ogre know where it should look for potential resources. Ogre does not initialize all of these resources during this step. We will do that later. Looking at the file, we can see it is essentially a list of directories. Here is the file that is being used to write these tutorials:
{CODE(wrap="1" colors="ini")}
# Resources required by the sample browser and most samples.
[Essential]
Zip=../media/packs/SdkTrays.zip

# Resource locations to be added to the default path
[General]
FileSystem=../media
FileSystem=../media/materials/scripts
FileSystem=../media/materials/textures
FileSystem=../media/models
{CODE}
One important thing to note is that Ogre __will not__ automatically search sub-directories. From the example, you can see that including '../media' did not prevent us from having to include '../media/materials/scripts'.

For the next step, we need to include Ogre::ConfigFile.
{CODE(caption="TutorialApplication.cpp" wrap="1", colors="c++")}
#include <OgreConfigFile.h>
{CODE}
We already set up the resources string when we created the root object. Now we will create a Ogre::ConfigFile object and use it to parse our cfg file. Add the following to {MONO()}go{MONO} after we create the root object:
{CODE(wrap="1", colors="c++")}
Ogre::ConfigFile cf;
cf.load(mResourcesCfg);
{CODE}
You are free to use your own config file formats and parser. To do this, simply replace Ogre's ConfigFile parser with your own.

If you look back to your 'resources.cfg' file, you will see that they are divided into sections like [Essential]. Now that we have the information loaded from the cfg file, we have to add these sections and their list of locations to the [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_resource_group_manager.html|ResourceGroupManager].

The first thing we will do is define two strings we will use to gather information from the parsed config file.
{CODE(wrap="1" colors="c++")}
Ogre::String name, locType;
{CODE}
The {MONO()}name{MONO} parameter is the path to the resources (i.e. "../media"). The {MONO()}locType{MONO} parameter defines what kind of location this is (i.e. Filesystem, Zip, etc.)
To get started, we will ask for a SectionIterator. This will allow us to iterate through all of the sections discovered by the parser.
{CODE(wrap="1" colors="c++")}
Ogre::ConfigFile::SectionIterator secIt = cf.getSectionIterator();
{CODE}
We will now iterate through all of the results.
{CODE(wrap="1" colors="c++")}
while (secIt.hasMoreElements())
{
{CODE}
Now, inside of this loop we are going to ask for another iterator that will let us iterate through the items in each section.
{CODE(wrap="1" colors="c++")}
^ Ogre::ConfigFile::SettingsMultiMap* settings = secIt.getNext();
  Ogre::ConfigFile::SettingsMultiMap::iterator it;
{CODE}
Each section is returned as a SettingsMultiMap. This is Ogre's implementation of a [http://www.cplusplus.com/reference/map/multimap/|multimap] which contains pairs of settings. The "multi" part means it can contain multiple elements all with the same key. This is what we want here, because the key is the location type, and many of our locations will have the same type. We also set up an iterator to read through these pairs.

Now we will start another loop to scan through each item with this iterator.
{CODE(wrap="1" colors="c++")}
^ for (it = settings->begin(); it != settings->end(); ++it)
  {
{CODE}
We will now unpack each pair. The first object will be a string representing the location type of this resource, and the second argument will be the path.
{CODE(wrap="1" colors="c++")}
^   locType = it->first;
    name = it->second;
{CODE}
Finally, we will use these two strings to add this location to our ResourceGroupManager using the {MONO()}addResourceLocation{MONO} method.
{CODE(wrap="1" colors="c++")}
^   Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
      name, locType);
  }
}
{CODE}
Keep in mind that this only lets Ogre know where to look for the resources. We still have to initialize the particular resources we actually need later in the tutorial.

Compile and run your application. It still isn't doing much, but now your 'Ogre.log' should contain information about adding the resource locations.
!Configuring Our Render System
Next we need to choose the RenderSystem (usually either DirectX or OpenGL on a Windows machine) and then configure it. Most of the demo applications use the Ogre config dialog, which is a perfectly reasonable way to set up your application. Ogre also offers a way to restore the configuration that a user has already set, meaning you will never have to configure it after the first time.

First, we need to add a new class member to our BasicTutorial6 class to hold our Ogre::RenderWindow:
{CODE(wrap="1", colors="c++")}Ogre::RenderWindow* mWindow;{CODE}

Then, add the following code to {MONO()}BasicTutorial6::go{MONO}:
{CODE(wrap="1", colors="c++")}
// configure
// Show the configuration dialog and initialise the system
if(!(mRoot->restoreConfig() || mRoot->showConfigDialog()))
{
    return false;
}
{CODE}
In the first part of the if statement, we attempt to restore the config file. If that function returns false, it means that the file does not exist so we should show the config dialog, which is the second portion of the if statement. If that also returns false, it means the user canceled out of the config dialog (meaning they want to exit the program).

In this example we're merely returning false, and thus exiting the program. We could also throw an exception. If you use this technique in practice, I would recommend that if you catch an exception during Ogre's startup, you delete the ogre.cfg file in the catch block; it's possible that the settings the user has chosen in the config dialog have caused a problem and need to be changed.

While you're debugging, turning off the config dialog can help cut down on development time by a small amount, as you won't have to confirm the graphics settings every time you run the program.

Your application may also manually set up the RenderSystem if you choose to use something other than Ogre's config dialog. A basic example of this would be as follows:
{CODE(wrap="1", colors="c++")}
// Do not add this to the application
RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");
// or use "OpenGL Rendering Subsystem"
mRoot->setRenderSystem(rs);
rs->setConfigOption("Full Screen", "No");
rs->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");
{CODE}
You can use {MONO()}Root::getAvailableRenderers(){MONO} to find out which RenderSystems are available for your application to use.
Once you have retrieved a RenderSystem, you can use the {MONO()}RenderSystem::getConfigOptions{MONO} to see what options are available for the user.
By combining these two function calls, you can create your own config dialog for your application.

!Creating a RenderWindow
Now that we have chosen the RenderSystem, we need a window to render Ogre in.
There are actually a lot of options for how to do this, but we will really only cover a couple.

If you want Ogre to create a render window for you, then this is very easy to do.
Add the following code to {MONO()}BasicTutorial6::go{MONO}:
{CODE(wrap="1", colors="c++")}
mWindow = mRoot->initialise(true, "BasicTutorial6 Render Window");
{CODE}
This call initialises the RenderSystem we set in the previous section.
The first parameter is whether or not Ogre should create a RenderWindow for you.

Alternatively, you can create a render window yourself using the win32 API, wxWidgets, or one of the many other Windows or Linux GUI systems.
A quick example of how to do this under Windows would look something like this:
{CODE(wrap="1", colors="c++")}
// Do not add this to the application
mRoot->initialise(false);
HWND hWnd = 0;  // Get the hWnd of the application!
NameValuePairList misc;
misc["externalWindowHandle"] = StringConverter::toString((int)hWnd);
RenderWindow *win = mRoot->createRenderWindow("Main RenderWindow", 800, 600, false, &misc);
{CODE}
Note that you still have to call Root::initialise, but the first parameter is set to false.
Then, you must get the HWND of the window you want to render Ogre in.
How you get this will be determined entirely by the GUI toolkit you use to create the window (and under Linux I would imagine this would be a bit different as well).
After you have this, you use the NameValuePairList to assign the handle to "externalWindowHandle".
The {MONO()}Root::createRenderWindow{MONO} function can then be used to create the RenderWindow class from the window you have already created.
Consult the API documentation on this function for more information.

!Initialising Resources
Now we have our Root, RenderSystem and RenderWindow objects created and ready to go. The only thing left to do before we create our scene is initialise the resources we are about to use.

In a very large game or application, we may have hundreds or even thousands of resources for our game to use - everything from meshes to textures to scripts. However, we will probably be using only a small subset of these resources at any given time.

To keep down memory requirements, we can load only the resources that our application is using. We do this by dividing the resources into sections and only initialising them as we go. We will not be covering that in this tutorial, however. See ((Resources and ResourceManagers)) for a full tutorial devoted to resources.

Before we initialise the resources, we should also set the default number of ((Mipmapping|mipmaps)) that textures use; we must set it before we initialise the resources for it to have any effect.

Add the following code to {MONO()}BasicTutorial6::go{MONO}:
{CODE(wrap="1", colors="c++")}
// Set default mipmap level (note: some APIs ignore this)
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
// initialise all resource groups
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
{CODE}
The application now has all resource groups initialised and ready to be used.

!Creating a SceneManager
{CODE(wrap="1", colors="c++")}
#include <OgreSceneManager.h>
{CODE}
First, add a new private data member to the BasicTutorial6 class:
{CODE(wrap="1", colors="c++")}Ogre::SceneManager* mSceneMgr;{CODE}
And then add the following code to {MONO()}BasicTutorial6::go{MONO}:
{CODE(wrap="1", colors="c++")}
// Create the SceneManager, in this case a generic one
mSceneMgr = mRoot->createSceneManager("DefaultSceneManager");
{CODE}
!Creating the Camera
{CODE(wrap="1", colors="c++")}
#include <OgreCamera.h>
{CODE}
First, add a new private data member to the BasicTutorial6 class:
{CODE(wrap="1", colors="c++")}
Ogre::Camera* mCamera;{CODE}
And then add the following code to {MONO()}BasicTutorial6::go{MONO}:
{CODE(wrap="1", colors="c++")}
mCamera = mSceneMgr->createCamera("PlayerCam");

mCamera->setPosition(Ogre::Vector3(0,0,80));
mCamera->lookAt(Ogre::Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
{CODE}
!Adding a Viewport
{CODE(wrap="1", colors="c++")}
#include "OgreViewport.h"
#include "OgreRenderWindow.h"
{CODE}
Add the following code to BasicTutorial6::go:
{CODE(wrap="1", colors="c++")}
Ogre::Viewport* vp = mWindow->addViewport(mCamera);
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

mCamera->setAspectRatio(
  Ogre::Real(vp->getActualWidth()) / 
  Ogre::Real(vp->getActualHeight()));
{CODE}
You can create as many SceneManagers and Cameras as you like, but when you actually want to render something on the screen ''using'' a Camera, you have to add a Viewport for it.

!Setting Up the Scene
Let's add something to our scene, now that we've taken care of the basics.

Find the BasicTutorial6::go function and add the following code:
{CODE(wrap="1", colors="c++")}
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
headNode->attachObject(ogreHead);

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

Ogre::Light* l = mSceneMgr->createLight("MainLight");
l->setPosition(20,80,50);
{CODE}
We need to include OgreEntity for the code to compile error-free, so add this line to the top of '''BasicTutorial6.cpp''':
{CODE(wrap="1", colors="c++")}
#include <OgreEntity.h>
{CODE}

!An Initial Rendering Loop
In order to admire our work so far, we need to render our scene.

Ogre::Root features several methods to kick off a render loop, the most simple of which is {MONO()}Ogre::Root::renderOneFrame(){MONO}.
That basically loops until a function in the loop returns false.

Here's what it looks like when it's called in {MONO()}Root::startRendering(){MONO}:
{CODE(wrap="1", colors="c++")}
void Root::startRendering(void)
{
    assert(mActiveRenderer != 0);

    mActiveRenderer->_initRenderTargets();

    // Clear event times
    clearEventTimes();

    // Infinite loop, until broken out of by frame listeners
    // or break out by calling queueEndRendering()
    mQueuedEnd = false;

    while( !mQueuedEnd )
    {
        //Pump messages in all registered RenderWindow windows
        WindowEventUtilities::messagePump();

        if (!renderOneFrame())
            break;
    }
}
{CODE}
We will not go into the gritty details of that function, only point out the {MONO()}renderOneFrame(){MONO} function.

We briefly looked at this function in ((Basic Tutorial 4)), but let's cover it again:
{CODE(wrap="1", colors="c++")}
bool Root::renderOneFrame(void)
{
    if(!_fireFrameStarted())
        return false;
 
    if(!_updateAllRenderTargets())
        return false;
 
    return _fireFrameEnded();
}
{CODE}
Besides updating all render targets, it calls the frame event functions, and if either of those returns false, it will cause the render loop to exit.

Since we're not using any FrameListeners just yet, we need an alternative render loop.

Add this line to the list of includes at the top of our BasicTutorial6.cpp file:
{CODE(wrap="1", colors="c++")}#include <OgreWindowEventUtilities.h>{CODE}
Put this in the {MONO()}BasicTutorial6::go{MONO} function:
{CODE(wrap="1", colors="c++")}
while(true)
{
    // Pump window messages for nice behaviour
    Ogre::WindowEventUtilities::messagePump();
 
    if(mWindow->isClosed())
    {
        return false;
    }
 
    // Render a frame
    if(!mRoot->renderOneFrame()) return false;
}
{CODE}
It loops over and over, until the window is closed, or the {MONO()}renderOneFrame(){MONO} function returns false.

Compile and run your application. :)
You should see a window with an Ogre head in it.
You need to click the close button to exit the application, because we haven't implemented any input functionality yet.
!The Object-Oriented Input System (OIS)
Though not the only option for input in Ogre, OIS is one of the best.

We will briefly cover how to start up OIS in your application. For the actual uses of the library, you should check the various tutorials on this site (which use it extensively) and the OIS documentation itself.

Put the following includes in {MONO()}BasicTutorial6.h{MONO}:
{CODE(wrap="1", colors="c++")}
#include <OISEvents.h>
#include <OISInputManager.h>
#include <OISKeyboard.h>
#include <OISMouse.h>
{CODE}
Add the following data members to the BasicTutorial6 class:
{CODE(wrap="1", colors="c++")}
// OIS Input devices
OIS::InputManager* mInputManager;
OIS::Mouse*    mMouse;
OIS::Keyboard* mKeyboard;
{CODE}
Then we need to add OIS to our project settings:
|| Include Directory | $(OGRE_HOME)/include/OIS
Input Library | OIS_d.lib/OIS.lib ||
The library directory should already be set up.

Make sure you can compile the project after adding OIS to it.

!Initialising OIS
OIS uses a general InputManager which is a touch difficult to set up, but easy to use once you have created it properly. OIS does not integrate into Ogre; it's a standalone library, which means that you will need to provide it with some information at the beginning for it to work properly. In practice it really only needs the window handle in which Ogre is rendering. Thankfully, since we have used the automatically created window, Ogre makes this easy for us.

Go to the BasicTutorial6::go function and add this code to it, before our render loop:
{CODE(wrap="1", colors="c++")}
Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
OIS::ParamList pl;
size_t windowHnd = 0;
std::ostringstream windowHndStr;

mWindow->getCustomAttribute("WINDOW", &windowHnd);
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

mInputManager = OIS::InputManager::createInputSystem( pl );
{CODE}
This sets up the InputManager for use, but to actually use OIS to get input for the Keyboard, Mouse, or Joystick of your choice, you'll need to create those objects:
{CODE(wrap="1", colors="c++")}
mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, false ));
mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, false ));
{CODE}
We are passing '''false''' to the createInputObject function because we want mouse and keyboard unbuffered.
%note% __Note__: If you wish to use buffered input (meaning you get event callbacks to mouseMoved, mousePressed, keyReleased, and so on), then you ''must'' set the second parameter to createInputObject to be true.

!Shutting down OIS
__Note__: This is very important when building on linux.

OIS is a bit tricky to shut down properly.
The most failsafe way to do this is by using a {MONO()}WindowEventListener{MONO}.

First, put this line in your list of includes in {MONO()}BasicTutorial6.h{MONO}:
{CODE(wrap="1", colors="c++")}
#include <OgreWindowEventUtilities.h>
{CODE}
Then we need to change our class declaration to derive from {MONO()}WindowEventListener{MONO}:
{CODE(wrap="1", colors="c++")}
class BasicTutorial6 : public Ogre::WindowEventListener
{CODE}
We want to override {MONO()}WindowEventListener::windowResized{MONO} and {MONO()}WindowEventListener::windowClosed{MONO}, so put this in the protected section of the BasicTutorial6 class declaration:
{CODE(wrap="1", colors="c++")}
// Ogre::WindowEventListener
virtual void windowResized(Ogre::RenderWindow* rw);
virtual void windowClosed(Ogre::RenderWindow* rw);
{CODE}
Now, open BasicTutorial6.cpp and add the following to it:
{CODE(wrap="1", colors="c++")}
//Adjust mouse clipping area
void BasicTutorial6::windowResized(Ogre::RenderWindow* rw)
{
    unsigned int width, height, depth;
    int left, top;
    rw->getMetrics(width, height, depth, left, top);
 
    const OIS::MouseState &ms = mMouse->getMouseState();
    ms.width = width;
    ms.height = height;
}
 
//Unattach OIS before window shutdown (very important under Linux)
void BasicTutorial6::windowClosed(Ogre::RenderWindow* rw)
{
    //Only close for window that created OIS (the main window in these demos)
    if(rw == mWindow)
    {
        if(mInputManager)
        {
            mInputManager->destroyInputObject( mMouse );
            mInputManager->destroyInputObject( mKeyboard );
 
            OIS::InputManager::destroyInputSystem(mInputManager);
            mInputManager = 0;
        }
    }
}
{CODE}
{MONO()}windowResized{MONO} is called whenever the window is resized, and makes sure that the OIS mouse state is synchronised with the actual size of the window.
{MONO()}windowClosed{MONO} destroys OIS when the window is closed.

To make our application act as a WindowEventListener, we need to register it as one.
So add these lines to {MONO()}BasicTutorial6::go{MONO}:
{CODE(wrap="1", colors="c++")}
//Set initial mouse clipping size
windowResized(mWindow);
 
//Register as a Window listener
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
{CODE}

There's one more thing we need to do.
Find the BasicTutorial6 destructor and make it look like this:
{CODE(wrap="1", colors="c++")}
//Remove ourself as a Window listener
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
windowClosed(mWindow);
delete mRoot;
{CODE}

!Setting Up the Framelistener
No matter if you are using buffered or unbuffered input, every frame you must call the {MONO()}capture{MONO} method on all Keyboard, Mouse, and Joystick objects you use.

For unbuffered input, this is all you need to do.
Every frame you can call the various Keyboard and Mouse functions to query for the state of these objects.

So, we need to make our BasicTutorial6 class become a FrameListener. :)

Change the class declaration to look like this:
{CODE(wrap="1", colors="c++")}
class BasicTutorial6 : public Ogre::WindowEventListener, public Ogre::FrameListener
{CODE}
Then add this function declaration to the protected section of the class declaration:
{CODE(wrap="1", colors="c++")}
// Ogre::FrameListener
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
{CODE}
And then, in BasicTutorial6.cpp, add this function definition:
{CODE(wrap="1", colors="c++")}
bool BasicTutorial6::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    if(mWindow->isClosed())
        return false;
 
    //Need to capture/update each device
    mKeyboard->capture();
    mMouse->capture();

    if(mKeyboard->isKeyDown(OIS::KC_ESCAPE))
        return false;

    return true;
}
{CODE}

!Registering Our FrameListener
Before we're ready to compile and run our application, we need to register our application as a FrameListener.
We don't need a custom render loop either. Find BasicTutorial6::go and remove the while loop. Then add this to it:
{CODE(wrap="1", colors="c++")}
mRoot->addFrameListener(this);
{CODE}
This line adds the BasicTutorial6 instance to mRoot as a FrameListener, meaning that it will receive frame events.
If we don't register our class as a FrameListener with the Root object, the {MONO()}frameRenderingQueued(){MONO} function will never be called.
!Replacing Our Custom Render Loop
This line of code starts the rendering loop. We don't really need any special handling of the loop since we can perform our per-frame tasks in the {MONO()}frameRenderingQueued(){MONO} function.
{CODE(wrap="1", colors="c++")}
mRoot->startRendering();
{CODE}
Compile and run! :D
You should see the familiar Ogre head in an Ogre RenderWindow, and you should be able to exit the application by pressing the Escape key.

!Notes About Mac OS X
!!Cocoa Version
The new way to set up the current wiki tutorial framework for xcode in Mac OS X is much shorter and easy to understand.
Make sure your project is created as a cocoa application and changed the extension to .mm from .cpp and copy both resources and plugins cfg to your copy resource phase in your xcode target
Now change the following from
{CODE(wrap="1", colors="c++")}
#ifdef _DEBUG
    mResourcesCfg = "resources_d.cfg";
    mPluginsCfg = "plugins_d.cfg";
#else
    mResourcesCfg = "resources.cfg";
    mPluginsCfg = "plugins.cfg";
#endif
{CODE}
to
{CODE(wrap="1", colors="c++")}
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
	std::string mResourcePath = [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
#endif
#ifdef _DEBUG
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
    mResourcesCfg = mResourcePath + "/resources_d.cfg";
    mPluginsCfg = mResourcePath + "/plugins_d.cfg";
#else
    mResourcesCfg = "resources_d.cfg";
    mPluginsCfg = "plugins_d.cfg";
#endif
#else
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
    mResourcesCfg = mResourcePath + "/resources.cfg";
    mPluginsCfg = mResourcePath + "/plugins.cfg";
#else
    mResourcesCfg = "resources.cfg";
    mPluginsCfg = "plugins.cfg";
#endif
{CODE}
!!Carbon Version
Since Mac OS X uses app bundles, a concept radically different from what is used on Windows and Linux, the code described above is going to crash on Mac OS X.
* Add the following function:
{CODE(wrap="1", colors="c++")} #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
 #include <CoreFoundation/CoreFoundation.h>
 
 // This function will locate the path to our application on OS X,
 // unlike windows you cannot rely on the current working directory
 // for locating your configuration files and resources.
 std::string macBundlePath()
 {
     char path[1024];
     CFBundleRef mainBundle = CFBundleGetMainBundle();
     assert(mainBundle);
 	
     CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
     assert(mainBundleURL);
 	
     CFStringRef cfStringRef = CFURLCopyFileSystemPath( mainBundleURL, kCFURLPOSIXPathStyle);
     assert(cfStringRef);
 	
     CFStringGetCString(cfStringRef, path, 1024, kCFStringEncodingASCII);
 	
     CFRelease(mainBundleURL);
     CFRelease(cfStringRef);
 	
     return std::string(path);
 }
 #endif{CODE}
* In createRoot(), change
{CODE(wrap="1", colors="c++")} mRoot = new Root();{CODE}
to
{CODE(wrap="1", colors="c++")} #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
     mRoot = new Root(macBundlePath() + "/Contents/Resources/plugins.cfg");
 #else
     mRoot = new Root();
 #endif{CODE}
* In defineResources(), change
{CODE(wrap="1", colors="c++")} cf.load("resources.cfg");{CODE}
to
{CODE(wrap="1", colors="c++")} #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
     cf.load(macBundlePath() + "/Contents/Resources/resources.cfg");	
 #else
     cf.load("resources.cfg");		
 #endif{CODE}
* Also in defineResources(), change
{CODE(wrap="1", colors="c++")} Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName);{CODE}
to
{CODE(wrap="1", colors="c++")} #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
     Ogre::ResourceGroupManager::getSingleton().addResourceLocation( String(macBundlePath() + "/" + archName), typeName, secName);
 #else
     Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName);
 #endif{CODE}

!Conclusion
Now you should have a basic understanding of how to create your own simple Ogre application.

!Full Source
The full source for this tutorial is ((BasicTutorial6SourceCurrent|here)).
!Next
((Basic Tutorial 7))
---
Alias: (alias(Basic_Tutorial_6))

        

History

Information Version
Sun 19 of Apr, 2020 12:06 GMT-0000 paroj 137
Wed 15 of Apr, 2015 22:30 GMT-0000 kabbotta 136
Wed 15 of Apr, 2015 21:40 GMT-0000 kabbotta 135
Wed 15 of Apr, 2015 21:39 GMT-0000 kabbotta 134
Thu 09 of Apr, 2015 06:37 GMT-0000 kabbotta 133
Thu 09 of Apr, 2015 06:32 GMT-0000 kabbotta 132
Thu 09 of Apr, 2015 06:26 GMT-0000 kabbotta 131
Thu 09 of Apr, 2015 06:24 GMT-0000 kabbotta 130
Thu 09 of Apr, 2015 01:19 GMT-0000 kabbotta 129
Thu 09 of Apr, 2015 01:16 GMT-0000 kabbotta 128
Thu 09 of Apr, 2015 01:14 GMT-0000 kabbotta 127
Thu 09 of Apr, 2015 01:12 GMT-0000 kabbotta 126
Thu 09 of Apr, 2015 01:12 GMT-0000 kabbotta 125
Thu 09 of Apr, 2015 01:10 GMT-0000 kabbotta 124
Thu 09 of Apr, 2015 01:07 GMT-0000 kabbotta 123
Thu 09 of Apr, 2015 00:57 GMT-0000 kabbotta 122
Thu 09 of Apr, 2015 00:43 GMT-0000 kabbotta 121
Thu 09 of Apr, 2015 00:42 GMT-0000 kabbotta 120
Thu 09 of Apr, 2015 00:40 GMT-0000 kabbotta 119
Thu 09 of Apr, 2015 00:40 GMT-0000 kabbotta 118
Thu 09 of Apr, 2015 00:40 GMT-0000 kabbotta 117
Thu 09 of Apr, 2015 00:19 GMT-0000 kabbotta 116
Wed 08 of Apr, 2015 04:05 GMT-0000 kabbotta 115
Wed 08 of Apr, 2015 04:04 GMT-0000 kabbotta 114
Wed 08 of Apr, 2015 04:00 GMT-0000 kabbotta 113