[Bullet3]三種碰撞檢測及實現


官方文檔:http://bulletphysics.org
開源代碼:https://github.com/bulletphysics/bullet3/releases
API文檔:http://bulletphysics.org/Bullet/BulletFull/annotated.html

bullet3的三種碰撞檢測

以下三種方式都是可以達到碰撞檢測的效果:

  1. btCollisionWorld::contactTest 檢測指定對象是否與場景發生碰撞;
  2. btCollisionWorld::performDiscreteCollisionDetection 檢測場景中所有的碰撞;
  3. btDynamicsWorld::stepSimulation 模擬運動。

還有一種射線檢測,但是與這里的物體碰撞稍微有些區別,這里就不展開來講了。

0. 准備工作

先創建一個場景,增加一個地板(box)

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);
}

1. btCollisionWorld::contactTest

完整函數內容為

void btCollisionWorld::contactTest(btCollisionObject * colObj, ContactResultCallback & resultCallback)

contactTest會對確定的colObj對象與btCollisionWorld中的所有對象進行接觸檢測,並調用ContactResultCallBack回調。
其實這個函數不算碰撞檢測,只是算接觸檢測,如果距離為0,是會觸發回調的。

1.1. 繼承回調的結構體

ContactResultCallback結構體有一個名為addSingleResult的純虛函數,在繼承的時候一定要實現addSingleResult函數。這個也是碰撞的時候執行的回調函數。是這個結構體的核心。碰撞信息會存儲在btManifoldPoint & cp中,使用方法也比較簡單,可以參考API文檔的接口。其它地方的碰撞,也是用這個對象存儲,處理方法是一樣的。

// 碰撞檢測回調
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);
        };
};

1.2. 碰撞檢測

// 創建一個球體,並加入到場景中
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

運行結果:
result

2. btCollisionWorld::performDiscreteCollisionDetection

performDiscreteCollisionDetection會對場景中的所有物體進行一次碰撞檢測。而contactTest是對確定的物體進行碰撞檢測。

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內是存在重復的可能的,往往需要去重一下。

m_collisionObjects.sort();
m_collisionObjects.unique();

運行結果:
這里我多加了一個半徑為1,位置為{1,1,0}的求,然后基本上兩個球和地板發生了兩兩碰撞。
result

3. btDynamicsWorld::stepSimulation

完整的函數內容為:

virtual int btDynamicsWorld::stepSimulation(
    btScalar timeStep,
    int maxSubSteps = 1,
    btScalar fixedTimeStep = btScalar(1.)/btScalar(60.))

stepSimulation其實不是用來做碰撞檢測的,而是用來做物理運動模擬的。既然能做運動模擬,那肯定也能夠做碰撞檢測了。

3.1. 模擬運動

設置場景的重力加速為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());
	}
}

執行結果
result


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM