Replacing narrow phase collision detection

console
Posts: 18
Joined: Thu Sep 04, 2008 10:25 am

Replacing narrow phase collision detection

Post by console »

I'm researching collision detection and I'd like to replace the collision detection engine in Bullet with a variety of alternatives. Currently, I'm investigating SOLID (http://www.dtecta.com/) as a replacement.

Based on the advice in a previous post (http://www.bulletphysics.com/Bullet/php ... 61&p=10234), I've derived a custom class from btDiscreteDynamicsWorld and I've overridden the performDiscreteCollisionDetection() method. I've successfully replaced the broad phase, but I'm having difficulty with the narrow phase. I realise that the collision data needs to be placed in btPersistentManifold objects, but I'm unable to determine how these work. How do I create them? And how do I add them to the list of objects to be processed?

The narrow phase SOLID function I'm using is DT_GetPenDepth(), which returns two contact points that I think I can use with the btPersistentManifold.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: Replacing narrow phase collision detection

Post by Erwin Coumans »

Collision detection is basically over 90% of Bullet, so you won't use much of Bullet if you replace it. The Bullet collision detection system used SOLID as an inspiration, adding improvements in most areas.
console wrote:Based on the advice in a previous post (http://www.bulletphysics.com/Bullet/php ... 61&p=10234), I've derived a custom class from btDiscreteDynamicsWorld and I've overridden the performDiscreteCollisionDetection() method. I've successfully replaced the broad phase, but I'm having difficulty with the narrow phase. I realise that the collision data needs to be placed in btPersistentManifold objects, but I'm unable to determine how these work. How do I create them? And how do I add them to the list of objects to be processed?
You could try to create a btCollisionDispatcher, and only use it to create btPersistentManifold objects (use getNewManifold and releaseManifold).

The result of a single closest point/penetration query is usually a point on object A, point on object B, contact normal (connecting both points) and distance (between those two points, with negative distance == penetration).
btPersistentManifold is a contact point cache that collects and updates multiple contact points. Rigid body simulation becomes instable for most collision shapes when using only a single point, so using the btPersistentManifold contact cache is recommended. Just add the points using btPersistentManifold::addManifoldPoint. A btManifoldPoint contains the point on A and point on B in local space and world space, and contact normal in world space (pointing from point B towards A) and distance. Each frame, make sure to update all contact manifolds using btPersistentManifold::refreshContactPoints.

Note that Bullet 2.74 can produce multiple contact points at a time, using a perturbation method.
Hope this helps,
Erwin
console
Posts: 18
Joined: Thu Sep 04, 2008 10:25 am

Re: Replacing narrow phase collision detection

Post by console »

Thanks for the advice. I understand that collision detection accounts for the majority of Bullet and that there is typically no benefit to replacing it, but I need to be able to compare different collision detection algorithms for my research. And I need a physics engine to simulate the objects, so I'm using Bullet.

I followed your suggestions and created the following collision detection code, in my class inherited from btDiscreteDynamicsWorld:

Code: Select all

    for (int i = 0; i < m_dispatcher1->getNumManifolds(); i++) {
        const btRigidBody* body0 = reinterpret_cast<const btRigidBody*>(m_dispatcher1->getManifoldByIndexInternal(i)->getBody0());
        const btRigidBody* body1 = reinterpret_cast<const btRigidBody*>(m_dispatcher1->getManifoldByIndexInternal(i)->getBody1());
        m_dispatcher1->getManifoldByIndexInternal(i)->refreshContactPoints(body0->getWorldTransform(), body1->getWorldTransform());
    }

    btBroadphasePairArray& overlapping = m_broadphasePairCache->getOverlappingPairCache()->getOverlappingPairArray();
    for (int i = 0; i < overlapping.size(); i++) {
        btRigidBody*       object0  = reinterpret_cast<btRigidBody*>(overlapping[i].m_pProxy0->m_clientObject);
        btRigidBody*       object1  = reinterpret_cast<btRigidBody*>(overlapping[i].m_pProxy1->m_clientObject);
        const unsigned int pointer0 = reinterpret_cast<unsigned int>(object0->getUserPointer());
        const unsigned int pointer1 = reinterpret_cast<unsigned int>(object1->getUserPointer());

        DT_Vector3 point0;
        DT_Vector3 point1;
        for (unsigned int j = 0; j < _objects[pointer0].size(); j++) {
            for (unsigned int k = 0; k < _objects[pointer1].size(); k++) {
                const ScalarType distance = DT_GetClosestPair(_objects[pointer0][j].get(), _objects[pointer0][k].get(), point0, point1);
                btPersistentManifold* manifold = m_dispatcher1->getNewManifold(reinterpret_cast<void*>(object0), reinterpret_cast<void*>(object1));
                const btVector3 pointA(point0[0], point0[1], point0[2]);
                const btVector3 pointB(point1[0], point1[1], point1[2]);
                const btVector3 normal(pointB - pointA);
                manifold->addManifoldPoint(btManifoldPoint(pointA, pointB, normal, distance));
            }
        }
    }
A note on the code: _objects stores a list of SOLID objects. Since SOLID cannot represent a Bullet compound object, I use a 2D array with a list of objects for each Bullet object.

This code doesn't seem to do anything as all of my objects pass through one another. I am sure the problem is in my narrow phase as my broad phase works correctly when used with Bullet's narrow phase. Could you provide any advice as to what I may be doing wrong?
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: Replacing narrow phase collision detection

Post by Erwin Coumans »

console wrote: This code doesn't seem to do anything as all of my objects pass through one another. I am sure the problem is in my narrow phase as my broad phase works correctly when used with Bullet's narrow phase. Could you provide any advice as to what I may be doing wrong?
Is addManifoldPoint ever called?

There are several ways to hookup narrowphase collision algorithms.

The recommended way, that uses a lot of Bullet collision infrastructure, is to use register your custom collision algorithm, using a derived version of btDefaultCollisionConfiguration. This way, all datastructures are maintained properly.

Another way is to avoid all existing Bullet collision infrastructure: You could call SOLID dtTest, and add points during the callback (instead of calling it manually for each pair).

Please let us know how you get on,
Erwin
console
Posts: 18
Joined: Thu Sep 04, 2008 10:25 am

Re: Replacing narrow phase collision detection

Post by console »

Thanks again for your helpful feedback. Unfortunately, your recommended suggestion doesn't really work for my scenario as I want to be able to time SOLID with no intervention from Bullet. However, I followed your second suggestion (to use DT_Test) and I've implemented the following code:

Code: Select all

void MyClass::performDiscreteCollisionDetection() {
    for (int i = 0; i < m_dispatcher1->getNumManifolds(); i++) {
        const btRigidBody* body0 = reinterpret_cast<const btRigidBody*>(m_dispatcher1->getManifoldByIndexInternal(i)->getBody0());
        const btRigidBody* body1 = reinterpret_cast<const btRigidBody*>(m_dispatcher1->getManifoldByIndexInternal(i)->getBody1());
        m_dispatcher1->getManifoldByIndexInternal(i)->refreshContactPoints(body0->getWorldTransform(), body1->getWorldTransform());
    }

    const btCollisionObjectArray& objects = getCollisionObjectArray();
    for (unsigned int i = 1; i < _objects.size(); ++i) {
        const btTransform&  transform = objects[i]->getWorldTransform();
        const btVector3&    origin    = transform.getOrigin();
        const btQuaternion& rotation  = transform.getRotation();

        const DT_Vector3    position    = {origin[0], origin[1], origin[2]};
        const DT_Quaternion orientation = {rotation[0], rotation[1], rotation[2], rotation[3]};
        DT_SetPosition(_objects[i][0], position);
        DT_SetOrientation(_objects[i][0], orientation);
    }
    DT_Test(_scene, _responseTable);
}
DT_Bool MyClass::CollisionCallback(void* clientData, void* clientObject0, void* clientObject1, const DT_CollData* collData) {
    MyClass* pointer = reinterpret_cast<MyClass*>(clientData);

    unsigned int i = reinterpret_cast<unsigned int>(clientObject0);
    unsigned int j = reinterpret_cast<unsigned int>(clientObject1);

    if (i != j) {
        if (i > j) {
            std::swap(i, j);
        }
        btPersistentManifold* manifold = pointer->m_dispatcher1->getNewManifold(reinterpret_cast<void*>(pointer->getCollisionObjectArray()[i]), reinterpret_cast<void*>(pointer->getCollisionObjectArray()[j]));
        const btVector3 pointA(collData->point1[0], collData->point1[1], collData->point1[2]);
        btVector3 pointB(collData->point2[0], collData->point2[1], collData->point2[2]);
        pointB -= pointer->getCollisionObjectArray()[j]->getWorldTransform().getOrigin();
        const btVector3 normal(collData->normal[0], collData->normal[1], collData->normal[2]);
        manifold->addManifoldPoint(btManifoldPoint(pointA, pointB, normal, 0.0));
    }
    return DT_CONTINUE;
}
addManifoldPoint is correctly called when a collision occurs and pointA and pointB are correct as they are almost the same as the ones generated when I use Bullet. However, the normal isn't the same as Bullet -- it's completely different. I get (-7.8945e-5, -1.0, -3.0847318e-7) with Bullet but (3.0517578e-5, 0.51364136, 3.0517578e-5) with SOLID.

I'm using SOLID's DT_DEPTH_RESPONSE as this is the only response type to generate a normal.

Any information as to why the normals differ would be appreciated.
nerdjock
Posts: 4
Joined: Thu Oct 15, 2009 10:31 am

Re: Replacing narrow phase collision detection

Post by nerdjock »

Hi,

I am also attempting to replace the narrow phase completely, I wish to resolve all contact points in one function call, and not use the Bullet infrastructure. I have decided that I need to override the performDiscreteCollisionDetection() function as above, and then simply manually add points to a persistent manifold. I don't understand exactly how this works though. Are the manifolds created in the narrow phase, or is a manifold created in the broadphase upon finding an overlapping pair? Do I need to clear all the manifolds after each simulation step? Could someone explain in more detail how I create the manifolds, where I should create them, how to update them etc?

I'm very lost on this and I need to get this part done for a project very soon. Please help!

Thanks very much in advance.

Mike