Starting Ogre 3D

Exclusive offer: get 50% off this eBook here
OGRE 3D 1.7 Beginner's Guide

OGRE 3D 1.7 Beginner's Guide — Save 50%

Create real time 3D applications using OGRE 3D from scratch

$26.99    $13.50
by Felix Kerger | November 2010 | Beginner's Guides Open Source

In this article, by Felix Kerger, author of Ogre 3D 1.7, we will cover:

  • Adding resources
  • Using resources.cfg
  • Structure of a configuration file
  • Creating an application class
  • Adding a FrameListener
  • Investigating the FrameListener functionality

 

OGRE 3D 1.7 Beginner's Guide

OGRE 3D 1.7 Beginner's Guide

Create real time 3D applications using OGRE 3D from scratch

  • Easy-to-follow introduction to OGRE 3D
  • Create exciting 3D applications using OGRE 3D
  • Create your own scenes and monsters, play with the lights and shadows, and learn to use plugins
  • Get challenged to be creative and make fun and addictive games on your own
  • A hands-on do-it-yourself approach with over 100 examples

Images

        Read more about this book      

(For more resources on this subject, see here.)

Introduction

Up until now, the ExampleApplication class has started and initialized Ogre 3D for us; now we are going to do it ourselves.

Time for action – starting Ogre 3D

