BloodyMess Tutorial 2        
NxOgre   This page is related to a deprecated version of NxOgre, called BloodyMess. Some code from here may be adopted to newer versions with sufficient care and knowledge.   NxOgre


Disclaimer: This is not an official NxOgre tutorial. However, it has been revised and edited by the creator of NxOgre and BloodyMess, betajaen.

help Help: For any problems you encounter while working on these tutorials, as well as for detailed questions and propositions, please visit the NxOgre OgreAddons-Forum.

Image You can find the complete code for this tutorial here.

NxOgreCube.png Units Warning

NxOgre uses the metre-kilogram-second system, so for those of you who are most familiar with the imperial units system please take a moment to familiarize yourself with the International System of Units. Briefly put: NxOgre measures distance in metres, mass in kilograms, and time in seconds.

Newtons are a measurement of force. 1 Newton is the force required to accelerate one kilogram at a rate of one meter per second per second (1 KG·m/s2).

NxOgre makes use of the standard Cartesian coordinate system. So when I refer to the coordinates (10, 12, -8.7), I mean 10.0 metres in the X direction, 12.0 metres in the Y direction and -8.7 metres in the Z direction.

NxOgreCube.png Introduction

In the first tutorial, you have learned where to get BloodyMess, how to build NxOgre, and how to set up your IDE. Therefore, you now have everything you need to start simulating physics in your Ogre applications.

At the end of this tutorial something like the following should be achieved:

Peacefully Falling
Peacefully Falling

Midair Collision
Midair Collision

Rolling on the Floor
Rolling on the Floor

NxOgreCube.png The Initial Code

Start a new Ogre and NxOgre project in your -IDE. We will create the necessary code to make Ogre run using the ExampleApplication and ExampleFrameListener classes provided by the Ogre Samples.

Note: If you're fairly new with Ogre, it may be worth simply modifying the Ogre SkyBox sample instead of creating your own Ogre project.


The following code will serve as the basis for this tutorial. It should compile and run without errors.

#include "ExampleApplication.h"

class BloodyMessTutorial2Listener : public ExampleFrameListener
{ 
public:
    BloodyMessTutorial2Listener(RenderWindow *win, Camera *cam) 
        : ExampleFrameListener(win, cam)
    {
    }

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

protected:
};

class BloodyMessTutorial2 : public ExampleApplication
{
protected:
    
    void createScene()
    {
        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.5f, 0.5f, 0.5f));

        // Create a light
        Light* l = mSceneMgr->createLight("MainLight");
        l->setPosition(20, 80, 50);

        // Position the camera
        mCamera->setPosition(0, 20, 80);
        mCamera->lookAt(0, 20, 0);
    }

    // Create a new frame listener
    void createFrameListener()
    {
        mFrameListener = new BloodyMessTutorial2Listener(mWindow, mCamera);
        mRoot->addFrameListener(mFrameListener);
    }
};

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
    int main(int argc, char **argv)
#endif
    {
        // Create application object
        BloodyMessTutorial2 app;

        try {
            app.go();
        } catch(Exception& e) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBoxA(NULL, e.getFullDescription().c_str(),
                "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occurred: " << e.getFullDescription();
#endif
        }

        return 0;
    }

#ifdef __cplusplus
}
#endif

NxOgreCube.png Architecture of a BloodyMess World

The top-most BloodyMess class is the World, of which there can only be one instance. The size of this world is, for all intents and purposes, infinite, however truthfully it is only very, very large. Inside this world exist up to 32 Scenes. Objects in one scene cannot interact with objects in another scene.

In each scene are a large number of Actors, the number of which is only limited by your hardware. These Actors represent a physical object within the World, but they are completely invisible to us. In order to see them, you need to use a Body, which is simply an Actor that renders to your screen using a Render System. BloodyMess comes with several Rendering Systems built in including OpenGL and Ogre, however this does not restrict you from building your own.

Another important element of NxOgre is the TimeController. It controls the passage of time for the Actors within your World, from a frozen state, to that of real time, to a comically fast state.

NxOgreCube.png Our World, Scene, and Render System

First of all, you need to include NxOgre.h and NxOgreOGRE3D.h in order to use BloodyMess and its Ogre specific rendering classes, so place these at the top of your application:

#include <NxOgre.h>
#include <NxOgreOGRE3D.h>


Next, in our BloodyMessTutorial2 class, we declare our world, scene, and render system variables in the protected section of our class:

NxOgre::World* mWorld;
NxOgre::Scene* mScene;
OGRE3DRenderSystem* mRenderSystem;


You should notice that the render system is not part of the NxOgre namespace as it does not belong the the core BloodyMess code because the render systems are interchangeable. In our case, the OGRE3DRenderSystem comes from the NxOgreOGRE3D header.

Within our createScene() function we can then initialize the world:

mWorld = NxOgre::World::createWorld();


Before we initialise this scene, we will create as SceneDescription in our createScene() function, that we will then pass on to the scene initialization function:

NxOgre::SceneDescription sceneDesc;
sceneDesc.mGravity = NxOgre::Vec3(0, -9.8f, 0);
sceneDesc.mName = "BloodyMessTutorial2";

mScene = mWorld->createScene(sceneDesc);


