| /* | |
| * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org | |
| * | |
| * This software is provided 'as-is', without any express or implied | |
| * warranty. In no event will the authors be held liable for any damages | |
| * arising from the use of this software. | |
| * Permission is granted to anyone to use this software for any purpose, | |
| * including commercial applications, and to alter it and redistribute it | |
| * freely, subject to the following restrictions: | |
| * 1. The origin of this software must not be misrepresented; you must not | |
| * claim that you wrote the original software. If you use this software | |
| * in a product, an acknowledgment in the product documentation would be | |
| * appreciated but is not required. | |
| * 2. Altered source versions must be plainly marked as such, and must not be | |
| * misrepresented as being the original software. | |
| * 3. This notice may not be removed or altered from any source distribution. | |
| */ | |
| #include <Box2D/Dynamics/b2ContactManager.h> | |
| #include <Box2D/Dynamics/b2Body.h> | |
| #include <Box2D/Dynamics/b2Fixture.h> | |
| #include <Box2D/Dynamics/b2WorldCallbacks.h> | |
| #include <Box2D/Dynamics/Contacts/b2Contact.h> | |
| b2ContactFilter b2_defaultFilter; | |
| b2ContactListener b2_defaultListener; | |
| b2ContactManager::b2ContactManager() | |
| { | |
| m_contactList = NULL; | |
| m_contactCount = 0; | |
| m_contactFilter = &b2_defaultFilter; | |
| m_contactListener = &b2_defaultListener; | |
| m_allocator = NULL; | |
| } | |
| 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); | |
| } | |
| // Remove from the world. | |
| 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; | |
| } | |
| // Remove from body 1 | |
| 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; | |
| } | |
| // Remove from body 2 | |
| 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; | |
| } | |
| // Call the factory. | |
| b2Contact::Destroy(c, m_allocator); | |
| --m_contactCount; | |
| } | |
| // This is the top level collision call for the time step. Here | |
| // all the narrow phase collision is processed for the world | |
| // contact list. | |
| void b2ContactManager::Collide() | |
| { | |
| // Update awake contacts. | |
| 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(); | |
| // Is this contact flagged for filtering? | |
| if (c->m_flags & b2Contact::e_filterFlag) | |
| { | |
| // Should these bodies collide? | |
| if (bodyB->ShouldCollide(bodyA) == false) | |
| { | |
| b2Contact* cNuke = c; | |
| c = cNuke->GetNext(); | |
| Destroy(cNuke); | |
| continue; | |
| } | |
| // Check user filtering. | |
| if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) | |
| { | |
| b2Contact* cNuke = c; | |
| c = cNuke->GetNext(); | |
| Destroy(cNuke); | |
| continue; | |
| } | |
| // Clear the filtering flag. | |
| c->m_flags &= ~b2Contact::e_filterFlag; | |
| } | |
| bool activeA = bodyA->IsAwake() && bodyA->m_type != b2_staticBody; | |
| bool activeB = bodyB->IsAwake() && bodyB->m_type != b2_staticBody; | |
| // At least one body must be awake and it must be dynamic or kinematic. | |
| 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); | |
| // Here we destroy contacts that cease to overlap in the broad-phase. | |
| if (overlap == false) | |
| { | |
| b2Contact* cNuke = c; | |
| c = cNuke->GetNext(); | |
| Destroy(cNuke); | |
| continue; | |
| } | |
| // The contact persists. | |
| 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(); | |
| // Are the fixtures on the same body? | |
| if (bodyA == bodyB) | |
| { | |
| return; | |
| } | |
| // TODO_ERIN use a hash table to remove a potential bottleneck when both | |
| // bodies have a lot of contacts. | |
| // Does a contact already exist? | |
| 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) | |
| { | |
| // A contact already exists. | |
| return; | |
| } | |
| if (fA == fixtureB && fB == fixtureA && iA == indexB && iB == indexA) | |
| { | |
| // A contact already exists. | |
| return; | |
| } | |
| } | |
| edge = edge->next; | |
| } | |
| // Does a joint override collision? Is at least one body dynamic? | |
| if (bodyB->ShouldCollide(bodyA) == false) | |
| { | |
| return; | |
| } | |
| // Check user filtering. | |
| if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) | |
| { | |
| return; | |
| } | |
| // Call the factory. | |
| b2Contact* c = b2Contact::Create(fixtureA, indexA, fixtureB, indexB, m_allocator); | |
| if (c == NULL) | |
| { | |
| return; | |
| } | |
| // Contact creation may swap fixtures. | |
| fixtureA = c->GetFixtureA(); | |
| fixtureB = c->GetFixtureB(); | |
| indexA = c->GetChildIndexA(); | |
| indexB = c->GetChildIndexB(); | |
| bodyA = fixtureA->GetBody(); | |
| bodyB = fixtureB->GetBody(); | |
| // Insert into the world. | |
| c->m_prev = NULL; | |
| c->m_next = m_contactList; | |
| if (m_contactList != NULL) | |
| { | |
| m_contactList->m_prev = c; | |
| } | |
| m_contactList = c; | |
| // Connect to island graph. | |
| // Connect to body A | |
| c->m_nodeA.contact = c; | |
| c->m_nodeA.other = bodyB; | |
| c->m_nodeA.prev = NULL; | |
| c->m_nodeA.next = bodyA->m_contactList; | |
| if (bodyA->m_contactList != NULL) | |
| { | |
| bodyA->m_contactList->prev = &c->m_nodeA; | |
| } | |
| bodyA->m_contactList = &c->m_nodeA; | |
| // Connect to body B | |
| c->m_nodeB.contact = c; | |
| c->m_nodeB.other = bodyA; | |
| c->m_nodeB.prev = NULL; | |
| c->m_nodeB.next = bodyB->m_contactList; | |
| if (bodyB->m_contactList != NULL) | |
| { | |
| bodyB->m_contactList->prev = &c->m_nodeB; | |
| } | |
| bodyB->m_contactList = &c->m_nodeB; | |
| // Wake up the bodies | |
| bodyA->SetAwake(true); | |
| bodyB->SetAwake(true); | |
| ++m_contactCount; | |
| } |