3
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ You could always use Gamekit's Ogrekit. \$\endgroup\$
    – user17002
    Jun 11, 2012 at 0:09

1 Answer 1

5
\$\begingroup\$
  • The Ogre ground plane is at y=0, but the Bullet ground is at y=1, because the motion state transform has its position set to (0,1,0) by this line: Transform.setOrigin(btVector3(0,1,0)); This is why the boxes are sinking into the ground.
  • Do the dimensions of cube.mesh match the TScale dimensions which are being used to create the rigid body? I'd guess the cube.mesh is bigger, which makes it look like the cubes are not colliding.
  • The Ogre SceneNode::setOrientation function does not use Euler angles, it uses the quaternion components directly.
\$\endgroup\$
1
  • \$\begingroup\$ Thank you. I appreciate the response. It is of course working now. I will edit the question with the changes I made in case it helps anyone else. :) \$\endgroup\$
    – Tim
    Oct 26, 2011 at 3:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .