OgreBullet Tutorial 2         First Demo - Boxes and a static plane

This tutorial is entirely depending on the previous tutorial. We will use the project that was created there.


Open the empty project (which is not completely empty, by the way) that you created last time and run it. You can see the Ogre head, I suppose.

But, it doesn't matter now. Let's start coding!

We won't change OgreBullet_Collision_test.cpp so you can close it if you want.

Open OgreBullet_Collision_test.h. From now on everything happens here.


Notes:

- You can split it to .h and .cpp file if you like. It's not a big program so I won't :-)

- I won't use namespaces, so you can see which classes contain which function.

Coding Part

First of all we need this header to be able to use OgreBullet:

#include "OgreBulletDynamicsRigidBody.h"


Now change the constructor of your FrameListener (insert two extra fields):

OgreBullet_Collision_testFrameListener( SceneManager *sceneMgr,
                                           RenderWindow* win,
                                           Camera* cam,
                                           Vector3 &gravityVector,
                                           AxisAlignedBox &bounds)
                                           : ExampleFrameListener(win, cam),
                                             mSceneMgr(sceneMgr)

The gravityVector defines the gravity in our example. I thought bounds defined the bounds where Bullet is active, but it seems that bounds has no effect. Anyway, we need it :-)

Find createFrameListener(void) function and change the call of the previously modified function:

void createFrameListener(void)
   {
      mFrameListener= new OgreBullet_Collision_testFrameListener( mSceneMgr,
                                                  mWindow,
                                                  mCamera,
                                                  Vector3(0,-9.81,0), // gravity vector for Bullet
                                                  AxisAlignedBox (Ogre::Vector3 (-10000, -10000, -10000), //aligned box for Bullet
                                                               Ogre::Vector3 (10000,  10000,  10000)));
      mRoot->addFrameListener(mFrameListener);
   }

Now we have our frameListener as before but with the new parameters included.
Maybe try to run it. Nothing changed yet.

So, define these variables in the OgreBullet_Collision_testFrameListener class:

