diff options
Diffstat (limited to 'Source/external/Box2D/Dynamics/b2ContactManager.cpp')
-rw-r--r-- | Source/external/Box2D/Dynamics/b2ContactManager.cpp | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/Source/external/Box2D/Dynamics/b2ContactManager.cpp b/Source/external/Box2D/Dynamics/b2ContactManager.cpp new file mode 100644 index 0000000..051cc2f --- /dev/null +++ b/Source/external/Box2D/Dynamics/b2ContactManager.cpp @@ -0,0 +1,296 @@ +/* +* 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 = 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); + } + + // 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 == nullptr) + { + 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 = nullptr; + c->m_next = m_contactList; + if (m_contactList != nullptr) + { + 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 = 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; + + // Connect to body B + 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; + + // Wake up the bodies + if (fixtureA->IsSensor() == false && fixtureB->IsSensor() == false) + { + bodyA->SetAwake(true); + bodyB->SetAwake(true); + } + + ++m_contactCount; +} |