Tutorial 5         Adding something exciting

Adding something exciting

Wow, Tutorial 5, already, are we having fun yet? :-D
From this point on we will be building upon
the TutorialApplication we created in the previous steps.

Skybox

In our last Tutorial we had a black background, let’s make it more interesting by adding a skybox. Remember the TutorialApplication::createScene() method we had to add? Well this method has been setup to be called once at the start of the application. It is in here, that we add most of the code for setting up what we want to see on the screen. Let us add our first bit of fancy code:

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


Your probably thinking, whoa, what’s this? Where did mSceneMgr come from, what is Examples/SpaceSkyBox? Well, do not panic. mSceneMgr is created by the class we inherited TutorialApplication from. If you look in the ExampleApplication.h file you will see the following code, this is what created the mSceneMgr for us:

virtual void chooseSceneManager(void)
{
    // Get the SceneManager, in this case a generic one
    mSceneMgr = mRoot->getSceneManager(ST_GENERIC);
}


Later in a different tutorial we will cover choosing different scenemanagers, but for now rest assured that we have a scenemanager. As for the Examples/SpaceSkyBox, this refers to a material definition. These definitions are found in .material files in the media directory. Materials will be covered in a different tutorial. So let’s just go with SpaceSkyBox at the moment. Now compile the code and run it. You should see something close to the following:

xorekis5_1.png

Hey cool, you have a skybox. Did you notice that you could spin around using the mouse? Again, this is default code that is supplied by ExampleApplication.

A ship

As cool as this is, what now? I want to do some more. How about adding a user controlled ship? So how do we go about doing this? Well one of the first things to understand is how Ogre works with scenemanagers. Mesh based objects are not part of the scenegraph, they are in fact objects attached to SceneNodes in the scenegraph. SceneNodes are what gives the entities their position, rotation, scale etc in the scene. For us to create our ship we need to create an entity and a SceneNode to attach the entity to. Open up TutorialApplication.h again, as we need to add some member variables:

class TutorialApplication : public ExampleApplication
{
public:
    TutorialApplication();
    virtual ~TutorialApplication();
protected:
    void createScene();
    // ADD THE FOLLOWING TWO LINES TO YOUR CODE
    Entity* mShip;
    SceneNode* mShipNode;
};


mShipNode will be the scenegraph node we will attach our ship entity mShip to. Now open up the TutorialApplication.cpp, we need to add code to the createScene method:

void TutorialApplication::createScene()
{
// Create the SkyBox
mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");
// Create our ship entity
mShip = mSceneMgr->createEntity("razor", "razor.mesh");


The first entry is still our skybox. The second entry is a new one that creates our ship. Ogre comes with a plane/ship model that is called razor. For this tutorial we will be using the Ogre supplied model. Later I may do another tutorial for adding in our own models from milkshape. createEntry requires two parameters, the first being a general but unique name we want to call our entity, the second parameter is the name of the mesh that is to be displayed. The mesh must be in our media resource directory. Add the following two lines below the createEntity line.

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


When the SceneManager is created, a root scene node is created. This root node is used for the rendering the scene. So to have something render, we need to create a child SceneNode from the RootSceneNode. Once we have our node, we then attach our ship entity to it.

Space is dark

Let’s compile and run, we should see a ship in our screen now.

xorekis5_2.png

If yours like mine, then you’re wondering why the ship is so dark. The razor material has been setup to reflect to lights. Since no lights have been added to the scene it is using the ambient light and the ambient light is set very low. For now, we will just adjust the ambient light. Add the following line below the attachObject line in our createScene() method:

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


Ogre Colour elements are in the range of 0.f to 1.0f. Some other 3D engines use 0 to 255.

  • Black = ColourValue( 0.f, 0.f, 0.f )
  • White = ColourValue( 1.f, 1.f, 1.f )
  • Red = ColourValue( 1.f, 0.f, 0.f )


Recompile and rerun the TutorialApplication.

xorekis5_3.png

Yup, the ship is brighter, now using the mouse and the keyboard, you can move around the ship. As you can see, if it fairly simple to add entities to the scene graph. Try adding a two more ships one either side of the original one. Hint: SceneNode has a function called setPosition();

Who is flying my ship?

Well I did promise a user controlled ship, so this tutorial is not over yet. Ogre uses a built in render loop. It is possible to create and manage your own render loop, but that is out of scope on these initial tutorials. Anyway, we need a way of plugging in code into
this render loop. Ogre provides us a way by using what are called FrameListeners. These FrameListeners are called at the beginning of the render loop and at the end of the render loop. That way, we can plug in code, that needs to be executed before rendering and after rendering. In this case we will only be interested in plugging in code before the render starts. For our example we need to create our own version of a FrameListener. Open up the TutorialApplication.cpp file. We will be adding code after the include but before the TutorialApplication constructor:

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


Why did we inherit from ExampleFrameListener? Well ExampleFrameListener has some need stuff already added, so it just made sense for now. Here we have a custom constructor, RenderWindow and Camera are needed by the FrameListener. But SceneNode is not, we pass it because we want to move our ship around. If we did not pass the node, we would have no idea where it was, unless we created it globally. The last line of the above class definition is an important one. FrameListerner has two methods that get called. FrameStarted is called before rendering frameEnded is called after rendering. FrameEvent is a simple structure that will hold the delta time between frames and between events. Considering we want to add code before the render starts, we add the frameStarted method override. Let’s add some body to the frame started. Add the following code after the class definition:

bool TutorialFrameListener::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( KC_ESCAPE))
    {
        return false;
    }
    return true;
}


