#include "box2d/b2_body.h"
#include "box2d/b2_contact.h"
#include "box2d/b2_contact_manager.h"
#include "box2d/b2_fixture.h"
#include "box2d/b2_world_callbacks.h"
b2ContactFilter b2_defaultFilter;
b2ContactListener b2_defaultListener;
b2ContactManager::b2ContactManager()
{
m_contactList = nullptr;
m_contactCount = 0;
m_contactFilter = &b2_defaultFilter;
m_contactListener = &b2_defaultListener;
m_allocator = nullptr;
}
void b2ContactManager::Destroy(b2Contact* c)
{
b2Fixture* fixtureA = c->GetFixtureA();
b2Fixture* fixtureB = c->GetFixtureB();
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
if (m_contactListener && c->IsTouching())
{
m_contactListener->EndContact(c);
}
if (c->m_prev)
{
c->m_prev->m_next = c->m_next;
}
if (c->m_next)
{
c->m_next->m_prev = c->m_prev;
}
if (c == m_contactList)
{
m_contactList = c->m_next;
}
if (c->m_nodeA.prev)
{
c->m_nodeA.prev->next = c->m_nodeA.next;
}
if (c->m_nodeA.next)
{
c->m_nodeA.next->prev = c->m_nodeA.prev;
}
if (&c->m_nodeA == bodyA->m_contactList)
{
bodyA->m_contactList = c->m_nodeA.next;
}
if (c->m_nodeB.prev)
{
c->m_nodeB.prev->next = c->m_nodeB.next;
}
if (c->m_nodeB.next)
{
c->m_nodeB.next->prev = c->m_nodeB.prev;
}
if (&c->m_nodeB == bodyB->m_contactList)
{
bodyB->m_contactList = c->m_nodeB.next;
}
b2Contact::Destroy(c, m_allocator);
--m_contactCount;
}
void b2ContactManager::Collide()
{
b2Contact* c = m_contactList;
while (c)
{
b2Fixture* fixtureA = c->GetFixtureA();
b2Fixture* fixtureB = c->GetFixtureB();
int32 indexA = c->GetChildIndexA();
int32 indexB = c->GetChildIndexB();
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
if (c->m_flags & b2Contact::e_filterFlag)
{
if (bodyB->ShouldCollide(bodyA) == false)
{
b2Contact* cNuke = c;
c = cNuke->GetNext();
Destroy(cNuke);
continue;
}
if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false)
{
b2Contact* cNuke = c;
c = cNuke->GetNext();
Destroy(cNuke);
continue;
}
c->m_flags &= ~b2Contact::e_filterFlag;
}
bool activeA = bodyA->IsAwake() && bodyA->m_type != b2_staticBody;
bool activeB = bodyB->IsAwake() && bodyB->m_type != b2_staticBody;
if (activeA == false && activeB == false)
{
c = c->GetNext();
continue;
}
int32 proxyIdA = fixtureA->m_proxies[indexA].proxyId;
int32 proxyIdB = fixtureB->m_proxies[indexB].proxyId;
bool overlap = m_broadPhase.TestOverlap(proxyIdA, proxyIdB);
if (overlap == false)
{
b2Contact* cNuke = c;
c = cNuke->GetNext();
Destroy(cNuke);
continue;
}
c->Update(m_contactListener);
c = c->GetNext();
}
}
void b2ContactManager::FindNewContacts()
{
m_broadPhase.UpdatePairs(this);
}
void b2ContactManager::AddPair(void* proxyUserDataA, void* proxyUserDataB)
{
b2FixtureProxy* proxyA = (b2FixtureProxy*)proxyUserDataA;
b2FixtureProxy* proxyB = (b2FixtureProxy*)proxyUserDataB;
b2Fixture* fixtureA = proxyA->fixture;
b2Fixture* fixtureB = proxyB->fixture;
int32 indexA = proxyA->childIndex;
int32 indexB = proxyB->childIndex;
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
if (bodyA == bodyB)
{
return;
}
b2ContactEdge* edge = bodyB->GetContactList();
while (edge)
{
if (edge->other == bodyA)
{
b2Fixture* fA = edge->contact->GetFixtureA();
b2Fixture* fB = edge->contact->GetFixtureB();
int32 iA = edge->contact->GetChildIndexA();
int32 iB = edge->contact->GetChildIndexB();
if (fA == fixtureA && fB == fixtureB && iA == indexA && iB == indexB)
{
return;
}
if (fA == fixtureB && fB == fixtureA && iA == indexB && iB == indexA)
{
return;
}
}
edge = edge->next;
}
if (bodyB->ShouldCollide(bodyA) == false)
{
return;
}
if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false)
{
return;
}
b2Contact* c = b2Contact::Create(fixtureA, indexA, fixtureB, indexB, m_allocator);
if (c == nullptr)
{
return;
}
fixtureA = c->GetFixtureA();
fixtureB = c->GetFixtureB();
bodyA = fixtureA->GetBody();
bodyB = fixtureB->GetBody();
c->m_prev = nullptr;
c->m_next = m_contactList;
if (m_contactList != nullptr)
{
m_contactList->m_prev = c;
}
m_contactList = c;
c->m_nodeA.contact = c;
c->m_nodeA.other = bodyB;
c->m_nodeA.prev = nullptr;
c->m_nodeA.next = bodyA->m_contactList;
if (bodyA->m_contactList != nullptr)
{
bodyA->m_contactList->prev = &c->m_nodeA;
}
bodyA->m_contactList = &c->m_nodeA;
c->m_nodeB.contact = c;
c->m_nodeB.other = bodyA;
c->m_nodeB.prev = nullptr;
c->m_nodeB.next = bodyB->m_contactList;
if (bodyB->m_contactList != nullptr)
{
bodyB->m_contactList->prev = &c->m_nodeB;
}
bodyB->m_contactList = &c->m_nodeB;
++m_contactCount;
}