Ogre 3d and bullet physics interaction

Posted by Tim on Game Development See other posts from Game Development or by Tim
Published on 2011-10-25T23:41:14Z Indexed on 2012/06/11 4:47 UTC
Read the original article Hit count: 504

Filed under:
|
|

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. weird physics

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.

© Game Development or respective owner

Related posts about c++

Related posts about ogre