Humble Beginnings         This tutorial is a genuine Ogre classic! Has been around since at least 2004 and was for a long time *the* first tutorial

Introduction

This tutorial is designed to start at the very basics of using Ogre. It will begin with setting up a project and by the end of it we will have a space ship that can be moved around in a 3D scene.

This tutorial was originally written by AgentGreen

Creating the Project Files

In these tutorials I will assume that the projects being created are located in their own folder in ogrenew\Samples. I will use APPNAME as a place holder for where you type in the name of the application. So in the example of where the project files should be stored it would be OgreNew\Samples\APPNAME.

NB: In a moments time we will embark on creating a small application called "Space", based on some of the examples supplied with Ogre. I suggest that if you plan on doing that tutorial that you use Space as the APPNAME.

Visual C++ 7 (also part of Visual Studio .NET 2003)

First, create a blank Win32 Project (in the New Project options box, it's 'Visual C++ Projects' / 'Win32' / 'Win32 Project'). After entering the project name, hit OK, and when the next window pops up, go to the 'Application Settings' tab, leave the 'Windows application' radiobutton highlighted and tick the 'Empty Project' checkbox, then hit Finish.

Now add a new file to the project's Source Files - APPNAME.cpp. A .cpp file must already be present for the IDE to display the 'C/C++' properties menu for the next step, and this file will soon contain our code. Now right click on the project name in the solution explorer, click on Properties, and change the following options in the Property Pages:

Debugging       / Working Directory              = "..\Common\Bin\Debug"
<br /> C/C++  / General         / Additional Include Directories = "..\Common\Include";"..\..\OgreMain\include"
<br /> C/C++  / Code Generation / Runtime Library                = Multithreaded Debug DLL
<br /> C/C++  / Preprocessor    / Preprocessor Definitions       = _WINDOWS,_STLP_USE_DYNAMIC_LIB,
<br />                                                                OGRE_LIBRARY_IMPORTS,_DEBUG,WIN32,_STLP_DEBUG
<br /> Linker / General         / Output File                    = ..\Common\Bin\Debug\APPNAME.EXE
<br /> Linker / General         / Additional Library Directories = "..\..\OgreMain\Lib\Debug"
<br /> Linker / Input           / Additional Dependencies        = OgreMain_d.lib


If you subsequently wish to compile your code in Release Mode: 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, where you have put 'Multithreaded Debug DLL', change 'Debug' to 'Release'; in C/C++'s 'Preprocessor' section, remove the preprocessor definitions '_DEBUG' and '_STLP_DEBUG'; in Linker's 'Input' section, change OgreMain_d.lib to OgreMain.lib.

Now copy Samples\Skyplane\Src\SkyPlane.CPP and Samples\Skyplane\Include\SkyPlane.H into the directory with your project. Right click on the project in the solution explorer and select "Add Existing Item". Highlight the two files you just copied to the directory, and add them to the project. Now rebuild your solution, and run it in debug mode. Everything should work.

Now remove those two files from your project and delete them from your project's directory.

To finish setting up the project, we need to create the main program loop. This is simpler than it sounds, because if we are following the Ogre Example Framework then we only need to create an instance of our Application object and call its go() method. In Visual C++ the code, in our APPNAME.cpp file, will look like this:

/* APPNAME.CPP */
 
 #include "Ogre.h"
 #include "APPNAMEApplication.h"
 
 #define WIN32_LEAN_AND_MEAN
 #include "windows.h"
 
 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
 {
    // Create application object
    APPNAMEApplication app;
 
    try {
        app.go();
    } catch( Ogre::Exception& e ) {
        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL );
    }
 
    return 0;
 }


This doesn't compile, does it? No, because we have not written APPNAMEApplication.h yet. That will be discussed in the next section.

Visual C++ 6

This is almost identical to the instructions for Visual C++ 7.

The first thing you need to do is create a blank Win32 Project. Make sure that you create a C++ project and that you have ticked the "Empty Project" tick box.
Now select "Project" from the menu and then select "Settings". We'll change some properties from here (legend is T=Tab, C="Category"):

