Skip to main content
History: BloodyMess Tutorial 2
View published page
Source of version: 12
(current)
{INCLUDE(page="BloodyMess tpl")}{INCLUDE} __Disclaimer:__ This is not an official NxOgre tutorial. However, it has been revised and edited by the creator of NxOgre and BloodyMess, ((User:betajaen|betajaen)). %help%~hs~__Help:__ For any problems you encounter while working on these tutorials, as well as for detailed questions and propositions, please visit the [http://www.ogre3d.org/addonforums/viewforum.php?f=6|NxOgre OgreAddons-Forum]. {maketoc} {DIV(class="Bloody_box1")}{img src="img/wiki_up/CPP_small.png" alt="" width="22"}You can find the complete code for this tutorial ((BloodyMess Tutorial 2 Source|here)).{DIV} !!{img src="img/wiki_up/NxOgreCube.png" alt="NxOgreCube.png"} Units Warning NxOgre uses the [http://en.wikipedia.org/wiki/SI|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. [http://en.wikipedia.org/wiki/Newton|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/s{SUP()}2{SUP}). NxOgre makes use of the standard [http://en.wikipedia.org/wiki/Cartesian_coordinate_system|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. !!{img src="img/wiki_up/NxOgreCube.png" alt="NxOgreCube.png"} Introduction In the ((BloodyMess Tutorial 1|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: {SPLIT(width="50%", align="center")} {IMG(src="img/wiki_up/Bloody_mess_tut1_pic01.jpg", thumb="y", button="y", rel="box[g]", stylebox=border, width="150", title="Peacefully Falling", desc="Peacefully Falling")}{IMG} --- {IMG(src="img/wiki_up/Bloody_mess_tut1_pic02.jpg", thumb="y", button="y", rel="box[g]", stylebox=border, width="150", title="Midair Collision", desc="Midair Collision")}{IMG} --- {IMG(src="img/wiki_up/Bloody_mess_tut1_pic03.jpg", thumb="y", button="y", rel="box[g]", stylebox=border, width="150", title="Rolling on the Floor", desc="Rolling on the Floor")}{IMG} {SPLIT} !!{img src="img/wiki_up/NxOgreCube.png" alt="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. {DIV(class="Bloody_box2")}__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.{DIV} The following code will serve as the basis for this tutorial. It should compile and run without errors. {CODE(wrap="1", colors="c++")}#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{CODE} !!{img src="img/wiki_up/NxOgreCube.png" alt="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. !!{img src="img/wiki_up/NxOgreCube.png" alt="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: {CODE(wrap="1", colors="c++")}#include <NxOgre.h> #include <NxOgreOGRE3D.h>{CODE} Next, in our BloodyMessTutorial2 class, we declare our world, scene, and render system variables in the protected section of our class: {CODE(wrap="1", colors="c++")}NxOgre::World* mWorld; NxOgre::Scene* mScene; OGRE3DRenderSystem* mRenderSystem;{CODE} 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: {CODE(wrap="1", colors="c++")}mWorld = NxOgre::World::createWorld();{CODE} 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: {CODE(wrap="1", colors="c++")}NxOgre::SceneDescription sceneDesc; sceneDesc.mGravity = NxOgre::Vec3(0, -9.8f, 0); sceneDesc.mName = "BloodyMessTutorial2"; mScene = mWorld->createScene(sceneDesc);{CODE} With this code we have created a scene which uses the [http://en.wikipedia.org/wiki/Earth%27s_gravity|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: {CODE(wrap="1", colors="c++")}mScene->getMaterial(0)->setStaticFriction(0.5); mScene->getMaterial(0)->setDynamicFriction(0.5); mScene->getMaterial(0)->setRestitution(0.1);{CODE} These control the default frictional values and the bounce ([http://en.wikipedia.org/wiki/Restitution|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. {CODE(wrap="1", colors="c++")}mRenderSystem = new OGRE3DRenderSystem(mScene);{CODE} We are now ready to add objects to our scene. !!{img src="img/wiki_up/NxOgreCube.png" alt="NxOgreCube.png"} Adding a Cube Since our scene is quite empty right now we will now populate it with a cube. {DIV(class="Bloody_box2")}__Note:__ This project makes use of the BloodyCake (an ((NxOgre)) example framework) mesh "''cube.1m.mesh''" which can be found in the archive [http://static.nxogre.org/releases/cake/bloody/|here]. For more information see [http://www.ogre3d.org/addonforums/viewtopic.php?f=6&t=10302|this] forum post.{DIV} 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. {CODE(wrap="1", colors="c++")}OGRE3DBody* mCube;{CODE} 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 {CODE(wrap="1", colors="c++")}mCube = mRenderSystem->createBody(new NxOgre::Box(1, 1, 1), NxOgre::Vec3(0, 20, 0), "cube.1m.mesh");{CODE} 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''. !!{img src="img/wiki_up/NxOgreCube.png" alt="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: {CODE(wrap="1", colors="c++")}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; };{CODE} 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. !!{img src="img/wiki_up/NxOgreCube.png" alt="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: {CODE(wrap="1", colors="c++")}OGRE3DBody* mCubeTwo;{CODE} And add the following to the bottom of the createScene() function: {CODE(wrap="1", colors="c++")}mCubeTwo = mRenderSystem->createBody(new NxOgre::Box(1, 1, 1), NxOgre::Vec3(20, 35, 0), "cube.1m.mesh"); mCubeTwo->addForce(NxOgre::Vec3(-800, -200, 0));{CODE} 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: {CODE(wrap="1", colors="c++")}mScene->createSceneGeometry(new NxOgre::PlaneGeometry(0, NxOgre::Vec3(0, 1, 0)), Matrix44_Identity);{CODE} {DIV(class="Bloody_box2")}__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.{DIV} 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: {CODE(wrap="1", colors="c++")}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);{CODE} Compile and run your application and the two boxes should collide in mid air before finally landing on your floor plane to rest peacefully. !!{img src="img/wiki_up/NxOgreCube.png" alt="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. {INCLUDE(page="NxOgre tpl")}{INCLUDE}
Search by Tags
Search Wiki by Freetags
Latest Changes
Compiled API Reference
Overlay Editor
Introduction - JaJDoo Shader Guide - Basics
RT Shader System
RapidXML Dotscene Loader
One Function Ogre
One Function Ogre
...more
Search
Find
Online Users
174 online users
OGRE Wiki
Support and community documentation for Ogre3D
Ogre Forums
ogre3d.org
Log in
Username
Password
CapsLock is on.
Remember me (for 1 year)
Log in