Table of Contents

LAB9: Physics

In this lab we will scratch the surface of using the Bullet physics blibrary with Ogre.

Discussion

Discussion thread for this lab is on Piazza

Bullet

  1. Setup Bullet Project.
  2. Downlaod Bullet - Use version 2.83.7 (later versions are not supported).
  3. Build Bullet
    1. Run <bullet root>\build\vs2010.bat to generate the VS 2010 project files.
    2. Open the <bullet root>\build\vs2010\0BulletSolution.sln With you VS of choice.
    3. Visual studio should prompt you to update the project to visual studio, accept that.
    4. We have confirmed that compiling using either the v110 version or the v141 version of the visual studio compiler works
      Note: that you can select properties for one project, then multi select the projects you want to change the properties for to change the value in multiple projects at the same time.
    5. Change the C/C++→Code Generation→Runtime Library to Multi Threaded DLL (/MDd (Debug) and /MD (Release))
    6. Build the solution for debug and release. You can skip all projects starting with test_ or app_ if you like.
    7. Note: If your Ogre was compiled for 64 bit, your Bullet build will also need to be compiled for 64 bit!

Lab

And now for the Ogre project.

Please Note:

You will not be able to compile until you have finished step 8!
  1. Add <Bullet Root>\bin; to Linker→General→Additional Library Directories (Debug and Release)
  2. Add <Bullet Root>\src; to C++→General→Additional Include Directories (Debug and Release)
  3. Add (note: file names may vary slightly, e.g. _64bit is appended for 64 bit builds, verify what files you have!) BulletCollision_vs2010_debug.lib;BulletDynamics_vs2010_debug.lib;LinearMath_vs2010_debug.lib to the Linker→Input→Additional Dependencies (Debug)
  4. Add (note: file names may vary slightly, e.g. _64bit is appended for 64 bit builds, verify what files you have!) BulletCollision_vs2010.lib;BulletDynamics_vs2010.lib;LinearMath_vs2010.lib to the Linker→Input→Additional Dependencies (Release)
  5. Create class Physics.
    // Header!
     
    #ifndef PHYSICS_H
    #define PHYSICS_H
    #include "btBulletCollisionCommon.h"
    #include "btBulletDynamicsCommon.h"
    #include "OgreMotionState.h"
    #include <vector>
    #include <map>
     
    class Physics{
    	btDefaultCollisionConfiguration* collisionConfiguration;
    	btCollisionDispatcher* dispatcher;
    	btBroadphaseInterface* overlappingPairCache;
    	btSequentialImpulseConstraintSolver* solver;
    	btDiscreteDynamicsWorld* dynamicsWorld;
    	std::vector<btCollisionShape *> collisionShapes;
    	std::map<std::string, btRigidBody *> physicsAccessors;
    public:
    	Physics();
            void initObjects();
    	virtual ~Physics();
    	btDiscreteDynamicsWorld* getDynamicsWorld();
     
    };
     
    #endif //PHYSICS_H
     
    // Implementation!
     
    #include "Physics.h"
     
    Physics::Physics() {
     
    }
     
    Physics::~Physics() {
    	delete dynamicsWorld;
    	delete solver;
    	delete overlappingPairCache;
    	delete dispatcher;
    	delete collisionConfiguration;
    }
     
    void Physics::initObjects() {
    	collisionConfiguration = new btDefaultCollisionConfiguration();
    	dispatcher = new btCollisionDispatcher(collisionConfiguration);
    	overlappingPairCache = new btDbvtBroadphase();
    	solver = new btSequentialImpulseConstraintSolver();
    	dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
    }
     
    btDiscreteDynamicsWorld* Physics::getDynamicsWorld() {
    	return dynamicsWorld;
    }
  6. Create a pointer to physics in your Application, and initialize it in the startup function.
    	_physicsEngine = new Physics();
    	_physicsEngine->initObjects();
  7. Before we start there are some basic changes that have to made to the environment, in order to make the physics look more realist, we must make sure that our environmet is in a logical scale. If you have the handout code, Sinbad will be around 9.5 units high, Bullet library uses 1 unit as equal to 1 meter.
    1. You can check out Sinbad's actualt height by doing _SinbadNode→getBoundingBox().getSize(), this function will return the bounding box the the sceneNode, which is an axis aligned box that encompasses the entities of the SceneNode and is defined by the vertex extremas + an additional 2% scale.
    2. Now scale the sinbad node down by 0.2 _SinbadNode→scale(Ogre::Vector3(0.2f,0.2f,0.2f));
    3. Change the nearClippingPlaneDistance of the camera to 0.5
    4. In the frameListener class, change the movement speed of the camera to something around 10-20.
  8. Now to create Rigid Bodies we must first let bullet know what form of rigid body we want, and we must also tell bullet the size, position and orientation of our RigidBody.
    1. Add this function to your Physics class.
      btRigidBody* AddDynamicCubeRigidBoidy(Ogre::SceneNode* node, Ogre::Entity* ent, btScalar mass)
       
      btRigidBody* Physics::AddDynamicCubeRigidBoidy(Ogre::SceneNode* node, Ogre::Entity* ent, btScalar mass) {
      	Ogre::Vector3 aabbSize = ent->getBoundingBox().getSize() / 1.02f;
       
      	btScalar x = 0.5f * node->getScale().x * aabbSize.x;
      	btScalar y = 0.5f * node->getScale().y * aabbSize.y;
      	btScalar z = 0.5f * node->getScale().z * aabbSize.z;
      	btCollisionShape* colShape = new btBoxShape(btVector3(x, y, z));
       
      	/// Create Dynamic Objects
      	btTransform startTransform;
      	startTransform.setIdentity();
       
      	//rigidbody is dynamic if and only if mass is non zero, otherwise static
      	bool isDynamic = (mass != 0.f);
       
      	btVector3 localInertia(0, 0, 0);
      	if (isDynamic)
      		colShape->calculateLocalInertia(mass, localInertia);
       
      	startTransform.setOrigin(btVector3(node->getPosition().x, node->getPosition().y, node->getPosition().z));
       
      	btQuaternion initRotation(node->getOrientation().x, node->getOrientation().y, node->getOrientation().z, node->getOrientation().w);
      	startTransform.setRotation(initRotation);
       
      	OgreMotionState* motionState = new OgreMotionState(startTransform, node);
      	btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, colShape, localInertia);
      	btRigidBody* body = new btRigidBody(rbInfo);
       
      	dynamicsWorld->addRigidBody(body);
       
      	return body;
      }
    2. As you might have noticed, that function uses a class called OgreMotionState, this is a class that we must implement in order for Bullet to automatically transoform our SceneNode according to the movement of the physics object.
    3. Add the file OgreMotionState.h to your project.
      #ifndef OGREMOTIONSTATE_H
      #define OGREMOTIONSTATE_H
       
      #include "LinearMath\btMotionState.h"
      #include "OGRE\Ogre.h"
      class OgreMotionState : public btMotionState {
      public:
      	OgreMotionState(const btTransform &initialpos, Ogre::SceneNode *node) {
      		mVisibleobj = node;
      		mPos1 = initialpos;
      	}
       
      	virtual ~OgreMotionState() {
      	}
       
      	void setNode(Ogre::SceneNode *node) {
      		mVisibleobj = node;
      	}
       
      	virtual void getWorldTransform(btTransform &worldTrans) const {
      		worldTrans = mPos1;
      	}
       
      	virtual void setWorldTransform(const btTransform &worldTrans) {
      		if (NULL == mVisibleobj)
      			return;
      		btQuaternion rot = worldTrans.getRotation();
      		mVisibleobj->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
      		btVector3 pos = worldTrans.getOrigin();
      		mVisibleobj->setPosition(pos.x(), pos.y(), pos.z());
      	}
       
      protected:
      	Ogre::SceneNode *mVisibleobj;
      	btTransform mPos1;
      };
       
      #endif //OGREMOTIONSTATE_H
  9. Now we are ready to create some rigid Bodies.
    1. Lets start by setting the ground as a collision plane. Make the following changes and add the bullet code to the definition of your ground plane.
      // Create the plane.
      	Ogre::Plane plane(Ogre::Vector3::UNIT_Y, 0);
      	Ogre::MeshManager::getSingleton().createPlane("plane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
      		1500, 1500, 200, 200, true, 1, 5, 5, Ogre::Vector3::UNIT_Z);
       
      	// Set the plane as the ground and add a texture to it.
      	Ogre::Entity* ground = _sceneManager->createEntity("LightPlaneEntity", "plane");
      	Ogre::SceneNode* groundNode = _sceneManager->getRootSceneNode()->createChildSceneNode();
      	groundNode->attachObject(ground);
      	ground->setMaterialName("Examples/BeachStones");
       
      	groundNode->setPosition(0, -1, 0);
       
      	// Create the collision shape, and give it the ground plane normals.
      	btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(plane.normal.x, plane.normal.y, plane.normal.z), 0);
       
      	// Create the collision transform.
      	btTransform groundTransform;
       
      	// Set up the collision location and orientation.
      	groundTransform.setIdentity();
      	groundTransform.setOrigin(btVector3(groundNode->getPosition().x, groundNode->getPosition().y, groundNode->getPosition().z));
      	btQuaternion initRotation(groundNode->getOrientation().x, groundNode->getOrientation().y, groundNode->getOrientation().z, groundNode->getOrientation().w);
      	groundTransform.setRotation(initRotation);
       
      	// Give the plane a mass of 0, because our plane will be static, thus will not moce.
      	btScalar mass(0.0f);
       
      	// Set the ground as a static object.
      	bool isDynamic = false;
       
      	// This plane isnt going to be moving so i dont care about setting the motion state
      	btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
      	btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, myMotionState, groundShape);
      	btRigidBody* body = new btRigidBody(rbInfo);
       
      	//add the body to the dynamics world
      	_physicsEngine->getDynamicsWorld()->addRigidBody(body);
    2. Now lets add some cubes :), add this function to your create scene function.
      	int ringCount = 16;
      	int ringheight = 10;
      	for (int i = 0; i < ringheight; ++i) {
      		for (int j = 0; j < ringCount; ++j) {
      			Ogre::Entity* cubeEnt = _sceneManager->createEntity("Cube.mesh");
      			Ogre::SceneNode* cubeNode = _sceneManager->getRootSceneNode()->createChildSceneNode();
      			cubeNode->setScale(0.006f, 0.004f, 0.006f);
      			Ogre::Radian angle((Ogre::Math::TWO_PI / ringCount)*j + (Ogre::Math::TWO_PI / ringCount)*0.5f*(i%2) );
       
      			cubeNode->setOrientation(Ogre::Quaternion(-angle, Ogre::Vector3::UNIT_Y));
      			//cubeNode->setPosition(j*0.62+((i%2)*0.31f), i*0.405-(1-0.2), 0);
       
      			cubeNode->setPosition(Ogre::Math::Cos(angle) * 1.9f, i*0.405f - (1 - 0.2f), Ogre::Math::Sin(angle) * 1.9f);
       
      			cubeNode->attachObject(cubeEnt);
      			btRigidBody* temp = _physicsEngine->AddDynamicCubeRigidBoidy(cubeNode, cubeEnt, 1);
      			temp->setFriction(1);
      		}	
      	}
  10. And finally we must update the dynamicWorld and of course make the camera shoot some boxes! Add the RoamingCamera from Lab 3