(T) Debug - (C) General - Working Directory                   = ..\Common\Bin\Debug
<br /> (T) C/C++ - (C) Preprocessor - Preprocessor Definitions       = _WINDOWS,_STLP_USE_DYNAMIC_LIB,OGRE_LIBRARY_IMPORTS,_DEBUG,WIN32,_STLP_DEBUG
<br /> (T) C/C++ - (C) Preprocessor - Additional Include Directories = ..\Common\Include,..\..\OgreMain\include
<br /> (T) Link - (C) General - Output Filename                      = ..\Common\Bin\Debug\APPNAME.EXE
<br /> (T) Link - (C) Input - Additional Library Path                = ..\..\OgreMain\Lib\Debug
<br /> (T) Link - (C) General - Object/Library Modules               = OgreMain_d.lib (add to whatever is there)


NB: Check that your application is set to use the 'Multithreaded Debug Dll' runtime library (under the code generation options in the C++ section).

In release mode you should change 'Debug' to 'Release', and remove the preprocessor defines '_DEBUG' and '_STLP_DEBUG', and chaneg OgreMain_d.lib to OgreMain.lib.

Set up the source files exactly as in the VC7 section.

Linux

To begin, make the Samples/Space directory for your project and copy these files into it:

  • Samples/SkyPlane/src/SkyPlane.cpp
  • Samples/SkyPlane/include/SkyPlane.h
  • Samples/Common/include/ExampleApplication.h
  • Samples/Common/include/ExampleFrameListener.h


Next we will set up a basic build system, for which we are going to use GNU automake and autoconf. If you want to learn more about these tools, I suggest reading the excellent tutorial at the autotools website. First we will write Samples/Space/configure.ac, which tells autoconf which programs and libraries we are going to use to compile our application:

AC_INIT([Ogre Space Tutorial],
         [0.0.1],
         [Mark Ivey zovirl@zovirl.com],
         [Space])
 AM_CONFIG_HEADER(config.h)
 AM_INIT_AUTOMAKE([dist-bzip2])
 
 AC_PROG_CC
 AC_PROG_CXX
 AC_PROG_LIBTOOL
 
 PKG_CHECK_MODULES(OGRE, OGRE >= 1.0.0,,AC_MSG_ERROR("OGRE not found!"))
 AM_CXXFLAGS="$AM_CXXFLAGS $OGRE_CFLAGS"
 AM_LDFLAGS="$AM_LDFLAGS $OGRE_LIBS"
 
 AC_SUBST(AM_CXXFLAGS, "$AM_CXXFLAGS")
 AC_SUBST(AM_LDFLAGS, "$AM_LDFLAGS")
 AC_SUBST(PKGDATADIR, "${datadir}/${PACKAGE}")
 
 AC_CONFIG_FILES([
    Makefile
 ])
 
 AC_OUTPUT


Most of that is boiler-plate. The two important parts are "PKG_CHECK_MODULES(OGRE...)" which tells autoconf we will be using OGRE (and it should go find the correct compiler flags for us) library and "AC_CONFIG_FILES(...)" which tells autoconf which files we want it to generate.

Next we will write Samples/Space/Makefile.am, which tells automake how to compile our program:

bin_PROGRAMS = Space
 
 noinst_HEADERs= ExampleApplication.h                 ExampleFrameListener.h                 SkyPlane.h
 
 Space_SOURCES= SkyPlane.cpp

 EXTRA_DIST = bootstrap configure


Ok, the last part of the build system is a short script to set everything up for us, Samples/Space/bootstrap:

#!/bin/sh
 
 set -x
 rm -f config.cache &&
 libtoolize --force &&
 aclocal &&
 autoconf &&
 autoheader &&
 automake --foreign --add-missing


Make that executable with "chmod +x bootstrap"

You are ready to compile (from the Samples/Space/ directory):

./bootstrap
 ./configure
 make


Finally, you should be able to go into the data directory (Samples/Common/bin) and run ../../Space/Space.

Ok, the build system is done, now it is time to add the real source code. Remove SkyPlane.cpp and SkyPlane.h and edit Makefile.am to reflect the new files you are about to add:

