Ogre 3d and bullet physics interaction
- by Tim
I have been playing around with Ogre3d and trying to integrate bullet physics. I have previously somewhat successfully got this functionality working with irrlicht and bullet and I am trying to base this on what I had done there, but modifying it to fit with Ogre. It is working but not correctly and I would like some help to understand what it is I am doing wrong.
I have a state system and when I enter the "gamestate" I call some functions such as setting up a basic scene, creating the physics simulation. I am doing that as follows.
void GameState::enter() {
...
// Setup Physics
btBroadphaseInterface *BroadPhase = new btAxisSweep3(btVector3(-1000,-1000,-1000), btVector3(1000,1000,1000));
btDefaultCollisionConfiguration *CollisionConfiguration = new btDefaultCollisionConfiguration();
btCollisionDispatcher *Dispatcher = new btCollisionDispatcher(CollisionConfiguration);
btSequentialImpulseConstraintSolver *Solver = new btSequentialImpulseConstraintSolver();
World = new btDiscreteDynamicsWorld(Dispatcher, BroadPhase, Solver, CollisionConfiguration);
...
createScene();
}
In the createScene method I add a light and try to setup a "ground" plane to act as the ground for things to collide with.. as follows. I expect there is issues with this as I get objects colliding with the ground but half way through it and they glitch around like crazy on collision.
void GameState::createScene() {
m_pSceneMgr->createLight("Light")->setPosition(75,75,75);
// Physics
// As a test we want a floor plane for things to collide with
Ogre::Entity *ent;
Ogre::Plane p;
p.normal = Ogre::Vector3(0,1,0); p.d = 0;
Ogre::MeshManager::getSingleton().createPlane(
"FloorPlane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
p, 200000, 200000, 20, 20, true, 1, 9000,9000,Ogre::Vector3::UNIT_Z);
ent = m_pSceneMgr->createEntity("floor", "FloorPlane");
ent->setMaterialName("Test/Floor");
Ogre::SceneNode *node = m_pSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(ent);
btTransform Transform;
Transform.setIdentity();
Transform.setOrigin(btVector3(0,1,0));
// Give it to the motion state
btDefaultMotionState *MotionState = new btDefaultMotionState(Transform);
btCollisionShape *Shape = new btStaticPlaneShape(btVector3(0,1,0),0);
// Add Mass
btVector3 LocalInertia;
Shape->calculateLocalInertia(0, LocalInertia);
// CReate the rigid body object
btRigidBody *RigidBody = new btRigidBody(0, MotionState, Shape, LocalInertia);
// Store a pointer to the Ogre Node so we can update it later
RigidBody->setUserPointer((void *) (node));
// Add it to the physics world
World->addRigidBody(RigidBody);
Objects.push_back(RigidBody);
m_pNumEntities++;
// End Physics
}
I then have a method to create a cube and give it rigid body physics properties. I know there will be errors here as I get the items colliding with the ground but not with each other properly. So I would appreciate some input on what I am doing wrong.
void GameState::CreateBox(const btVector3 &TPosition, const btVector3 &TScale, btScalar TMass)
{
Ogre::Vector3 size = Ogre::Vector3::ZERO;
Ogre::Vector3 pos = Ogre::Vector3::ZERO;
Ogre::Vector3 scale = Ogre::Vector3::ZERO;
pos.x = TPosition.getX();
pos.y = TPosition.getY();
pos.z = TPosition.getZ();
scale.x = TScale.getX();
scale.y = TScale.getY();
scale.z = TScale.getZ();
Ogre::Entity *entity = m_pSceneMgr->createEntity(
"Box" + Ogre::StringConverter::toString(m_pNumEntities),
"cube.mesh");
entity->setCastShadows(true);
Ogre::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
entity->setMaterialName("Test/Cube");
Ogre::SceneNode *node = m_pSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(entity);
node->setPosition(pos);
//node->scale(scale);
// Physics
btTransform Transform;
Transform.setIdentity();
Transform.setOrigin(TPosition);
// Give it to the motion state
btDefaultMotionState *MotionState = new btDefaultMotionState(Transform);
btVector3 HalfExtents(TScale.getX()*0.5f,TScale.getY()*0.5f,TScale.getZ()*0.5f);
btCollisionShape *Shape = new btBoxShape(HalfExtents);
// Add Mass
btVector3 LocalInertia;
Shape->calculateLocalInertia(TMass, LocalInertia);
// CReate the rigid body object
btRigidBody *RigidBody = new btRigidBody(TMass, MotionState, Shape, LocalInertia);
// Store a pointer to the Ogre Node so we can update it later
RigidBody->setUserPointer((void *) (node));
// Add it to the physics world
World->addRigidBody(RigidBody);
Objects.push_back(RigidBody);
m_pNumEntities++;
}
Then in the GameState::update() method which which runs every frame to handle input and render etc I call an UpdatePhysics method to update the physics simulation.
void GameState::UpdatePhysics(unsigned int TDeltaTime)
{
World->stepSimulation(TDeltaTime * 0.001f, 60);
btRigidBody *TObject;
for(std::vector<btRigidBody *>::iterator it = Objects.begin(); it != Objects.end(); ++it) {
// Update renderer
Ogre::SceneNode *node = static_cast<Ogre::SceneNode *>((*it)->getUserPointer());
TObject = *it;
// Set position
btVector3 Point = TObject->getCenterOfMassPosition();
node->setPosition(Ogre::Vector3((float)Point[0], (float)Point[1], (float)Point[2]));
// set rotation
btVector3 EulerRotation;
QuaternionToEuler(TObject->getOrientation(), EulerRotation);
node->setOrientation(1,(Ogre::Real)EulerRotation[0], (Ogre::Real)EulerRotation[1], (Ogre::Real)EulerRotation[2]);
//node->rotate(Ogre::Vector3(EulerRotation[0], EulerRotation[1], EulerRotation[2]));
}
}
void GameState::QuaternionToEuler(const btQuaternion &TQuat, btVector3 &TEuler) {
btScalar W = TQuat.getW();
btScalar X = TQuat.getX();
btScalar Y = TQuat.getY();
btScalar Z = TQuat.getZ();
float WSquared = W * W;
float XSquared = X * X;
float YSquared = Y * Y;
float ZSquared = Z * Z;
TEuler.setX(atan2f(2.0f * (Y * Z + X * W), -XSquared - YSquared + ZSquared + WSquared));
TEuler.setY(asinf(-2.0f * (X * Z - Y * W)));
TEuler.setZ(atan2f(2.0f * (X * Y + Z * W), XSquared - YSquared - ZSquared + WSquared));
TEuler *= RADTODEG;
}
I seem to have issues with the cubes not colliding with each other and colliding strangely with the ground. I have tried to capture the effect with the attached image. I would appreciate any help in understanding what I have done wrong. Thanks.
EDIT : Solution
The following code shows the changes I made to get accurate physics.
void GameState::createScene()
{
m_pSceneMgr->createLight("Light")->setPosition(75,75,75);
// Physics
// As a test we want a floor plane for things to collide with
Ogre::Entity *ent;
Ogre::Plane p;
p.normal = Ogre::Vector3(0,1,0); p.d = 0;
Ogre::MeshManager::getSingleton().createPlane(
"FloorPlane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
p, 200000, 200000, 20, 20, true, 1, 9000,9000,Ogre::Vector3::UNIT_Z);
ent = m_pSceneMgr->createEntity("floor", "FloorPlane");
ent->setMaterialName("Test/Floor");
Ogre::SceneNode *node = m_pSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(ent);
btTransform Transform;
Transform.setIdentity();
// Fixed the transform vector here for y back to 0 to stop the objects sinking into the ground.
Transform.setOrigin(btVector3(0,0,0));
// Give it to the motion state
btDefaultMotionState *MotionState = new btDefaultMotionState(Transform);
btCollisionShape *Shape = new btStaticPlaneShape(btVector3(0,1,0),0);
// Add Mass
btVector3 LocalInertia;
Shape->calculateLocalInertia(0, LocalInertia);
// CReate the rigid body object
btRigidBody *RigidBody = new btRigidBody(0, MotionState, Shape, LocalInertia);
// Store a pointer to the Ogre Node so we can update it later
RigidBody->setUserPointer((void *) (node));
// Add it to the physics world
World->addRigidBody(RigidBody);
Objects.push_back(RigidBody);
m_pNumEntities++;
// End Physics
}
void GameState::CreateBox(const btVector3 &TPosition, const btVector3 &TScale, btScalar TMass)
{
Ogre::Vector3 size = Ogre::Vector3::ZERO;
Ogre::Vector3 pos = Ogre::Vector3::ZERO;
Ogre::Vector3 scale = Ogre::Vector3::ZERO;
pos.x = TPosition.getX();
pos.y = TPosition.getY();
pos.z = TPosition.getZ();
scale.x = TScale.getX();
scale.y = TScale.getY();
scale.z = TScale.getZ();
Ogre::Entity *entity = m_pSceneMgr->createEntity(
"Box" + Ogre::StringConverter::toString(m_pNumEntities),
"cube.mesh");
entity->setCastShadows(true);
Ogre::AxisAlignedBox boundingB = entity->getBoundingBox();
// The ogre bounding box is slightly bigger so I am reducing it for
// use with the rigid body.
size = boundingB.getSize()*0.95f;
entity->setMaterialName("Test/Cube");
Ogre::SceneNode *node = m_pSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(entity);
node->setPosition(pos);
node->showBoundingBox(true);
//node->scale(scale);
// Physics
btTransform Transform;
Transform.setIdentity();
Transform.setOrigin(TPosition);
// Give it to the motion state
btDefaultMotionState *MotionState = new btDefaultMotionState(Transform);
// I got the size of the bounding box above but wasn't using it to set
// the size for the rigid body. This now does.
btVector3 HalfExtents(size.x*0.5f,size.y*0.5f,size.z*0.5f);
btCollisionShape *Shape = new btBoxShape(HalfExtents);
// Add Mass
btVector3 LocalInertia;
Shape->calculateLocalInertia(TMass, LocalInertia);
// CReate the rigid body object
btRigidBody *RigidBody = new btRigidBody(TMass, MotionState, Shape, LocalInertia);
// Store a pointer to the Ogre Node so we can update it later
RigidBody->setUserPointer((void *) (node));
// Add it to the physics world
World->addRigidBody(RigidBody);
Objects.push_back(RigidBody);
m_pNumEntities++;
}
void GameState::UpdatePhysics(unsigned int TDeltaTime)
{
World->stepSimulation(TDeltaTime * 0.001f, 60);
btRigidBody *TObject;
for(std::vector<btRigidBody *>::iterator it = Objects.begin(); it != Objects.end(); ++it) {
// Update renderer
Ogre::SceneNode *node = static_cast<Ogre::SceneNode *>((*it)->getUserPointer());
TObject = *it;
// Set position
btVector3 Point = TObject->getCenterOfMassPosition();
node->setPosition(Ogre::Vector3((float)Point[0], (float)Point[1], (float)Point[2]));
// Convert the bullet Quaternion to an Ogre quaternion
btQuaternion btq = TObject->getOrientation();
Ogre::Quaternion quart = Ogre::Quaternion(btq.w(),btq.x(),btq.y(),btq.z());
// use the quaternion with setOrientation
node->setOrientation(quart);
}
}
The QuaternionToEuler function isn't needed so that was removed from code and header files. The objects now collide with the ground and each other appropriately.