// HEADER!
#pragma once
#include <Ogre.h>
#include <OgreApplicationContext.h>
// A simple indepdendent camera that can be flown around the scene
// using the mouse and WASD keys, while pressing and holding the
// right mouse button.
class RoamingCamera
{
public:
    // Constructor
    // sceneManager: contains the scene the camera will roam about
    // win: is a valid render window into which the camera view will get rendered
    RoamingCamera(Ogre::SceneManager* sceneManager, Ogre::RenderWindow* win, Physics* physicsEngine);
    // Destructor
    ~RoamingCamera();
    // Update
    // dt: Elapsed time in seconds from the last time this method got called
    // state: Valid SDL keyboard state, as retrieved with SDL_GetKeyboardState(..)
    void update(Ogre::Real dt, const Uint8 * state);
private:
    Ogre::Camera*    _cam; // My actual camera
    Ogre::SceneNode* _node;     // My scene graph node
    Physics* _physicsEngine;
};
 
// Implementation!
#include "RoamingCamera.h"
RoamingCamera::RoamingCamera(Ogre::SceneManager* sceneManager, Ogre::RenderWindow* win, Physics* physicsEngine)
{
    _physicsEngine = physicsEngine;
    // Attaching the camera
    _node = sceneManager->getRootSceneNode()->createChildSceneNode();
    _cam = sceneManager->createCamera("myCam");
    _node->attachObject(_cam);
    _node->setPosition(0, 0, 50);
    _node->lookAt(Ogre::Vector3(0, 0, 0), 
    Ogre::Node::TransformSpace::TS_WORLD);
    // Configuring the camera
    _cam->setNearClipDistance(5);
    Ogre::Viewport* vp = win->addViewport(_cam);
    vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0));
    _cam->setAspectRatio(Ogre::Real(vp->getActualWidth()) / 
    Ogre::Real(vp->getActualHeight()));
}
RoamingCamera::~RoamingCamera()
{
}
void RoamingCamera::update(Ogre::Real dt, const Uint8 * state)
{
    int x = 0, y = 0;
    // Leave if right mouse button is not being pressed
    // ...but also retrieve and store mouse movement
    if (!(SDL_GetRelativeMouseState(&x, &y) & SDL_BUTTON_RMASK)) return;
    // Construct displacement vector
    Ogre::Vector3 vec = Ogre::Vector3(0, 0, 0);
    if (state[SDL_SCANCODE_W]) {
        vec = Ogre::Vector3(0, 0, -1);
    }
    if (state[SDL_SCANCODE_S]) {
        vec = Ogre::Vector3(0, 0, 1);
    }
    if (state[SDL_SCANCODE_A]) {
        vec = Ogre::Vector3(-1, 0, 0);
    }
    if (state[SDL_SCANCODE_D]) {
        vec = Ogre::Vector3(1, 0, 0);
    }
    if (state[SDL_SCANCODE_Q]) {
        vec = Ogre::Vector3(0, 1, 0);
    }
    if (state[SDL_SCANCODE_E]) {
        vec = Ogre::Vector3(0, -1, 0);
    }
    // Construct view rotation
    float rotX = x * dt * -1 * 2.0f;
    float rotY = y * dt * -1 * 2.0f;
    // Update camera with new displacement and rotation
    _cam->yaw(Ogre::Radian(rotX));
    _cam->pitch(Ogre::Radian(rotY));
    _cam->moveRelative(dt * 4.0 * vec);
}

make it shoot boxes when some button is pressed, you can shoot boxes like this:

// Create a cube entity.
Ogre::Entity* cubeEnt = _sceneManager->createEntity("Cube.mesh");
Ogre::SceneNode* cubeNode = _sceneManager->getRootSceneNode()->createChildSceneNode();
 
cubeNode->setScale(0.005f, 0.005f, 0.005f);
// Set the position of the cube to the front of the origin of the camera 
cubeNode->setPosition(_cam->getPosition() + _cam->getDirection());
cubeNode->attachObject(cubeEnt);
 
// Now make the cube a rigid body, give it some linearVelocity and add it to the physics world
btRigidBody* boxBody = _physicsEngine->AddDynamicCubeRigidBoidy(cubeNode, cubeEnt, 2);
boxBody->setLinearVelocity(btVector3(_cam->getDirection().x, _cam->getDirection().y, _cam->getDirection().z) * 30);
  1. Finally we update the physics world
    // Now update the physics world with the delta time. "Note: normally we would want to have the physics world update a little more independant of the framerate, but this will do for now :)"
    	_physicsEngine->getDynamicsWorld()->stepSimulation(evt.timeSinceLastFrame);

Attention!

Compile and run using a Release built target!!! Very important, unless you plan to fry some eggs and bacon on your laptop :) Just make sure to use the Release version of the plugins file when you run the application.It can be found under <YourOgreSDK>\bin\Release\plugins.cfg Check out these pre-processor variables to help you out
#if _DEBUG
	cf.load("../LabFiles/OgreConfig/resources_d.cfg");
#elif NDEBUG
	cf.load("../LabFiles/OgreConfig/resources.cfg");
#endif
 
#if _DEBUG
	_root = new Ogre::Root("../LabFiles/OgreConfig/plugins_d.cfg","../LabFiles/OgreConfig/ogre.cfg", "../LabFiles/ogre.log");
#elif NDEBUG
	_root = new Ogre::Root("../LabFiles/OgreConfig/plugins.cfg","../LabFiles/OgreConfig/ogre.cfg", "../LabFiles/ogre.log");
#endif

When You Are Finished

Upload your commented source files into Lab9 in MySchool (zip them up if more than one).