7
\$\begingroup\$

I have got problem implementing collision callback into my project. I would like to have detection between two specific objects. I have got normall collision but I want one object to stop or change color or whatever when colides with another. I wrote code from bullet wiki:

int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
    for (int i=0;i<numManifolds;i++)
    {
        btPersistentManifold* contactManifold =  dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);
        btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0());
        btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1());

        int numContacts = contactManifold->getNumContacts();
        for (int j=0;j<numContacts;j++)
        {
            btManifoldPoint& pt = contactManifold->getContactPoint(j);
            if (pt.getDistance()<0.f)
            {
                const btVector3& ptA = pt.getPositionWorldOnA();
                const btVector3& ptB = pt.getPositionWorldOnB();
                const btVector3& normalOnB = pt.m_normalWorldOnB;
                bool x = (ContactProcessedCallback)(pt,fallRigidBody,earthRigidBody);
                if(x)
                    printf("collision\n");
            }
        }
    }

where fallRigidBody is a dynamic body - a sphere and earthRigiBody is static body - StaticPlaneShape and sphere isn't touching earthRigidBody all the time. I have got also other objects that are colliding with sphere and it works fine. But the program detects collision all the time. It doesn't matter if the objects are or aren't colliding.

I have also added after declarations of rigid body:

    earthRigidBody->setCollisionFlags(earthRigidBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
fallRigidBody->setCollisionFlags(fallRigidBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);

So can someone tell me what I am doing wrong? Maybe it is something simple?

\$\endgroup\$
1

2 Answers 2

4
\$\begingroup\$

I'm familiar with JBullet but as its the java port of the original the concept should be the same.

It seemed to me that there's always a collision with your static plane and other objects. But you need to do something only on collision of specific objects. What you need to use is the user pointer of the collision shape. Here's a piece of code from my own engine in java. Here GameEntity is the class I wrap my objects and CollisionListener is another class of mine which I use to implement what collision does. I also pass positions and collision normal to the listener to achive more fancy effects.

            if (pt.getDistance() < 0.f) {
                Vector3f ptA = pt.getPositionWorldOnA(new Vector3f());
                Vector3f ptB = pt.getPositionWorldOnB(new Vector3f());
                Vector3f normalOnB = pt.normalWorldOnB;
                GameEntity thisObject = (GameEntity) (obA.getUserPointer());
                GameEntity thatObject = (GameEntity) (obB.getUserPointer());
                if(thisObject == null){
                    return;
                }
                if (thisObject.getCollisionListener() != null) {
                    thisObject.getCollisionListener().collided(
                            ptA, ptB, normalOnB, thatObject);
                }
                if (thatObject.getCollisionListener() != null) {
                    thatObject.getCollisionListener().collided(
                            ptB, ptA, normalOnB, thisObject);
                }
            }
\$\endgroup\$
0
\$\begingroup\$

Minimal runnable example

Two spheres falling from different heights in function of time.

Since they start at different heights, they will hit the ground at different times.

We detect the ground hit, and print out which sphere touched the ground when.

Gnuplot visualization of stdout:

See how:

  • when one sphere hits the ground, the pink square collision indicator goes to 1
  • when the other sphere hits the ground, the green X goes to 1

Code:

#include <cstdio>
#include <cstdlib>
#include <map>
#include <vector>

#include <btBulletDynamicsCommon.h>

#define PRINTF_FLOAT "%7.3f"

constexpr float gravity = -10.0f;
constexpr float timeStep = 1.0f / 60.0f;
constexpr float groundRestitution = 0.9f;
constexpr float objectRestitution = 0.9f;
constexpr int maxNPoints = 500;
constexpr float initialXs[] = {  0.0f, 5.0f };
constexpr float initialYs[] = { 10.0f, 5.0f };
constexpr size_t nObjects = sizeof(initialYs) / sizeof(*initialYs);

std::map<const btCollisionObject*,std::vector<btManifoldPoint*>> objectsCollisions;
void myTickCallback(btDynamicsWorld *dynamicsWorld, btScalar timeStep) {
    objectsCollisions.clear();
    int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
    for (int i = 0; i < numManifolds; i++) {
        btPersistentManifold *contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);
        auto *objA = contactManifold->getBody0();
        auto *objB = contactManifold->getBody1();
        auto& collisionsA = objectsCollisions[objA];
        auto& collisionsB = objectsCollisions[objB];
        int numContacts = contactManifold->getNumContacts();
        for (int j = 0; j < numContacts; j++) {
            btManifoldPoint& pt = contactManifold->getContactPoint(j);
            collisionsA.push_back(&pt);
            collisionsB.push_back(&pt);
        }
    }
}

