In this lab we will scratch the surface of using the Bullet physics blibrary with Ogre.
Discussion thread for this lab is on Piazza
And now for the Ogre project.
<Bullet Root>\bin;
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 :).
You will have to pass a pointer to the SceneManager into the frameListener, either through the constructor, a set function or if you're lazy, a public member.
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).