官方文檔:http://bulletphysics.org
開源代碼:https://github.com/bulletphysics/bullet3/releases
API文檔:http://bulletphysics.org/Bullet/BulletFull/annotated.htmlhtml
如下三種方式都是能夠達到碰撞檢測的效果:git
btCollisionWorld::contactTest
檢測指定對象是否與場景發生碰撞;btCollisionWorld::performDiscreteCollisionDetection
檢測場景中全部的碰撞;btDynamicsWorld::stepSimulation
模擬運動。還有一種射線檢測,可是與這裏的物體碰撞稍微有些區別,這裏就不展開來說了。github
先建立一個場景,增長一個地板(box)app
btDefaultCollisionConfiguration* g_colConfig; btCollisionDispatcher* g_dispatcher; btBroadphaseInterface* g_broadInterface; btSequentialImpulseConstraintSolver* g_solver; btDynamicsWorld* g_world; // 場景信息,退出的時候須要delete g_colConfig = new btDefaultCollisionConfiguration(); g_dispatcher = new btCollisionDispatcher(g_colConfig); g_broadInterface = new btDbvtBroadphase(); g_solver = new btSequentialImpulseConstraintSolver; g_world = new btDiscreteDynamicsWorld(g_dispatcher, g_broadInterface, g_solver, g_colConfig); g_world->setGravity(btVector3(0,-10,0)); // 設置重力加速度 // add a test box { btCollisionShape* shape = new btBoxShape(btVector3(btScalar(1000.),btScalar(10.),btScalar(1000.))); btTransform trans; trans.setIdentity(); trans.setOrigin(btVector3(0, -10, 0)); btScalar mass=0.f; btVector3 localInertia(0, 0, 0); bool isDynamic = (mass != 0.f); if (isDynamic) shape->calculateLocalInertia(mass, localInertia); btDefaultMotionState* myMotionState = new btDefaultMotionState(trans); btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia); btRigidBody* body = new btRigidBody(cInfo); g_world->addRigidBody(body); }
btCollisionWorld::contactTest
完整函數內容爲函數
void btCollisionWorld::contactTest(btCollisionObject * colObj, ContactResultCallback & resultCallback)
contactTest
會對肯定的colObj對象與btCollisionWorld
中的全部對象進行接觸檢測,並調用ContactResultCallBack
回調。
其實這個函數不算碰撞檢測,只是算接觸檢測,若是距離爲0,是會觸發回調的。spa
ContactResultCallback
結構體有一個名爲addSingleResult
的純虛函數,在繼承的時候必定要實現addSingleResult
函數。這個也是碰撞的時候執行的回調函數。是這個結構體的核心。碰撞信息會存儲在btManifoldPoint & cp
中,使用方法也比較簡單,能夠參考API文檔的接口。其它地方的碰撞,也是用這個對象存儲,處理方法是同樣的。3d
// 碰撞檢測回調 struct MyColCallBack : btCollisionWorld::ContactResultCallback { public: btScalar addSingleResult( btManifoldPoint & cp, const btCollisionObjectWrapper * colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper * colObj1Wrap, int partId1, int index1) { btVector3 posA = cp.getPositionWorldOnA(); btVector3 posB = cp.getPositionWorldOnB(); printf("col pos for A {%f, %f, %f}\n", posA.getX(), posA.getY(), posA.getZ()); printf("col pos for B {%f, %f, %f}\n", posB.getX(), posB.getY(), posB.getZ()); return btScalar(0.f); }; };
// 建立一個球體,並加入到場景中 btCollisionShape* shape = new btSphereShape(btScalar(1.f)); btTransform trans; trans.setIdentity(); trans.setOrigin(btVector3(0, 1, 0)); btScalar mass=1.f; btVector3 localInertia(0, 0, 0); bool isDynamic = (mass != 0.f); if (isDynamic) shape->calculateLocalInertia(mass, localInertia); btDefaultMotionState* myMotionState = new btDefaultMotionState(trans); btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia); btRigidBody* g_body = new btRigidBody(cInfo); g_world->addRigidBody(g_body); // 建立回調並碰撞檢測 MyColCallBack callBack; g_world->contactTest(g_body, callBack); // todo delete
運行結果:
code
btCollisionWorld::performDiscreteCollisionDetection
performDiscreteCollisionDetection
會對場景中的全部物體進行一次碰撞檢測。而contactTest
是對肯定的物體進行碰撞檢測。orm
g_world->performDiscreteCollisionDetection(); list<btCollisionObject*> m_collisionObjects; int numManifolds = g_world->getDispatcher()->getNumManifolds(); for(int i=0; i<numManifolds; i++) { btPersistentManifold* contactManifold = g_world->getDispatcher()->getManifoldByIndexInternal(i); btCollisionObject* obA = (btCollisionObject*)(contactManifold->getBody0()); btCollisionObject* obB = (btCollisionObject*)(contactManifold->getBody1()); int numContacts = contactManifold->getNumContacts(); for(int j=0; j<numContacts; j++) { btManifoldPoint& pt = contactManifold->getContactPoint(j); if(pt.getDistance()<=0.f) { m_collisionObjects.push_back(obA); m_collisionObjects.push_back(obB); btVector3 posA = pt.getPositionWorldOnA(); btVector3 posB = pt.getPositionWorldOnB(); printf("%d A -> {%f, %f, %f}\n", i, posA.getX(), posA.getY(), posA.getZ()); // 碰撞點 printf("%d B -> {%f, %f, %f}\n", i, posB.getX(), posB.getY(), posB.getZ()); } } }
這裏須要注意一下,多個物體兩兩碰撞的時候,列表m_collisionObjects
內是存在重複的可能的,每每須要去重一下。htm
m_collisionObjects.sort(); m_collisionObjects.unique();
運行結果:
這裏我多加了一個半徑爲1,位置爲{1,1,0}的求,而後基本上兩個球和地板發生了兩兩碰撞。
btDynamicsWorld::stepSimulation
完整的函數內容爲:
virtual int btDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps = 1, btScalar fixedTimeStep = btScalar(1.)/btScalar(60.))
stepSimulation
其實不是用來作碰撞檢測的,而是用來作物理運動模擬的。既然能作運動模擬,那確定也可以作碰撞檢測了。
設置場景的重力加速爲btVector3(0,-10,0)
,增長一個半徑爲1,位置爲{0,100,0}的球體,並設置其質量爲1,衝量爲{2,0,0},即球體會以x軸速度爲2,Y軸以-10的加速度作拋物線運動。
// 設置重力加速度 g_world->setGravity(btVector3(0,-10,0)); // 建立一個球體,並加入到場景中 btCollisionShape* shape = new btSphereShape(btScalar(1.f)); btTransform trans; trans.setIdentity(); trans.setOrigin(btVector3(0, 100, 0)); btScalar mass=1.f; btVector3 localInertia(0, 0, 0); bool isDynamic = (mass != 0.f); if (isDynamic) shape->calculateLocalInertia(mass, localInertia); btDefaultMotionState* myMotionState = new btDefaultMotionState(trans); btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia); btRigidBody* g_body = new btRigidBody(cInfo); g_body->applyCentralImpulse(btVector3(2,0,0)); // 設置衝量 g_world->addRigidBody(g_body); for (i=0;i<10;i++) { g_world->stepSimulation(1.f/60.f,10); // 模擬運動 trans = g_body->getWorldTransform(); printf("world pos = %f,%f,%f\n", trans.getOrigin().getX(), trans.getOrigin().getY(), trans.getOrigin().getZ()); } }
執行結果