int main() {
    int i, j;

    btDefaultCollisionConfiguration *collisionConfiguration
            = new btDefaultCollisionConfiguration();
    btCollisionDispatcher *dispatcher = new btCollisionDispatcher(collisionConfiguration);
    btBroadphaseInterface *overlappingPairCache = new btDbvtBroadphase();
    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
    btDiscreteDynamicsWorld *dynamicsWorld = new btDiscreteDynamicsWorld(
            dispatcher, overlappingPairCache, solver, collisionConfiguration);
    dynamicsWorld->setGravity(btVector3(0, gravity, 0));
    dynamicsWorld->setInternalTickCallback(myTickCallback);
    btAlignedObjectArray<btCollisionShape*> collisionShapes;

    // Ground.
    {
        btTransform groundTransform;
        groundTransform.setIdentity();
        groundTransform.setOrigin(btVector3(0, 0, 0));
        btCollisionShape* groundShape;
        groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), -1);
        collisionShapes.push_back(groundShape);
        btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
        btRigidBody::btRigidBodyConstructionInfo rbInfo(0, myMotionState, groundShape, btVector3(0, 0, 0));
        btRigidBody* body = new btRigidBody(rbInfo);
        body->setRestitution(groundRestitution);
        dynamicsWorld->addRigidBody(body);
    }

    // Objects.
    for (size_t i = 0; i < nObjects; ++i) {
        btCollisionShape *colShape;
        colShape = new btSphereShape(btScalar(1.0));
        collisionShapes.push_back(colShape);
        btTransform startTransform;
        startTransform.setIdentity();
        startTransform.setOrigin(btVector3(initialXs[i], initialYs[i], 0));
        btVector3 localInertia(0, 0, 0);
        btScalar mass(1.0f);
        colShape->calculateLocalInertia(mass, localInertia);
        btDefaultMotionState *myMotionState = new btDefaultMotionState(startTransform);
        btRigidBody *body = new btRigidBody(btRigidBody::btRigidBodyConstructionInfo(
                mass, myMotionState, colShape, localInertia));
        body->setRestitution(objectRestitution);
        dynamicsWorld->addRigidBody(body);
    }

    // Main loop.
    std::printf("step body x y z collision2 collision1\n");
    for (i = 0; i < maxNPoints; ++i) {
        dynamicsWorld->stepSimulation(timeStep);
        for (j = dynamicsWorld->getNumCollisionObjects() - 1; j >= 0; --j) {
            btCollisionObject *obj = dynamicsWorld->getCollisionObjectArray()[j];
            btRigidBody *body = btRigidBody::upcast(obj);
            btTransform trans;
            if (body && body->getMotionState()) {
                body->getMotionState()->getWorldTransform(trans);
            } else {
                trans = obj->getWorldTransform();
            }
            btVector3 origin = trans.getOrigin();
            std::printf("%d %d " PRINTF_FLOAT " " PRINTF_FLOAT " " PRINTF_FLOAT " ",
                    i, j, origin.getX(), origin.getY(), origin.getZ());
            auto& manifoldPoints = objectsCollisions[body];
            if (manifoldPoints.empty()) {
                std::printf("0");
            } else {
                std::printf("1");
            }
            puts("");
        }
    }

    // Cleanup.
    for (i = dynamicsWorld->getNumCollisionObjects() - 1; i >= 0; --i) {
        btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
        btRigidBody* body = btRigidBody::upcast(obj);
        if (body && body->getMotionState()) {
            delete body->getMotionState();
        }
        dynamicsWorld->removeCollisionObject(obj);
        delete obj;
    }
    for (i = 0; i < collisionShapes.size(); ++i) {
        delete collisionShapes[i];
    }
    delete dynamicsWorld;
    delete solver;
    delete overlappingPairCache;
    delete dispatcher;
    delete collisionConfiguration;
    collisionShapes.clear();
}

Tested on Bullet 2.83, Ubuntu 15.10.

GitHub upstream: https://github.com/cirosantilli/cpp-cheat/blob/d7b70153b8f86b5864c22fbc6f7005049a93491f/bullet/which_collision.cpp

\$\endgroup\$
3
  • \$\begingroup\$ @Downvoters, please explain so I can learn and improve ;-) \$\endgroup\$ Commented Jul 26, 2017 at 20:10
  • \$\begingroup\$ I think your answer and example is a bit long. Can you reduce it to just show a specific answer to the question? \$\endgroup\$ Commented Jun 13, 2020 at 12:45
  • \$\begingroup\$ @SteveSmith I think the answer is perfect :-) \$\endgroup\$ Commented Jun 13, 2020 at 14:00

You must log in to answer this question.

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