Disclaimer: This is not an official NxOgre tutorial. However, it has been revised and edited by the creator of NxOgre and BloodyMess, betajaen.
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.
Table of contents
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.
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.
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.
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
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);
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.
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!
Official Wiki and Website - Forum - Ogre Portal for NxOgre
Betajaen's Guide to creating a Bloody mess
Code snippetsbloody mess
General - Particles - Mesh Cooking - Cloths
Spacegaier's Tutorialsbloody mess
Setting up BloodyMess - First steps - Visual Debugger - Volumes & Triggers - Kinematics - Raycasting - Resources and Meshes