With this code we have created a scene which uses the Earth's gravity and named the scene. Naming scenes are not important in BloodyMess, and it can indeed function quite well without them, but it is simply a good habit to name them.

With the next three lines, we will also specify some default physical values that are applied to the whole scene:

mScene->getMaterial(0)->setStaticFriction(0.5);
mScene->getMaterial(0)->setDynamicFriction(0.5);
mScene->getMaterial(0)->setRestitution(0.1);


These control the default frictional values and the bounce (restitution) of the scene. In this case the values you've just set are for a moderate amount of friction with very little bounce.

Now there is only one thing left before we have our scene set up: Declaring the renderer which is, in our case, Ogre.

mRenderSystem = new OGRE3DRenderSystem(mScene);


We are now ready to add objects to our scene.

NxOgreCube.png Adding a Cube

Since our scene is quite empty right now we will now populate it with a cube.

Note: This project makes use of the BloodyCake (an NxOgre example framework) mesh "cube.1m.mesh" which can be found in the archive here. For more information see this forum post.


We will create a body with a cube shape. First we need to create another variable, this time of the type OGRE3DBody, which we will initialize in our createScene() function.

OGRE3DBody* mCube;


To actually create this body we have to call the createBody()' function of the render system and pass some parameters:

  • the shape of our actor, which is in this case a box of the size 1x1x1 metre
  • the position of our new body, which is 20 metres above the center of the scene
  • the mesh we want to use to visualize our body

mCube = mRenderSystem->createBody(new NxOgre::Box(1, 1, 1), NxOgre::Vec3(0, 20, 0), "cube.1m.mesh");


If you compile the application now you will see the cube standing somewhere in the scene without moving at all, although we told our scene to have a gravity force. Why is this? We have not told our scene to "move". Or more precisely: we did not advance our TimeController.

NxOgreCube.png Getting Some Motion

To get some motion in the scene we will use our FrameListener to advance our TimeController every frame. Therefore, we have to alter its constructor a bit to get the TimeController, which the listener class has to store as a member. In every call of the frameStarted() function, the TimeController will now be advanced by the delta time (the time since the last frame).

Delete, or modify, the listener class and replace it with the following:

class BloodyMessTutorial2Listener : public ExampleFrameListener
{ 
public:
    BloodyMessTutorial2Listener(RenderWindow *win, Camera *cam) 
        : ExampleFrameListener(win, cam)
    {
        mTimeController = NxOgre::TimeController::getSingleton();
    }

    bool frameStarted(const FrameEvent& evt)
    {
        mTimeController->advance(evt.timeSinceLastFrame);
        return ExampleFrameListener::frameStarted(evt);
    }

protected:
    NxOgre::TimeController* mTimeController;
};


Compiling and running the application now, you will see NxOgre simulating physics within your application for the first time in the form of a box falling through your scene.

NxOgreCube.png Physical Interactions

What we did so far with NxOgre could also be easily achieved within Ogre by just moving the entity downward. To fully simulate a physical environment we will now have our box interact with other objects in the scene (what Ogre entities can't) by adding a few more things to our scene.

We will start with adding another cube. But as we don't want both of them just falling straight down, we will add a force to the second so that they collide in midair.

Declare a new OGRE3DBody:

OGRE3DBody* mCubeTwo;


And add the following to the bottom of the createScene() function:

mCubeTwo = mRenderSystem->createBody(new NxOgre::Box(1, 1, 1), NxOgre::Vec3(20, 35, 0), "cube.1m.mesh");
mCubeTwo->addForce(NxOgre::Vec3(-800, -200, 0));


The first line should look familiar to you, but the second needs a little explanation. The addForce() is a function which will will add a force to the box of 800 Newtons in the -X direction, 200 Newtons in the -Y direction and 0 newtons in the Z direction. In other words it will fly off at an angle and hopefully collide with the other box in our scene.

The last step is to create a floor plane, which is an actor, of infinite size that can not be moved:

mScene->createSceneGeometry(new NxOgre::PlaneGeometry(0, NxOgre::Vec3(0, 1, 0)), Matrix44_Identity);

Note: If you are using NxOgre BloodyMess version 1.5.4 then you will need to include the NxOgre namespace to the Matrix44_Identify call.


Now, we have an invisible floor plane on which our two cubes will land on. You can enhance the example by adding an Ogre::Plane to visualize the floor, but this has nothing to do with NxOgre and is only making use of simple Ogre calls:

MovablePlane *plane = new MovablePlane("Plane");
plane->d = 0;
plane->normal = Vector3::UNIT_Y;
Ogre::MeshManager::getSingleton().createPlane("PlaneMesh", 
    ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
    *plane, 120, 120, 1, 1, true, 1, 3, 3, Vector3::UNIT_Z);
Entity *planeEnt = mSceneMgr->createEntity("PlaneEntity", "PlaneMesh");
planeEnt->setMaterialName("Examples/GrassFloor");

Ogre::SceneNode* mPlaneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mPlaneNode->attachObject(planeEnt);
mPlaneNode->scale(100, 100, 100);


Compile and run your application and the two boxes should collide in mid air before finally landing on your floor plane to rest peacefully.

NxOgreCube.png Conclusion

That's it! You have now learned the very basics of BloodyMess. Now, continue on to the next tutorial where we will explore the very helpful Visual Debugger.