BloodyMess Tutorial 4        
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 Introduction

In the third tutorial, you learned about the Visual Debugger, a tool that can visually show all physics objects in a scene, even those that are invisible. In this tutorial we will get to know one kind of these invisible element: Volumes/Trigger. Once you have completed this tutorial it would be a good idea to go back and integrate the visual debugger so that you can see this object within our scene.

NxOgreCube.png What is a Volume?

A volume is an invisible physics object of an arbitrary shape. It doesn't move, nor can it be seen. So what is the value of it? It is used as a trigger. As soon as a physics object enters it, is fully within it, or leaves it, a special event is fired that can be used to start a defined action within your program.

One example for a trigger could be the finish area in a racing game. From the finish line up to a distance beyond it, a trigger volume is placed and as soon as the player enters this volume, an event is fired and the game can, for example, react by showing a message, playing a sound or doing whatever the programmer wanted it to do.

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 it 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"

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

class BloodyMessTutorial4Listener : public ExampleFrameListener
{ 
public:
    BloodyMessTutorial4Listener(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;
};

class BloodyMessTutorial4 : public ExampleApplication
{
protected:
    NxOgre::World*            mWorld;
    NxOgre::Scene*            mScene;
    NxOgre::TimeController*        mTimeController;
    OGRE3DRenderSystem*        mRenderSystem;

    OGRE3DBody*            mCube;
    
    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 the world
        mWorld = NxOgre::World::createWorld();

        // Create scene description
        NxOgre::SceneDescription sceneDesc;
        sceneDesc.mGravity = NxOgre::Vec3(0, -9.81f, 0);
        sceneDesc.mName = "DemoScene";

        // Create scene
        mScene = mWorld->createScene(sceneDesc);

        // Set some physical scene values
        mScene->getMaterial(0)->setStaticFriction(0.5);
        mScene->getMaterial(0)->setDynamicFriction(0.5);
        mScene->getMaterial(0)->setRestitution(0.1);

        // Create render system
        mRenderSystem = new OGRE3DRenderSystem(mScene);

        //Create time controller
        mTimeController = NxOgre::TimeController::getSingleton();

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

        // Create floor plane (Ogre)
        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);
    }

    // Create a new frame listener
    void createFrameListener()
    {
        mFrameListener = new BloodyMessTutorial4Listener(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
        BloodyMessTutorial4 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 Initialize the Volume

First of all, we need to make two changes to our tutorial class. We have to declare a new member variable:

NxOgre::Volume* mVolume;


We also have to make our tutorial class inherit NxOgre::Callback in order for it to be able to deal with the events that a volume will fire when, in this case, an NxOgre::Actor enters.

As such, ensure that our class declaration looks like this:

class BloodyMessTutorial4 : public ExampleApplication, public NxOgre::Callback


Next, at the bottom of our createScene() function, we need to create a volume that has these parameters:

  • the shape of the volume, in this case a 5x5x5 metre box
  • the position of the volume, which is the center of our scene
  • the callback class for the volume, which is this class which we previously had inherit from NxOgre::Callback
  • the callback behavior (for which events the onVolumeEvent() should be called), which will react to an Actor entering, existing within, or exiting the volume

mVolume = mScene->createVolume(new NxOgre::Box(5), NxOgre::Matrix44(NxOgre::Vec3(0, 0, 0)),
                                     this, NxOgre::Enums::VolumeCollisionType_All);

NxOgreCube.png The Callback Function

The final thing we have to do is create the function which will be called when an Actor triggers our callback. Place this function at the bottom of our tutorial class:

void onVolumeEvent(NxOgre::Volume* volume, NxOgre::Shape* volumeShape, NxOgre::RigidBody* rigidBody,
                            NxOgre::Shape* rigidBodyShape, unsigned int collisionEvent)
{
    if(collisionEvent == NxOgre::Enums::VolumeCollisionType_OnEnter)
    {
        NxOgre::Actor* actor = static_cast<NxOgre::Actor*>(rigidBody);
        float y =   (9.81 * actor->getMass())                  // counteract gravity
            + (-actor->getLinearVelocity().y * actor->getMass())      // counteract vertical velocity
            + (10 - actor->getGlobalPosition().y * actor->getMass()); // add some force to move it to the top

        actor->addForce(NxOgre::Vec3(0, y, 0), NxOgre::Enums::ForceMode_Impulse); 
    }
}


This bit of code checks what type of collisionEvent occurred, which in this case we only want when an actor enters our volume. We then apply a force to our actor which will send it flying back up vertically along the Y axis.

NxOgreCube.png Conclusion

That's it! When you run the application now, you should see our cube falling straight down till it reaches our volume where it will then reverse direction and go flying back in to the air until gravity begins to pull it back down once more, so on and so fourth. Onward, for in the next tutorial we will cover Kinematics!