I'm new to bullet and managed to get it working together with (bt)ogre. Things work generally fine, but I have a few problems:
There's a quadrotor helicopter being simulated, and after translating rotor speeds into thrust, it flies nicely through the world. My problem is that when I just let it fall, it won't ever collide with the btHeightfieldTerrainShape set up. For debugging purposes, I have already added a simple btStaticPlaneShape (the last 5 lines of the first listing, currently commented) and that does work just fine. Yes, I've read http://bulletphysics.org/Bullet/phpBB3/ ... php?t=4819 and although I'm not quite sure what transform he's talking about, I'm quite confident the heightfield isn't just offset:
This is how I set up the world:
Code: Select all
//Bullet initialisation.
mBtBroadphase = new btAxisSweep3(btVector3(-10000,-10000,-10000), btVector3(10000,10000,10000), 1024);
mBtCollisionConfig = new btDefaultCollisionConfiguration();
mBtDispatcher = new btCollisionDispatcher(mBtCollisionConfig);
mBtSolver = new btSequentialImpulseConstraintSolver();
mBtWorld = new btDiscreteDynamicsWorld(mBtDispatcher, mBtBroadphase, mBtSolver, mBtCollisionConfig);
mBtWorld->setGravity(btVector3(0,-9.8,0));
//----------------------------------------------------------
// Debug drawing!
//----------------------------------------------------------
mBtDebugDrawer = new BtOgre::DebugDrawer(mOgreWidget->sceneManager()->getRootSceneNode(), mBtWorld);
mBtWorld->setDebugDrawer(mBtDebugDrawer);
mBtDebugDrawer->setDebugMode(1);
//----------------------------------------------------------
// Vehicle!
//----------------------------------------------------------
qDebug() << "Vehicle::Vehicle(): creating vehicle";
mVehicleEntity = mOgreWidget->sceneManager()->createEntity("vehicleEntity", "quad.mesh");
// "vehicleNode" is fixed, used in ogrewidget.cpp
mVehicleNode = mOgreWidget->sceneManager()->getRootSceneNode()->createChildSceneNode("vehicleNode", Ogre::Vector3(0,10,0), Ogre::Quaternion::IDENTITY);
mVehicleNode->attachObject(mVehicleEntity);
//Create shape.
BtOgre::StaticMeshToShapeConverter converter(mVehicleEntity);
mVehicleShape = converter.createConvex();
// Reduce vertices (20k to maybe 100?)
btShapeHull* hull = new btShapeHull(mVehicleShape);
btScalar margin = mVehicleShape->getMargin();
hull->buildHull(margin);
mVehicleShape = new btConvexHullShape((btScalar*)hull->getVertexPointer(), hull->numVertices()/*, sizeof(btVector3)*/);
//Calculate inertia.
btScalar mass = 1.5;
btVector3 inertia;
mVehicleShape->calculateLocalInertia(mass, inertia);
//Create BtOgre MotionState (connects Ogre and Bullet).
BtOgre::RigidBodyState *vehicleState = new BtOgre::RigidBodyState(mVehicleNode);
//Create the Body.
mVehicleBody = new btRigidBody(mass, vehicleState, mVehicleShape, inertia);
// mVehicleBody->setCollisionFlags(mVehicleBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK );
mVehicleBody->setDamping(.5, .9);
// mVehicleBody->setActivationState(ISLAND_SLEEPING);
mBtWorld->addRigidBody(mVehicleBody/*, COL_VEHICLE, COL_GROUND*/);
// mVehicleBody->setActivationState(ISLAND_SLEEPING);
//----------------------------------------------------------
// Load terrain!
//----------------------------------------------------------
std::string terrainFileStr = "terrain.cfg";
Ogre::DataStreamPtr configStream = Ogre::ResourceGroupManager::getSingleton().openResource(terrainFileStr, Ogre::ResourceGroupManager::getSingleton().getWorldResourceGroupName());
Ogre::ConfigFile config;
config.load(configStream);
Ogre::String widthStr = config.getSetting("PageSize");
int width = atoi(widthStr.c_str());
Ogre::String imgFile = config.getSetting("Heightmap.image");
Ogre::Image* heightmap = new Ogre::Image();
heightmap->load(imgFile, Ogre::ResourceGroupManager::getSingleton().getWorldResourceGroupName());
qDebug() << "Vehicle::Vehicle(): image buffer size" << heightmap->getSize();
Ogre::String maxHeightStr = config.getSetting("MaxHeight");
btScalar maxHeight = atof(maxHeightStr.c_str());
btScalar heightScale = maxHeight / 256;
// This code for the localScaling is taken directly from the TerrainSceneManager, adapted to a btVector3
btVector3 localScaling(1, 1, 1);
localScaling.setX(atof(config.getSetting("PageWorldX").c_str()) / (width -1));
localScaling.setZ(atof(config.getSetting("PageWorldZ").c_str()) / (width -1));
// And now, we actually call Bullet. heightmap needs to be on the heap, as bullet does not copy it.
mGroundShape = new btHeightfieldTerrainShape(width, width, heightmap->getData(), heightScale, 0, maxHeight, 1, PHY_UCHAR, false);
mGroundShape->setLocalScaling(localScaling);
// All thats left is to line up the Ogre::SceneNode with the btHeightfieldTerrainShape.
// We have to do it this way because of differences in how Bullet and Ogre orient the shapes.
// In Ogre, the terrain's top left corner is at (0, 0, 0) in the local TransformSpace
// In Bullet, not only is the center of the btHeighfield terrrain shape at (0, 0, 0), but from
// what I can tell, its immovable.
btVector3 min, max;
mGroundShape->getAabb(btTransform::getIdentity(), min, max);
Ogre::SceneNode *sNode = mOgreWidget->sceneManager()->getSceneNode("Terrain");
sNode->setPosition(BtOgre::Convert::toOgre(min));
// Finally, create your btMotionState, and btRigidBody, and all the rigid body to the physics world.
BtOgre::RigidBodyState* terrainState = new BtOgre::RigidBodyState(sNode, btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 0, 0)));
mGroundBody = new btRigidBody(0, terrainState, mGroundShape, btVector3(0, 0, 0));
// mGroundBody->setCollisionFlags(mGroundBody->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT);
mBtWorld->addRigidBody(mGroundBody, COL_GROUND, COL_NOTHING);
// Debug, add a plane like the tutorial says
// btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
// btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
// btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
// btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
// mBtWorld->addRigidBody(groundRigidBody);
Code: Select all
const int simulationTime = mSimulator->getSimulationTime(); // milliseconds
const btScalar deltaS = (simulationTime - mTimeOfLastUpdate) / 1000.0f; // elapsed time since last call in seconds
const int maxSubSteps = 100;
const btScalar fixedTimeStep = 1.0 / 60.0;
qDebug() << "Vehicle::slotUpdatePhysics(): stepping physics, time is" << simulationTime << "delta" << deltaS;
mVehicleBody->applyDamping(deltaS);
Q_ASSERT(deltaS < maxSubSteps * fixedTimeStep); // http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
mBtWorld->stepSimulation(deltaS, maxSubSteps, fixedTimeStep);
mBtDebugDrawer->step();
mTimeOfLastUpdate = simulationTime;
mOgreWidget->update();
- Why doesn't the object collide with the heightfield while it does collide with the simple btStaticPlaneShape?
- Sometimes when I start the simulation, my vehicle-object doesn't move (e.g. fall without any forces except gravity applied) at all, although it should. When I restart the program and click on start again, it works. I don't need perfect determinism, but this is really strange. How can I debug this?
Thanks for your help!
ben