In this lab we will scratch the surface of using the Bullet physics blibrary with Ogre.
Discussion thread for this lab is on Piazza
You can use your own project or start fresh with the base application code
And now for the Ogre project.
<Bullet Root>\lib;
to Linker→General→Additional Library Directories
(Debug and Release) <Bullet Root>\src;
to C++→General→Additional Include Directories
(Debug and Release) BulletCollision_vs2010_debug.lib;BulletDynamics_vs2010_debug.lib;LinearMath_vs2010_debug.lib
to the Linker→Input→Additional Dependencies
(Debug)BulletCollision_vs2010.lib;BulletDynamics_vs2010.lib;LinearMath_vs2010.lib
to the Linker→Input→Additional Dependencies
(Release)// 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; }
_physicsEngine = new Physics(); _physicsEngine->initObjects();
_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. _SinbadNode→scale(Ogre::Vector3(0.2f,0.2f,0.2f));
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; }
#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
// 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);
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); } }
_listener→_physicsEngine = _physicsEngine;
class MyFrameListener : public Ogre::FrameListener, OIS::MouseListener
And add the three virtual functions of OIS::MouseListener
virtual bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id) { mouseMask |= 1 << (int)id; return true; } virtual bool mouseMoved(const OIS::MouseEvent &arg) { return true; } virtual bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id) { return true; }
And in the constructor: replace
_Mouse = static_cast<OIS::Mouse*>(_InputManager->createInputObject(OIS::OISMouse, false));
with this
// 7.3 Initialize the Mouse input listener. try { _Mouse = static_cast<OIS::Mouse*>(_InputManager->createInputObject(OIS::OISMouse, true)); _Mouse->setEventCallback(this); std::cout << "Successfuly created Mouse"; } catch (...) { std::cout << "Failed to initialize Mouse"; _Mouse = 0; }
Also make sure to add the int member variable mouseMask, and set it to 0 in the FrameListener constructor. Now you can query whether a mouse button was pressed like so (mouseMask & (1 « (int)OIS::MouseButtonID::MB_Left))
make sure to add mouseMask = 0
before your call to _Mouse→capture()
in you FrameStarted function so we don't retain old mouse states :).
if ( (mouseMask & (1 << (int)OIS::MouseButtonID::MB_Left)) ) { // 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); }
// 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!
<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
Upload your commented source files into Lab9 in MySchool (zip them up if more than one).