bin_PROGRAMS = Space
  
 noinst_HEADERs= ExampleApplication.h                  ExampleFrameListener.h                  SpaceApplication.h
  
 Space_SOURCES= SpaceApplication.cpp
 
 EXTRA_DIST = bootstrap configure


Finally, add the code for the application's main loop in SpaceApplication.cpp:

#include "Ogre.h"
 #include "SpaceApplication.h"
 
 int main(int argc, char *argv[])
 {
    // Create application object
    SpaceApplication  app;
    try {
       app.go();
    } catch( Ogre::Exception& e ) {
 
    std::cerr << "An exception has occured: " <<
        e.getFullDescription().c_str() << std::endl;
    }
 
    return 0;
 }

Setting up the scene

A scene in outer space

When we derive our own application class from the ExampleApplication class we overrride the protected method createScene whose prototype is:

virtual void createScene(void) = 0;    // pure virtual - this has to be overridden


As you can see this is a purely virtual function, so if we do not override it then we will get a nasty error. This method is responsible for setting up the scene within the application. This involves creating entities, lights, particle systems, etc.

It's time to get into the code, so if you have not done so already create an application following the previous steps and call it Space. To begin with we are going to create a scene from outer space, with planets visible in the distance based on the Skybox example.

Create a new header file called SpaceApplication.h. This is the file that we included in Space.cpp and is where we are going to define our own class SpaceApplication. The first thing we need to do is

#include "ExampleApplication.h"


This gives us access to the ExampleApplication class that our application class is derived from. Now to create our class definition

class SpaceApplication : public ExampleApplication
 {
 protected:
     void createScene(void);
 };


Now to override that createScene method with our own. There is only one thing we are interested in doing right now, create a skybox that shows us a scene in outer space. We do this by using the Scene Manager (mSceneMgr).

void SpaceApplication::createScene(void)
    {
       // Create a skybox
        mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");
 
    }


Compile and run the program. After the Ogre configuration box is gone you should be thrust into our space scene. The mouse will let you look left, right, up, and down. Time for a quick look at "Examples/SpaceSkyBox".

Resources

When we set the sky box we told the Scene Manager to use the material "Examples/SpaceSkyBox". But where does this come from? When an ExampleApplication class is created it tells the Resource Manager to add the following places to search for resources

../../../Media/dragon.zip
        ../../../Media/knot.zip
        ../../../Media/skybox.zip
        ../../../Media/


The Resource Manager searches those paths for a files ending in ".material" for definitions of materials. This material, Examples/SpaceSkyBox, happens to be defined in Example.material

// Skybox
 material Examples/SpaceSkyBox
 {
   technique
   {
     pass
     {
       // No dynamic lighting, fully lit
       lighting off
       // Depth writing off (always display stuff in front of it)
       depth_write off
 
       // Texture layer 0
       texture_unit
       {
         // 6-sided texture, stevecube_fr.jpg, stevecube_up.jpg etc
         cubic_texture stevecube.jpg separateUV
         // clamp to avoid fuzziness at edges due to filtering
         tex_address_mode clamp
       }
     }
   }
 }

Creating our first Scene Node and Entity

A ship in space

What use is outer space if there are no space ships in it? Its time to make something that we can fly around in. We are going to do this by creating instances of two object types. Entities and Scene Nodes.

Member variables

In SpaceApplication.h, in the class interface definition, create some private member variables to store pointers to the Scene Node and Entity for use later.

Entity* mShip;
 SceneNode* mShipNode;

Entities

An Entity in Ogre is a moveable object based on a mesh. These meshes are loaded through the Resource Manager and its position is determined by a Scene Node that it is attached to. To create our ship, add a line into the SpaceApplication::createScene method in SpaceApplication.h (after setting the sky box)

mShip = mSceneMgr->createEntity("razor", "razor.mesh");


Just like the skybox, we supply a resource name to the Scene Manager and it will locate it. In this case it is the name of a mesh file (razor.mesh) which is found in the Media directory. Another important thing to note about we just did, we gave the entity a name. This name (razor) has to be unique within the application or the Scene Manager will give you an error.