This time we are working on a blank sheet.

  1. Start with an empty code file, include Ogre3d.h, and create an empty main function:

    #include "Ogre\Ogre.h"
    int main (void)
    {
    return 0;
    }

  2. Create an instance of the Ogre 3D Root class; this class needs the name of the "plugin.cfg":
  3. "plugin.cfg":
    Ogre::Root* root = new Ogre::Root("plugins_d.cfg");

  4. If the config dialog can't be shown or the user cancels it, close the application:

    if(!root->showConfigDialog())
    {
    return -1;
    }

  5. Create a render window:

    Ogre::RenderWindow* window = root->initialise(true,"Ogre3D
    Beginners Guide");

  6. Next create a new scene manager:

    Ogre::SceneManager* sceneManager = root-
    >createSceneManager(Ogre::ST_GENERIC);

  7. Create a camera and name it camera:

    Ogre::Camera* camera = sceneManager->createCamera("Camera");
    camera->setPosition(Ogre::Vector3(0,0,50));
    camera->lookAt(Ogre::Vector3(0,0,0));
    camera->setNearClipDistance(5);

  8. With this camera, create a viewport and set the background color to black:

    Ogre::Viewport* viewport = window->addViewport(camera);
    viewport->setBackgroundColour(Ogre::ColourValue(0.0,0.0,0.0));

  9. Now, use this viewport to set the aspect ratio of the camera:

    camera->setAspectRatio(Ogre::Real(viewport->getActualWidth())/
    Ogre::Real(viewport->getActualHeight()));

  10. Finally, tell the root to start rendering:

    root->startRendering();

  11. Compile and run the application; you should see the normal config dialog and then a black window. This window can't be closed by pressing Escape because we haven't added key handling yet. You can close the application by pressing CTRL+C in the console the application has been started from.

What just happened?

We created our first Ogre 3D application without the help of the ExampleApplication. Because we aren't using the ExampleApplication any longer, we had to include Ogre3D.h, which was previously included by ExampleApplication.h. Before we can do anything with Ogre 3D, we need a root instance. The root class is a class that manages the higher levels of Ogre 3D, creates and saves the factories used for creating other objects, loads and unloads the needed plugins, and a lot more. We gave the root instance one parameter: the name of the file that defines which plugins to load. The following is the complete signature of the constructor:

Root(const String & pluginFileName = "plugins.cfg",const String &
configFileName = "ogre.cfg",const String & logFileName = "Ogre.log")

Besides the name for the plugin configuration file, the function also needs the name of the Ogre configuration and the log file. We needed to change the first file name because we are using the debug version of our application and therefore want to load the debug plugins. The default value is plugins.cfg, which is true for the release folder of the Ogre 3D SDK, but our application is running in the debug folder where the filename is plugins_d.cfg.

ogre.cfg contains the settings for starting the Ogre application that we selected in the config dialog. This saves the user from making the same changes every time he/she start our application. With this file Ogre 3D can remember his choices and use them as defaults for the next start. This file is created if it didn't exist, so we don't append an _d to the filename and can use the default; the same is true for the log file.

Using the root instance, we let Ogre 3D show the config dialog to the user in step 3. When the user cancels the dialog or anything goes wrong, we return -1 and with this the application closes. Otherwise, we created a new render window and a new scene manager in step 4. Using the scene manager, we created a camera, and with the camera we created the viewport; then, using the viewport, we calculated the aspect ratio for the camera. After creating all requirements, we tell the root instance to start rendering, so our result would be visible. Following is a diagram showing which object was needed to create the other:

Adding resources

We have now created our first Ogre 3D application, which doesn't need the ExampleApplication. But one important thing is missing: we haven't loaded and rendered a model yet.

Time for action – loading the Sinbad mesh

We have our application, now let's add a model.

  1. After setting the aspect ratio and before starting the rendering, add the zip archive containing the Sinbad model to our resources:

    Ogre::ResourceGroupManager::getSingleton().
    addResourceLocation("../../Media/packs/Sinbad.zip","Zip");

  2. We don't want to index more resources at the moment, so index all added resources now:

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

  3. Now create an instance of the Sinbad mesh and add it to the scene:

    Ogre::Entity* ent = sceneManager->createEntity("Sinbad.mesh");
    sceneManager->getRootSceneNode()->attachObject(ent);

  4. Compile and run the application; you should see Sinbad in the middle of the screen:

What just happened?

We used the ResourceGroupManager to index the zip archive containing the Sinbad mesh and texture files, and after this was done, we told it to load the data with the createEntity() call in step 3.

Using resources.cfg

Adding a new line of code for each zip archive or folder we want to load is a tedious task and we should try to avoid it. The ExampleApplication used a configuration file called resources.cfg in which each folder or zip archive was listed, and all the content was loaded using this file. Let's replicate this behavior.

Time for action – using resources.cfg to load our models

Using our previous application, we are now going to parse the resources.cfg.

  1. Replace the loading of the zip archive with an instance of a config file pointing at the resources_d.cfg:

    the resources_d.cfg:
    Ogre::ConfigFile cf;
    cf.load(«resources_d.cfg»);

  2. First get the iterator, which goes over each section of the config file:

    Ogre::ConfigFile::SectionIterator sectionIter =
    cf.getSectionIterator();

  3. Define three strings to save the data we are going to extract from the config file and iterate over each section:

    Ogre::String sectionName, typeName, dataname;
    while (sectionIter.hasMoreElements())
    {

  4. Get the name of the section:

    sectionName = sectionIter.peekNextKey();

  5. Get the settings contained in the section and, at the same time, advance the section iterator; also create an iterator for the settings itself:

    Ogre::ConfigFile::SettingsMultiMap *settings = sectionIter.
    getNext();
    Ogre::ConfigFile::SettingsMultiMap::iterator i;

  6. Iterate over each setting in the section:

    for (i = settings->begin(); i != settings->end(); ++i)
    {

  7. Use the iterator to get the name and the type of the resources:

    typeName = i->first;
    dataname = i->second;

  8. Use the resource name, type, and section name to add it to the resource index:

    Ogre::ResourceGroupManager::getSingleton().
    addResourceLocation(dataname, typeName, sectionName);

  9. Compile and run the application, and you should see the same scene as before.
OGRE 3D 1.7 Beginner's Guide Create real time 3D applications using OGRE 3D from scratch
Published: November 2010
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on this subject, see here.)

What just happened?

In the first step, we used another helper class of Ogre 3D, called ConfigFile. This class is used to easily load and parse simple configuration files, which consist of name-value pairs. By using an instance of the ConfigFile class, we loaded the resources_d.cfg. We hardcoded the filename with the debug postfix; this isn't good practice and in a production application we would use #ifdef to change the filename depending on the debug or release mode. ExampleApplication does this; let's take a look at ExampleApplication.h line 384:

#if OGRE_DEBUG_MODE
cf.load(mResourcePath + "resources_d.cfg");
#else
cf.load(mResourcePath + "resources.cfg");
#endif

Structure of a configuration file

The configuration file loaded by the helper class follows a simple structure; here is an example from resource.cfg. Of course your resource.cfg will consist of different paths:

[General]
FileSystem=D:/programming/ogre/ogre_trunk_1_7/Samples/Media

[General] starts a section, which goes on until another [sectionname] occurs in the file. Each configuration file can contain a lot of sections; in step 2 we created an iterator to iterate over all the sections in the file and in step 3 we used a while loop, which runs until we have processed each section.

A section consists of several settings and each setting assigns a key a value. We assign the key FileSystem the value D:/programming/ogre/ogre_trunk_1_7/Samples/Media. In step 4, we created an iterator so we can iterate over each setting. The settings are internally called name-value pairs. We iterate over this map and for each entry we use the map key as the type of the resource and the data we use as the path. Using the section name as resource group, we added the resource using the resource group manager in step 8. Once we had parsed the complete file, we indexed all the files.

Creating an application class

We now have the basis for our own Ogre 3D application, but all the code is in the main function, which isn't really desirable for reusing the code.

Time for action – creating a class

Using the previously applied code we are now going to create a class to separete the Ogre code from the main function.

  1. Create the class MyApplication, which has two private pointers, one to a Ogre 3D SceneManager and the other to the Root class:

    class MyApplication
    {
    private:
    Ogre::SceneManager* _sceneManager;
    Ogre::Root* _root;

  2. The rest of this class should be public:

    public:

  3. Create a loadResources() function, which loads the resources.cfg configuration file:

    void loadResources()
    {
    Ogre::ConfigFile cf;
    cf.load(«resources_d.cfg»);

  4. Iterate over the sections of the configuration file:

    Ogre::ConfigFile::SectionIterator sectionIter =
    cf.getSectionIterator();
    Ogre::String sectionName, typeName, dataname;
    while (sectionIter.hasMoreElements())
    {

  5. Get the section name and the iterator for the settings:

    sectionName = sectionIter.peekNextKey();
    Ogre::ConfigFile::SettingsMultiMap *settings = sectionIter.
    getNext();
    Ogre::ConfigFile::SettingsMultiMap::iterator i;

  6. Iterate over the settings and add each resource:

    for (i = settings->begin(); i != settings->end(); ++i)
    {
    typeName = i->first;
    dataname = i->second;
    Ogre::ResourceGroupManager::getSingleton().
    addResourceLocation(
    dataname, typeName, sectionName);
    }
    }
    Ogre::ResourceGroupManager::getSingleton().
    initialiseAllResourceGroups();
    }

  7. Also create a startup() function, which creates an Ogre 3D root class instance using the plugins.cfg:

    int startup()
    {
    _root = new Ogre::Root(«plugins_d.cfg»);

  8. Show the config dialog and when the user quits it, return -1 to close the application:
    if(!_root->showConfigDialog())
    {
    return -1;
    }

  9. Create the RenderWindow and the SceneManager:

    Ogre::RenderWindow* window = _root->initialise(true,"Ogre3D
    Beginners Guide");
    _sceneManager = root->createSceneManager(Ogre::ST_GENERIC);

  10. Create a camera and a viewport:

    Ogre::Camera* camera = _sceneManager->createCamera("Camera");
    camera->setPosition(Ogre::Vector3(0,0,50));
    camera->lookAt(Ogre::Vector3(0,0,0));
    camera->setNearClipDistance(5);

    Ogre::Viewport* viewport = window->addViewport(camera);
    viewport->setBackgroundColour(Ogre::ColourValue(0.0,0.0,0.0));
    camera->setAspectRatio(Ogre::Real(viewport->getActualWidth())/
    Ogre::Real(viewport->getActualHeight()));

  11. Call the function to load our resources and then a function to create a scene; after that, Ogre 3D starts rendering:

    loadResources();
    createScene();
    _root->startRendering();
    return 0;

  12. Then create the createScene() function, which contains the code for creating the SceneNode and the Entity:

    SceneNode and the Entity:
    void createScene()
    {
    Ogre::Entity* ent = _sceneManager->createEntity(«Sinbad.mesh»);
    _sceneManager->getRootSceneNode()->attachObject(ent);
    }

  13. We need the constructor to set both the pointers to NULL so we can delete it even if it hasn't been assigned a value:

    MyApplication()
    {
    _sceneManager = NULL;
    _root = NULL;
    }

  14. We need to delete the root instance when our application instance is destroyed, so implement a destructor which does this:

    ~MyApplication()
    {
    delete _root;
    }

  15. The only thing left to do is to adjust the main function:

    int main (void)
    {
    MyApplication app;
    app.startup();
    return 0;
    }

  16. Compile and run the application; the scene should be unchanged.

What just happened?

We refactored our starting codebase so that different functionalities are better organized. We also added a destructor so our created instances would be deleted when our application is closed. One problem is that our destructor won't be called; because startup() never returns, there is no way to close our application. We need to add a FrameListener to tell Ogre 3D to stop rendering.

Adding a FrameListener

We have already used the ExampleFrameListener; this time we are going to use our own implementation of the interface.

Time for action – adding a FrameListener

Using the code from before we are going to add our own FrameListener implementation

  1. Create a new class called MyFrameListener exposing three publicly visible event handler functions:

    class MyFrameListener : public Ogre::FrameListener
    {
    public:

  2. First, implement the frameStarted function, which for now returns false to close the application:

    bool frameStarted(const Ogre::FrameEvent& evt)
    {
    return false;
    }

  3. We also need a frameEnded function, which also returns false:

    bool frameEnded(const Ogre::FrameEvent& evt)
    {
    return false;
    }

  4. The last function we implement is the frameRenderingQueued function, which also returns false:

    bool frameRenderingQueued(const Ogre::FrameEvent& evt)
    {
    return false;
    }

  5. The main class needs a point to store the FrameListener:

    MyFrameListener* _listener;

  6. Remember that the constructor needs to set the initial value of the listener to NULL:

    _listener = NULL;

  7. Let the destructor delete the instance:

    delete _listener;

  8. At last, create a new instance of the FrameListener and add it to the root object; this should happen in the startup() function:

    _listener = new MyFrameListener();
    _root->addFrameListener(_listener);

  9. Compile and run the application; it should be closed directly.

What just happened?

We created our own FrameListener class, which didn't rely on the ExampleFrameListener implementation. This time we inherited directly from the FrameListener interface. This interface consists of three virtual functions, which we implemented. We already knew the frameStarted function, but the other two are new. All three functions return false, which is an indicator to Ogre 3D to stop rendering and close the application. Using our implementation, we added a FrameListener to the root instance and started the application; not surprisingly, it closed directly.

OGRE 3D 1.7 Beginner's Guide Create real time 3D applications using OGRE 3D from scratch
Published: November 2010
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on this subject, see here.)

Investigating the FrameListener functionality

Our FrameListener implementation has three functions; each is called at a different point in time. We are going to investigate in which sequence they are called.

Time for action – experimenting with the FrameListener implementation

Using the console printing we are going to inspect when the FrameListener is called.

  1. First let each function print a message to the console when it is called:

    bool frameStarted(const Ogre::FrameEvent& evt)
    {
    std::cout << «Frame started» << std::endl;
    return false;
    }
    bool frameEnded(const Ogre::FrameEvent& evt)
    {
    std::cout << «Frame ended» << std::endl;
    return false;
    }
    bool frameRenderingQueued(const Ogre::FrameEvent& evt)
    {
    std::cout << «Frame queued» << std::endl;
    return false;
    }

  2. Compile and run the application; in the console you should find the first string—Frame started.

What just happened?

We added a "debug" output to each of the FrameListener functions to see which function is getting called. Running the application, we noticed that only the first debug message is printed. The reason is that the frameStarted function returns false, which is a signal for the root instance to close the application.

Now that we know what happens when frameStarted() returns false, let's see what happens when frameStarted() returns true.

Time for action – returning true in the frameStarted function

Now we are going to modify the behavior of our FrameListener to see how this changed its behavior.

  1. Change frameStarted to return true:

    bool frameStarted(const Ogre::FrameEvent& evt)
    {
    std::cout << «Frame started» << std::endl;
    return true;
    }

  2. Compile and run the application. Before the application closes directly, you will see a short glimpse of the rendered scene and there should be the two following lines in the output:
    Frame started
    Frame queued

What just happened?

Now, the frameStarted function returns true and this lets Ogre 3D continue to render until false is returned by the frameRenderingQueued function. We see a scene this time because directly after the frameRenderingQueued function is called, the render buffers are swapped before the application gets the possibility to close itself.

Summary

In this article we have covered:

  • Adding resources
  • Using resources.cfg
  • Structure of a configuration file
  • Creating an application class
  • Adding a FrameListener
  • Investigating the FrameListener functionality

Further resources on this subject:


About the Author :


Felix Kerger

Felix Kerger is a Computer Science Student at the Technical University of Darmstadt and has been developing 3D real-time applications using OGRE 3D for more than 5 years. He has given several talks on software development and 3D real-time applications at different conferences and has been working for three years as an assistant researcher at the Fraunhofer Institute for Computer Graphics Research. He also works as a freelance journalist and reports yearly from the Game Developer Conference Europe.

Books From Packt


Blender 3D 2.49 Architecture, Buildings, and Scenery
Blender 3D 2.49 Architecture, Buildings, and Scenery

Unity 3D Game Development by Example Beginner's Guide
Unity 3D Game Development by Example Beginner's Guide

3D Graphics with XNA Game Studio 4.0
3D Graphics with XNA Game Studio 4.0

Papervision3D Essentials
Papervision3D Essentials

3D Game Development with Microsoft Silverlight 3: Beginner's Guide
3D Game Development with Microsoft Silverlight 3: Beginner's Guide

Blender 3D 2.49 Incredible Machines
Blender 3D 2.49 Incredible Machines

Irrlicht 1.7.1 Realtime 3D Engine Beginner's Guide: RAW
Irrlicht 1.7.1 Realtime 3D Engine Beginner's Guide: RAW

Away3D 3.6 Essentials
Away3D 3.6 Essentials


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
S
M
B
3
8
x
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software