Remember I said we could insert code into the render loop? One of the things you can do is react to user input. This is the reason we are adding code. We want to be able to control the ship. Ogre uses arbitrary units for its positions/rotations/scale. So moving one unit could mean one Millimeter or one Kilometer or one Parsec. It all depends on your scale.

In our tutorial we will assume the ship can move 80 units per second. But how do we move 80 units when we have a frame rate display of say 100? Well that is where evt.timeSinceLastFrame comes in. This is the time between frames as a percent of a second. So for us to figure out how far we move the ship in this frame, we multiply 80 by evt.timeSinceLastFrame giving us the MoveFactor as in the code. OK cool, we have the move distance, but now we need user input. Luckily ExampleApplication comes to our rescue again. Ogre uses a PlatformPlugin that gets automatically linked. In windows, the plugin uses DirectInput to query the Keyboard and Mouse only. I personally have expanded my plugin to work with my gamecontroller (Joystick). ExampleApplication provides us with an instance of this plugin though the variable mInputDevice.

For us to get information from the DirectInput we first need to capture the input. This is done by calling mInputDevice->capture(); Now the input device has captured the state of the keyboard and mouse. As we are only interested in the keyboard, we can query the input device to see if certain keys are pressed. This is done by the command mInputDevice->IsKeyDown(Ogre::KC_UP). Ogre has a number of enumerated keys, KC_UP is just one of them.

Now we have the Move Rate, and we know a certain key is down, we have all we need to move our ship. Remember how I said that SceneNode gives an entity position and rotation? Now we need to apply the move rate to the scenenode. This is done via SceneNodes::translate method. This method excepts three parameters. X, Y and Z. I should explain that Ogre uses a right handed coordinate system with Y being vertical and Z being depth. So to move our ship up and down on the screen we need to enter the move rate as Y. The code above will move our ship up and down and left and right on the screen.

Hey, don’t compile yet. We have to hook the TutorialFrameListener in to our TutorialApplication yet. While we are still in the TutorialApplication.cpp file, go to the bottom of the file and add the following code.

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


ExampleApplication has a virtual method called createFrameListener. We override this method to stop it from creating the default frameListener and instead create our new TutorialFrameListener. Once created we actually have to add the framelistener to mRoot. What is mRoot? Ogre has a base class that helps to link and manage the different components of Ogre. This is called Root. mRoot is an instance of Root. A different tutorial will go into Root more. We are almost done. Now open TutorialApplication.h. Add the following line before createScene in your class:
void createFrameListener();
Your class def should look like the following now

class TutorialApplication : public ExampleApplication
{
public:
    TutorialApplication();
    virtual ~TutorialApplication();
protected:
    void createScene();
    void createFrameListener();
    Entity* mShip;
    SceneNode* mShipNode;
};


By overriding the frameStarted method the way we did, we could no longer press escape to exit the application. This code allows us to continue doing this. FrameStarted returns a Boolean, this bool tells the Ogre render loop whether to breakout of the loop or to continue. If True is returned then the render loop will keep going. Compile and run your app. You can now move your ship up/down and left/right.

Congratulations you have completed your second Ogre Application.

Xorekis

Move on to Tutorial 6