The Scene Node

As mentioned above, Entities attach to Scene Nodes which dictate their position in the scene. Scene Nodes can be attached to other tree nodes to create a type of positional hierachy, but for the purposes of this tutorial just one will do.

To create this Scene Node we ask the root node of the scene to create it for us. The root node is maintained by Ogre and every entity that is part of the scene is directly or indirectly attached to it. When we ask the root node to create the node for us we are saying that the root node is the node's parent. After creating it we set its position orientation relative to the scene node (effectively the absolute position in the scene as far as this tutorial goes, but not necessarily true in other circumstances).

mShipNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();


To have the ship stay still you must add:

mShipNode->setPosition(0,0,0);


Otherwise when you press the keys(WASD) to move it will move the ship and not you.

Almost there... attach the ship Entity to the Scene Node.

mShipNode->attachObject(mShip);

Lighting

Lastly, we have to turn on some lights or you won't be able to see anything. Lets just use some ambient lighting supplied by the Scene Manager for now. Place this line at the start of createScene.

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


Compile and run. Have a walk around the ship.

Creating our first frame listener

The Frame Listener Class

A Frame Listener in Ogre is notified by the system at the start of every frame. The main method of interest to us is the frameStarted method. For the Frame Listener that we are creating we will give it a pointer to the Scene Node that the ship entity is attached to. This will allow us to move that Scene Node and therefore the ship as well.

class SpaceFrameListener : public ExampleFrameListener
 {
 protected:
     SceneNode* mShipNode;
 
 public:
     SpaceFrameListener(RenderWindow* win, Camera* cam, SceneNode* shipNode) : ExampleFrameListener(win, cam)
     {
         mShipNode = shipNode;
     };
 
     bool frameStarted(const FrameEvent& evt);
 };


In this example we want to move the ship on the screen based on keypresses from the user. For now we will simply get the ship to respond to the arrow keys and move accordingly (up, down, left, right).

The first thing we do in this method is determine how much the ship can move in this frame. To do this we simply multiply how many units per second we want the ship to move with the value of evt.timeSinceLastFrame (measured in fractions of a second). We will also want to know which keys, if any, are being pressed. To do this we call mInputDevice->capture() which takes a copy of the state of the input devices which we then query by using mInputDevice->isKeyDown().

Once we have determined what, if any, movement is to take place we use the translate method of the Scene Node.

bool frameStarted(const FrameEvent& evt)
 {
    Real MoveFactor = 80.0 * evt.timeSinceLastFrame;
 
    mInputDevice->capture();
 
    if(mInputDevice->isKeyDown(Ogre::KC_UP))
        mShipNode->translate(0.0, MoveFactor, 0.0);
 
    if(mInputDevice->isKeyDown(Ogre::KC_DOWN))
        mShipNode->translate(0.0, -MoveFactor, 0.0);
 
    if(mInputDevice->isKeyDown(Ogre::KC_LEFT))
        mShipNode->translate(-MoveFactor, 0.0, 0.0);
 
    if(mInputDevice->isKeyDown(Ogre::KC_RIGHT))
        mShipNode->translate(MoveFactor, 0.0, 0.0);        
 
    if(mInputDevice->isKeyDown(Ogre::KC_ESCAPE))
        return false;
 
    return true;
 }

Connecting the Frame Listener Class

All that remains now is to arrange for this Frame Listener class to be created and used by the application. The easy way to do this is to override the createFrameListener() method of the SpaceApplication class. The inherited version of createFrameListener creates an instance of ExampleFrameListener which implements the motion that we have seen in the examples that come with Ogre. By overriding this method and supplying our own class we will not get that behaviour any more, but we can then instead dictate our own.

void createFrameListener(void)
    {
        mFrameListener= new SpaceFrameListener(mWindow, mCamera, mShipNode);
        mRoot->addFrameListener(mFrameListener);
    }


Compile and run the program. The camera will remain in a fixed position but the ship will move up, down, left, and right as you press the arrow keys. It wasn't very hard was it? Here ends this tutorial, the next one we will have a bit more fun with moving and cameras.