Bullet doesn't provide a system to generate collision events between objects -- you have to write your own. If you search these forums or other pages you might find examples that people have provided. Here is a modified system that I pulled out of a project I'm working on, which itself was based on an example that someone else provided. I provide it here in case you find it useful.
Please note the following:
(1) I haven't tested this modified version of the code so maybe it has bugs! If so you'll have to find and fix yourself.
(2) It won't even compile if you don't have certain things setup already (e.g. a m_collisionDispatcher in the right context).
(3) The code prints out minimal info about each collision event. Those are the spots in the code where you would modify it to do what you specifically want.
An overview of the code is as follows: after stepSimulation() you walk all the ContactManifolds. For each pair of touching objects you update a class instance that records info about that touch (which is stored in a Map). After updating all contacts for that step you then walk all known contacts in the Map looking for those that were just added (BEGIN), those that have been touching for a while (CONTINUE) and those that are no longer touching (END) which are subsequently removed from the Map.
Put this code in a header or maybe up near the top of the correct cpp file:
Code: Select all
enum ContactEventType {
CONTACT_EVENT_TYPE_START,
CONTACT_EVENT_TYPE_CONTINUE,
CONTACT_EVENT_TYPE_END
};
// simple map key for pair of touching objects
class ContactKey
{
public:
ContactKey(btCollisionObject* a, btCollisionObject* b) : m_a(a), m_b(b) {}
bool operator<(const ContactKey& other) const { return m_a < other.m_a || (m_a == other.m_a && m_b < other.m_b); }
bool operator==(const ContactKey& other) const { return m_a == other.m_a && m_b == other.m_b; }
btCollisionObject* m_a;
btCollisionObject* m_b;
};
// simple class for tracking contact between two objects
class ContactInfo
{
public:
void update(uint32_t currentStep, btManifoldPoint& p)
{
m_lastStep = currentStep;
++m_numCollisions;
// uncomment these if you care about contact details
//m_contactPoint = p.m_positionWorldOnB;
//m_penetration = p.m_distance1 * p.m_normalWorldOnB;
//m_normal = p.m_normalWorldOnB;
}
ContactEventType computeType(uint32_t thisStep) const
{
if (m_lastStep != thisStep)
{
return CONTACT_EVENT_TYPE_END;
}
return (m_numCollisions == 1) ? CONTACT_EVENT_TYPE_START : CONTACT_EVENT_TYPE_CONTINUE;
}
uint32_t m_lastStep = 0;
uint32_t m_numCollisions = 0;
//btVector3 m_contactPoint;
//btVector3 m_penetration;
//btVector3 m_normal;
};
typedef std::map<ContactKey, ContactInfo> ContactMap;
ContactMap m_contactMap;
uint32_t m_numContactFrames = 0;
Put this code after where you call stepSimulation() on the DynamicsWorld. As an optimization: you don't need to call this code unless stepSimulation() actually took at least one real subStep:
Code: Select all
// walk all manifolds and add/update tracked contacts
int numManifolds = m_collisionDispatcher->getNumManifolds();
for (int i = 0; i < numManifolds; ++i)
{
btPersistentManifold* contactManifold = m_collisionDispatcher->getManifoldByIndexInternal(i);
if (contactManifold->getNumContacts() > 0)
{
const btCollisionObject* objectA = static_cast<const btCollisionObject*>(contactManifold->getBody0());
const btCollisionObject* objectB = static_cast<const btCollisionObject*>(contactManifold->getBody1());
if (!(objectA->isActive() || objectB->isActive()))
{
// both objects are inactive so don't update this contact,
// which will trigger a CONTACT_EVENT_TYPE_END later.
continue;
}
m_contactMap[ContactKey(a, b)].update(m_numContactFrames, contactManifold->getContactPoint(0));
}
}
// scan known contacts and trigger events
const uint32_t CONTINUE_EVENT_FILTER_FREQUENCY = 10;
ContactMap::iterator contactItr = m_contactMap.begin();
while (contactItr != m_contactMap.end())
{
btCollisionObject* A = contactItr->first.m_a;
btCollisionObject* B = contactItr->first.m_b;
ContactInfo& contact = contactItr->second;
ContactEventType type = contact.computeType(m_numContactFrames);
if (type == CONTACT_EVENT_TYPE_BEGIN) {
std::cout << A << " and " << B << " START" << std::endl;
}
else if (type == CONTACT_EVENT_TYPE_CONTINUE) {
// only handle CONTINUE events every so often
if (contact.m_numCollisions % CONTINUE_EVENT_FILTER_FREQUENCY == 0)
{
std::cout << A << " and " << B << " CONTINUE " << (contact.m_numCollisions / CONTINUE_EVENT_FILTER_FREQUENCY) << std::endl;
}
}
if (type == CONTACT_EVENT_TYPE_END)
{
std::cout << A << " and " << B << " END";
ContactMap::iterator iterToDelete = contactItr;
++contactItr;
m_contactMap.erase(iterToDelete);
}
else
{
++contactItr;
}
}
++m_numContactFrames;