class OgreBullet_CollisiontestFrameListener : public ExampleFrameListener
    {
    private:
       SceneManager* mSceneMgr;
       OgreBulletDynamics::DynamicsWorld *mWorld;   // OgreBullet World
       OgreBulletCollisions::DebugDrawer *debugDrawer;
       int mNumEntitiesInstanced;
        std::deque<OgreBulletDynamics::RigidBody *>         mBodies;
        std::deque<OgreBulletCollisions::CollisionShape *>  mShapes;

mWorld will be the main variable for OgreBullet (like mRoot for Ogre).
debugDrawer will be explained soon.
mBodies and mShapes will handle the Bullet shapes and bodies. We need them to be able to free the memory and therefore get rid of memory leaks.

Let's set up the basics of OgreBullet. Go to OgreBullet_Collision_testFrameListener constructor and paste this:

mMoveSpeed = 50;   // defined in ExampleFrameListener
          mNumEntitiesInstanced = 0; // how many shapes are created
          mSceneMgr = sceneMgr;
          // Start Bullet
          mWorld = new OgreBulletDynamics::DynamicsWorld(mSceneMgr, bounds, gravityVector);
          // add Debug info display tool
          debugDrawer = new OgreBulletCollisions::DebugDrawer();
          debugDrawer->setDrawWireframe(true);   // we want to see the Bullet containers
          mWorld->setDebugDrawer(debugDrawer);
          mWorld->setShowDebugShapes(true);      // enable it if you want to see the Bullet containers
          SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode("debugDrawer", Ogre::Vector3::ZERO);
          node->attachObject(static_cast <SimpleRenderable *> (debugDrawer));

mMoveSpeed is declared in ExampleFrameListener.h and is responsible for the maximum speed of the camera movement.
mNumEntitiesInstanced is a counter that we use when we create objects dynamically (we can't have two object using the same name).
mSceneMgr is the Ogre Scene Manager.
mWorld is the OgreBullet main variable.
debugDrawer will be used to show graphical debug information about OgreBullet. We use the setDrawWireframe(true) function to set the bounding box/sphere/etc. that Bullet uses to make collisions visible; turn it off if you don't want to see it, but it's very good for debugging.
Note that debugDrawer needs Ogre to be able to draw to the screen. That's why we create a node for it.

Of course we need to delete the variables/deques in the destructor:

~OgreBullet_Collision_testFrameListener(){
          // OgreBullet physic delete - RigidBodies
          std::deque<OgreBulletDynamics::RigidBody *>::iterator itBody = mBodies.begin();
          while (mBodies.end() != itBody)
          {   
             delete *itBody;
             ++itBody;
          }   
          // OgreBullet physic delete - Shapes
          std::deque<OgreBulletCollisions::CollisionShape *>::iterator itShape = mShapes.begin();
          while (mShapes.end() != itShape)
          {   
             delete *itShape;
             ++itShape;
          }
          mBodies.clear();
          mShapes.clear();
          delete mWorld->getDebugDrawer();
          mWorld->setDebugDrawer(0);
          delete mWorld;
         }

Set the position of the camera. Find createCamera(void) and change the setPosition to this:

mCamera->setPosition(Vector3(0,18,70));


Let's create the scene. Go to OgreBullet_Collision_testApp class and find the createScene(void) function. Delete the rows which defines the default OgreHead (these were the first three row in my code).

We won't create the scene here because this function is called before the frameListener. I don't want to change the ExampleApplication.h file so let's go back to OgreBullet_Collision_testFrameListener constructor and insert this code after the last row.

First, create the floor:

// Define a floor plane mesh
          Entity *ent;
            Plane p;
            p.normal = Vector3(0,1,0); p.d = 0;
            MeshManager::getSingleton().createPlane(
                "FloorPlane", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
                p, 200000, 200000, 20, 20, true, 1, 9000, 9000, Vector3::UNIT_Z);
            // Create an entity (the floor)
            ent = mSceneMgr->createEntity("floor", "FloorPlane");
          ent->setMaterialName("Examples/BumpyMetal");
            mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);


If you run the program now, you can see an endless (at least it seems endless) plane with texture. Let's add collision detection to it!

OgreBulletCollisions::CollisionShape *Shape;
          Shape = new OgreBulletCollisions::StaticPlaneCollisionShape(Ogre::Vector3(0,1,0), 0); // (normal vector, distance)
          OgreBulletDynamics::RigidBody *defaultPlaneBody = new OgreBulletDynamics::RigidBody(
                   "BasePlane",
                   mWorld);
          defaultPlaneBody->setStaticShape(Shape, 0.1, 0.8); // (shape, restitution, friction)
          // push the created objects to the deques
          mShapes.push_back(Shape);
          mBodies.push_back(defaultPlaneBody);


Try to run it and you can see there are some errors. We need a header to be included:

#include "Shapes/OgreBulletCollisionsStaticPlaneShape.h" // for static planes


Now the plane is detecting collisions! But how to test it when the camera is going through it? Let's create some boxes and throw them into the scene!

This header is needed to be able to create a box...:

#include "Shapes/OgreBulletCollisionsBoxShape.h"       // for Boxes

...and we need boxes. Let's redefine the processUnbufferedKeyInput() function. Originally it is defined in the ExampleFrameListener. I won't start to explain it, so if you didn't already know this start reading the Ogre basic tutorials :-)

bool processUnbufferedKeyInput(const FrameEvent& evt)
       {
          bool ret = ExampleFrameListener::processUnbufferedKeyInput(evt);
                // create and throw a box if 'B' is pressed
          if(mKeyboard->isKeyDown(OIS::KC_B) && mTimeUntilNextToggle <=0)
          {
             Vector3 size = Vector3::ZERO;   // size of the box
             // starting position of the box
             Vector3 position = (mCamera->getDerivedPosition() + mCamera->getDerivedDirection().normalisedCopy() * 10);
             // create an ordinary, Ogre mesh with texture
              Entity *entity = mSceneMgr->createEntity(
                   "Box" + StringConverter::toString(mNumEntitiesInstanced),
                   "cube.mesh");            
             entity->setCastShadows(true);
             // we need the bounding box of the box to be able to set the size of the Bullet-box
             AxisAlignedBox boundingB = entity->getBoundingBox();
             size = boundingB.getSize(); size /= 2.0f; // only the half needed
             size *= 0.96f;   // Bullet margin is a bit bigger so we need a smaller size
                               // (Bullet 2.76 Physics SDK Manual page 18)
             entity->setMaterialName("Examples/BumpyMetal");
             SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
             node->attachObject(entity);
             node->scale(0.05f, 0.05f, 0.05f);   // the cube is too big for us
             size *= 0.05f;                  // don't forget to scale down the Bullet-box too
             // after that create the Bullet shape with the calculated size
             OgreBulletCollisions::BoxCollisionShape *sceneBoxShape = new OgreBulletCollisions::BoxCollisionShape(size);
             // and the Bullet rigid body
             OgreBulletDynamics::RigidBody *defaultBody = new OgreBulletDynamics::RigidBody(
                   "defaultBoxRigid" + StringConverter::toString(mNumEntitiesInstanced),
                   mWorld);
             defaultBody->setShape(   node,
                               sceneBoxShape,
                               0.6f,         // dynamic body restitution
                               0.6f,         // dynamic body friction
                               1.0f,          // dynamic bodymass
                               position,      // starting position of the box
                               Quaternion(0,0,0,1));// orientation of the box
             mNumEntitiesInstanced++;            
             defaultBody->setLinearVelocity(
                      mCamera->getDerivedDirection().normalisedCopy() * 7.0f ); // shooting speed
                   // push the created objects to the deques
             mShapes.push_back(sceneBoxShape);
             mBodies.push_back(defaultBody);            
             mTimeUntilNextToggle = 0.5;
          }
          return ret;
       }

The code is well commented here, so I won't explain everything in detail. If you press the 'B' key, a box will be created. The box is created in several phases, like the plane before.

First, we create the Ogre-box by loading a mesh. This is needed to be able to see the actual box with texture.

After this, we create the shape for Bullet, which is a BoxCollisionShape this time. This means that we want a box-shaped collision object.

Once this is done, we've arrived at the last step. We need a RigidBody and we use the setShape() function to bind together the box, the shape, and the RigidBody.

The size variable is self-explanatory, but it's worth noting that getBoundingBox() will give us the smallest box that can be drawn around the object (in our case the cube).

When you run this demo and push the 'B' key, you can see the created box floating in the air. That's not what we want.

So, the last step is to make the box move. We need to override the frameStarted() and frameEnded() functions. Here they are:

bool frameStarted(const FrameEvent& evt)
       {
          bool ret = ExampleFrameListener::frameStarted(evt);
          mWorld->stepSimulation(evt.timeSinceLastFrame);   // update Bullet Physics animation
          return ret;
       }
      
       bool frameEnded(const FrameEvent& evt)
       {
          bool ret = ExampleFrameListener::frameEnded(evt);
          mWorld->stepSimulation(evt.timeSinceLastFrame);   // update Bullet Physics animation
          return ret;
       }


The only needed function to make Bullet functional is the stepSimulation().

That's it. You can throw boxes!

I hope this tutorial was helpful. If it was or if you found grammatical/coding error don't be afraid to correct them!

Thanks for reading and have a nice day! :-)

Ogre Bullet Tutorial 1 - Source Code