mirror of https://github.com/axmolengine/axmol.git
1077 lines
24 KiB
C++
1077 lines
24 KiB
C++
/*
|
|
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
|
|
*
|
|
* 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/b2World.h>
|
|
#include <Box2D/Dynamics/b2Body.h>
|
|
#include <Box2D/Dynamics/b2Fixture.h>
|
|
#include <Box2D/Dynamics/b2Island.h>
|
|
#include <Box2D/Dynamics/Joints/b2PulleyJoint.h>
|
|
#include <Box2D/Dynamics/Contacts/b2Contact.h>
|
|
#include <Box2D/Dynamics/Contacts/b2ContactSolver.h>
|
|
#include <Box2D/Dynamics/Contacts/b2TOISolver.h>
|
|
#include <Box2D/Collision/b2Collision.h>
|
|
#include <Box2D/Collision/b2BroadPhase.h>
|
|
#include <Box2D/Collision/Shapes/b2CircleShape.h>
|
|
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
|
|
#include <Box2D/Collision/b2TimeOfImpact.h>
|
|
#include <new>
|
|
|
|
b2World::b2World(const b2Vec2& gravity, bool doSleep)
|
|
{
|
|
m_destructionListener = NULL;
|
|
m_debugDraw = NULL;
|
|
|
|
m_bodyList = NULL;
|
|
m_jointList = NULL;
|
|
|
|
m_bodyCount = 0;
|
|
m_jointCount = 0;
|
|
|
|
m_warmStarting = true;
|
|
m_continuousPhysics = true;
|
|
|
|
m_allowSleep = doSleep;
|
|
m_gravity = gravity;
|
|
|
|
m_flags = e_clearForces;
|
|
|
|
m_inv_dt0 = 0.0f;
|
|
|
|
m_contactManager.m_allocator = &m_blockAllocator;
|
|
}
|
|
|
|
b2World::~b2World()
|
|
{
|
|
}
|
|
|
|
void b2World::SetDestructionListener(b2DestructionListener* listener)
|
|
{
|
|
m_destructionListener = listener;
|
|
}
|
|
|
|
void b2World::SetContactFilter(b2ContactFilter* filter)
|
|
{
|
|
m_contactManager.m_contactFilter = filter;
|
|
}
|
|
|
|
void b2World::SetContactListener(b2ContactListener* listener)
|
|
{
|
|
m_contactManager.m_contactListener = listener;
|
|
}
|
|
|
|
void b2World::SetDebugDraw(b2DebugDraw* debugDraw)
|
|
{
|
|
m_debugDraw = debugDraw;
|
|
}
|
|
|
|
b2Body* b2World::CreateBody(const b2BodyDef* def)
|
|
{
|
|
b2Assert(IsLocked() == false);
|
|
if (IsLocked())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void* mem = m_blockAllocator.Allocate(sizeof(b2Body));
|
|
b2Body* b = new (mem) b2Body(def, this);
|
|
|
|
// Add to world doubly linked list.
|
|
b->m_prev = NULL;
|
|
b->m_next = m_bodyList;
|
|
if (m_bodyList)
|
|
{
|
|
m_bodyList->m_prev = b;
|
|
}
|
|
m_bodyList = b;
|
|
++m_bodyCount;
|
|
|
|
return b;
|
|
}
|
|
|
|
void b2World::DestroyBody(b2Body* b)
|
|
{
|
|
b2Assert(m_bodyCount > 0);
|
|
b2Assert(IsLocked() == false);
|
|
if (IsLocked())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Delete the attached joints.
|
|
b2JointEdge* je = b->m_jointList;
|
|
while (je)
|
|
{
|
|
b2JointEdge* je0 = je;
|
|
je = je->next;
|
|
|
|
if (m_destructionListener)
|
|
{
|
|
m_destructionListener->SayGoodbye(je0->joint);
|
|
}
|
|
|
|
DestroyJoint(je0->joint);
|
|
}
|
|
b->m_jointList = NULL;
|
|
|
|
// Delete the attached contacts.
|
|
b2ContactEdge* ce = b->m_contactList;
|
|
while (ce)
|
|
{
|
|
b2ContactEdge* ce0 = ce;
|
|
ce = ce->next;
|
|
m_contactManager.Destroy(ce0->contact);
|
|
}
|
|
b->m_contactList = NULL;
|
|
|
|
// Delete the attached fixtures. This destroys broad-phase proxies.
|
|
b2Fixture* f = b->m_fixtureList;
|
|
while (f)
|
|
{
|
|
b2Fixture* f0 = f;
|
|
f = f->m_next;
|
|
|
|
if (m_destructionListener)
|
|
{
|
|
m_destructionListener->SayGoodbye(f0);
|
|
}
|
|
|
|
f0->DestroyProxy(&m_contactManager.m_broadPhase);
|
|
f0->Destroy(&m_blockAllocator);
|
|
f0->~b2Fixture();
|
|
m_blockAllocator.Free(f0, sizeof(b2Fixture));
|
|
}
|
|
b->m_fixtureList = NULL;
|
|
b->m_fixtureCount = 0;
|
|
|
|
// Remove world body list.
|
|
if (b->m_prev)
|
|
{
|
|
b->m_prev->m_next = b->m_next;
|
|
}
|
|
|
|
if (b->m_next)
|
|
{
|
|
b->m_next->m_prev = b->m_prev;
|
|
}
|
|
|
|
if (b == m_bodyList)
|
|
{
|
|
m_bodyList = b->m_next;
|
|
}
|
|
|
|
--m_bodyCount;
|
|
b->~b2Body();
|
|
m_blockAllocator.Free(b, sizeof(b2Body));
|
|
}
|
|
|
|
b2Joint* b2World::CreateJoint(const b2JointDef* def)
|
|
{
|
|
b2Assert(IsLocked() == false);
|
|
if (IsLocked())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b2Joint* j = b2Joint::Create(def, &m_blockAllocator);
|
|
|
|
// Connect to the world list.
|
|
j->m_prev = NULL;
|
|
j->m_next = m_jointList;
|
|
if (m_jointList)
|
|
{
|
|
m_jointList->m_prev = j;
|
|
}
|
|
m_jointList = j;
|
|
++m_jointCount;
|
|
|
|
// Connect to the bodies' doubly linked lists.
|
|
j->m_edgeA.joint = j;
|
|
j->m_edgeA.other = j->m_bodyB;
|
|
j->m_edgeA.prev = NULL;
|
|
j->m_edgeA.next = j->m_bodyA->m_jointList;
|
|
if (j->m_bodyA->m_jointList) j->m_bodyA->m_jointList->prev = &j->m_edgeA;
|
|
j->m_bodyA->m_jointList = &j->m_edgeA;
|
|
|
|
j->m_edgeB.joint = j;
|
|
j->m_edgeB.other = j->m_bodyA;
|
|
j->m_edgeB.prev = NULL;
|
|
j->m_edgeB.next = j->m_bodyB->m_jointList;
|
|
if (j->m_bodyB->m_jointList) j->m_bodyB->m_jointList->prev = &j->m_edgeB;
|
|
j->m_bodyB->m_jointList = &j->m_edgeB;
|
|
|
|
b2Body* bodyA = def->bodyA;
|
|
b2Body* bodyB = def->bodyB;
|
|
|
|
// If the joint prevents collisions, then flag any contacts for filtering.
|
|
if (def->collideConnected == false)
|
|
{
|
|
b2ContactEdge* edge = bodyB->GetContactList();
|
|
while (edge)
|
|
{
|
|
if (edge->other == bodyA)
|
|
{
|
|
// Flag the contact for filtering at the next time step (where either
|
|
// body is awake).
|
|
edge->contact->FlagForFiltering();
|
|
}
|
|
|
|
edge = edge->next;
|
|
}
|
|
}
|
|
|
|
// Note: creating a joint doesn't wake the bodies.
|
|
|
|
return j;
|
|
}
|
|
|
|
void b2World::DestroyJoint(b2Joint* j)
|
|
{
|
|
b2Assert(IsLocked() == false);
|
|
if (IsLocked())
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool collideConnected = j->m_collideConnected;
|
|
|
|
// Remove from the doubly linked list.
|
|
if (j->m_prev)
|
|
{
|
|
j->m_prev->m_next = j->m_next;
|
|
}
|
|
|
|
if (j->m_next)
|
|
{
|
|
j->m_next->m_prev = j->m_prev;
|
|
}
|
|
|
|
if (j == m_jointList)
|
|
{
|
|
m_jointList = j->m_next;
|
|
}
|
|
|
|
// Disconnect from island graph.
|
|
b2Body* bodyA = j->m_bodyA;
|
|
b2Body* bodyB = j->m_bodyB;
|
|
|
|
// Wake up connected bodies.
|
|
bodyA->SetAwake(true);
|
|
bodyB->SetAwake(true);
|
|
|
|
// Remove from body 1.
|
|
if (j->m_edgeA.prev)
|
|
{
|
|
j->m_edgeA.prev->next = j->m_edgeA.next;
|
|
}
|
|
|
|
if (j->m_edgeA.next)
|
|
{
|
|
j->m_edgeA.next->prev = j->m_edgeA.prev;
|
|
}
|
|
|
|
if (&j->m_edgeA == bodyA->m_jointList)
|
|
{
|
|
bodyA->m_jointList = j->m_edgeA.next;
|
|
}
|
|
|
|
j->m_edgeA.prev = NULL;
|
|
j->m_edgeA.next = NULL;
|
|
|
|
// Remove from body 2
|
|
if (j->m_edgeB.prev)
|
|
{
|
|
j->m_edgeB.prev->next = j->m_edgeB.next;
|
|
}
|
|
|
|
if (j->m_edgeB.next)
|
|
{
|
|
j->m_edgeB.next->prev = j->m_edgeB.prev;
|
|
}
|
|
|
|
if (&j->m_edgeB == bodyB->m_jointList)
|
|
{
|
|
bodyB->m_jointList = j->m_edgeB.next;
|
|
}
|
|
|
|
j->m_edgeB.prev = NULL;
|
|
j->m_edgeB.next = NULL;
|
|
|
|
b2Joint::Destroy(j, &m_blockAllocator);
|
|
|
|
b2Assert(m_jointCount > 0);
|
|
--m_jointCount;
|
|
|
|
// If the joint prevents collisions, then flag any contacts for filtering.
|
|
if (collideConnected == false)
|
|
{
|
|
b2ContactEdge* edge = bodyB->GetContactList();
|
|
while (edge)
|
|
{
|
|
if (edge->other == bodyA)
|
|
{
|
|
// Flag the contact for filtering at the next time step (where either
|
|
// body is awake).
|
|
edge->contact->FlagForFiltering();
|
|
}
|
|
|
|
edge = edge->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find islands, integrate and solve constraints, solve position constraints
|
|
void b2World::Solve(const b2TimeStep& step)
|
|
{
|
|
// Size the island for the worst case.
|
|
b2Island island(m_bodyCount,
|
|
m_contactManager.m_contactCount,
|
|
m_jointCount,
|
|
&m_stackAllocator,
|
|
m_contactManager.m_contactListener);
|
|
|
|
// Clear all the island flags.
|
|
for (b2Body* b = m_bodyList; b; b = b->m_next)
|
|
{
|
|
b->m_flags &= ~b2Body::e_islandFlag;
|
|
}
|
|
for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next)
|
|
{
|
|
c->m_flags &= ~b2Contact::e_islandFlag;
|
|
}
|
|
for (b2Joint* j = m_jointList; j; j = j->m_next)
|
|
{
|
|
j->m_islandFlag = false;
|
|
}
|
|
|
|
// Build and simulate all awake islands.
|
|
int32 stackSize = m_bodyCount;
|
|
b2Body** stack = (b2Body**)m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
|
|
for (b2Body* seed = m_bodyList; seed; seed = seed->m_next)
|
|
{
|
|
if (seed->m_flags & b2Body::e_islandFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (seed->IsAwake() == false || seed->IsActive() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// The seed can be dynamic or kinematic.
|
|
if (seed->GetType() == b2_staticBody)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Reset island and stack.
|
|
island.Clear();
|
|
int32 stackCount = 0;
|
|
stack[stackCount++] = seed;
|
|
seed->m_flags |= b2Body::e_islandFlag;
|
|
|
|
// Perform a depth first search (DFS) on the constraint graph.
|
|
while (stackCount > 0)
|
|
{
|
|
// Grab the next body off the stack and add it to the island.
|
|
b2Body* b = stack[--stackCount];
|
|
b2Assert(b->IsActive() == true);
|
|
island.Add(b);
|
|
|
|
// Make sure the body is awake.
|
|
b->SetAwake(true);
|
|
|
|
// To keep islands as small as possible, we don't
|
|
// propagate islands across static bodies.
|
|
if (b->GetType() == b2_staticBody)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Search all contacts connected to this body.
|
|
for (b2ContactEdge* ce = b->m_contactList; ce; ce = ce->next)
|
|
{
|
|
b2Contact* contact = ce->contact;
|
|
|
|
// Has this contact already been added to an island?
|
|
if (contact->m_flags & b2Contact::e_islandFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Is this contact solid and touching?
|
|
if (contact->IsEnabled() == false ||
|
|
contact->IsTouching() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Skip sensors.
|
|
bool sensorA = contact->m_fixtureA->m_isSensor;
|
|
bool sensorB = contact->m_fixtureB->m_isSensor;
|
|
if (sensorA || sensorB)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
island.Add(contact);
|
|
contact->m_flags |= b2Contact::e_islandFlag;
|
|
|
|
b2Body* other = ce->other;
|
|
|
|
// Was the other body already added to this island?
|
|
if (other->m_flags & b2Body::e_islandFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Assert(stackCount < stackSize);
|
|
stack[stackCount++] = other;
|
|
other->m_flags |= b2Body::e_islandFlag;
|
|
}
|
|
|
|
// Search all joints connect to this body.
|
|
for (b2JointEdge* je = b->m_jointList; je; je = je->next)
|
|
{
|
|
if (je->joint->m_islandFlag == true)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Body* other = je->other;
|
|
|
|
// Don't simulate joints connected to inactive bodies.
|
|
if (other->IsActive() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
island.Add(je->joint);
|
|
je->joint->m_islandFlag = true;
|
|
|
|
if (other->m_flags & b2Body::e_islandFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Assert(stackCount < stackSize);
|
|
stack[stackCount++] = other;
|
|
other->m_flags |= b2Body::e_islandFlag;
|
|
}
|
|
}
|
|
|
|
island.Solve(step, m_gravity, m_allowSleep);
|
|
|
|
// Post solve cleanup.
|
|
for (int32 i = 0; i < island.m_bodyCount; ++i)
|
|
{
|
|
// Allow static bodies to participate in other islands.
|
|
b2Body* b = island.m_bodies[i];
|
|
if (b->GetType() == b2_staticBody)
|
|
{
|
|
b->m_flags &= ~b2Body::e_islandFlag;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_stackAllocator.Free(stack);
|
|
|
|
// Synchronize fixtures, check for out of range bodies.
|
|
for (b2Body* b = m_bodyList; b; b = b->GetNext())
|
|
{
|
|
// If a body was not in an island then it did not move.
|
|
if ((b->m_flags & b2Body::e_islandFlag) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (b->GetType() == b2_staticBody)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Update fixtures (for broad-phase).
|
|
b->SynchronizeFixtures();
|
|
}
|
|
|
|
// Look for new contacts.
|
|
m_contactManager.FindNewContacts();
|
|
}
|
|
|
|
// Advance a dynamic body to its first time of contact
|
|
// and adjust the position to ensure clearance.
|
|
void b2World::SolveTOI(b2Body* body)
|
|
{
|
|
// Find the minimum contact.
|
|
b2Contact* toiContact = NULL;
|
|
float32 toi = 1.0f;
|
|
b2Body* toiOther = NULL;
|
|
bool found;
|
|
int32 count;
|
|
int32 iter = 0;
|
|
|
|
bool bullet = body->IsBullet();
|
|
|
|
// Iterate until all contacts agree on the minimum TOI. We have
|
|
// to iterate because the TOI algorithm may skip some intermediate
|
|
// collisions when objects rotate through each other.
|
|
do
|
|
{
|
|
count = 0;
|
|
found = false;
|
|
for (b2ContactEdge* ce = body->m_contactList; ce; ce = ce->next)
|
|
{
|
|
if (ce->contact == toiContact)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Body* other = ce->other;
|
|
b2BodyType type = other->GetType();
|
|
|
|
// Only bullets perform TOI with dynamic bodies.
|
|
if (bullet == true)
|
|
{
|
|
// Bullets only perform TOI with bodies that have their TOI resolved.
|
|
if ((other->m_flags & b2Body::e_toiFlag) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// No repeated hits on non-static bodies
|
|
if (type != b2_staticBody && (ce->contact->m_flags & b2Contact::e_bulletHitFlag) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (type == b2_dynamicBody)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check for a disabled contact.
|
|
b2Contact* contact = ce->contact;
|
|
if (contact->IsEnabled() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Prevent infinite looping.
|
|
if (contact->m_toiCount > 10)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Fixture* fixtureA = contact->m_fixtureA;
|
|
b2Fixture* fixtureB = contact->m_fixtureB;
|
|
|
|
// Cull sensors.
|
|
if (fixtureA->IsSensor() || fixtureB->IsSensor())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Body* bodyA = fixtureA->m_body;
|
|
b2Body* bodyB = fixtureB->m_body;
|
|
|
|
// Compute the time of impact in interval [0, minTOI]
|
|
b2TOIInput input;
|
|
input.proxyA.Set(fixtureA->GetShape());
|
|
input.proxyB.Set(fixtureB->GetShape());
|
|
input.sweepA = bodyA->m_sweep;
|
|
input.sweepB = bodyB->m_sweep;
|
|
input.tMax = toi;
|
|
|
|
b2TOIOutput output;
|
|
b2TimeOfImpact(&output, &input);
|
|
|
|
if (output.state == b2TOIOutput::e_touching && output.t < toi)
|
|
{
|
|
toiContact = contact;
|
|
toi = output.t;
|
|
toiOther = other;
|
|
found = true;
|
|
}
|
|
|
|
++count;
|
|
}
|
|
|
|
++iter;
|
|
} while (found && count > 1 && iter < 50);
|
|
|
|
if (toiContact == NULL)
|
|
{
|
|
body->Advance(1.0f);
|
|
return;
|
|
}
|
|
|
|
b2Sweep backup = body->m_sweep;
|
|
body->Advance(toi);
|
|
toiContact->Update(m_contactManager.m_contactListener);
|
|
if (toiContact->IsEnabled() == false)
|
|
{
|
|
// Contact disabled. Backup and recurse.
|
|
body->m_sweep = backup;
|
|
SolveTOI(body);
|
|
}
|
|
|
|
++toiContact->m_toiCount;
|
|
|
|
// Update all the valid contacts on this body and build a contact island.
|
|
b2Contact* contacts[b2_maxTOIContacts];
|
|
count = 0;
|
|
for (b2ContactEdge* ce = body->m_contactList; ce && count < b2_maxTOIContacts; ce = ce->next)
|
|
{
|
|
b2Body* other = ce->other;
|
|
b2BodyType type = other->GetType();
|
|
|
|
// Only perform correction with static bodies, so the
|
|
// body won't get pushed out of the world.
|
|
if (type == b2_dynamicBody)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check for a disabled contact.
|
|
b2Contact* contact = ce->contact;
|
|
if (contact->IsEnabled() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b2Fixture* fixtureA = contact->m_fixtureA;
|
|
b2Fixture* fixtureB = contact->m_fixtureB;
|
|
|
|
// Cull sensors.
|
|
if (fixtureA->IsSensor() || fixtureB->IsSensor())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// The contact likely has some new contact points. The listener
|
|
// gives the user a chance to disable the contact.
|
|
if (contact != toiContact)
|
|
{
|
|
contact->Update(m_contactManager.m_contactListener);
|
|
}
|
|
|
|
// Did the user disable the contact?
|
|
if (contact->IsEnabled() == false)
|
|
{
|
|
// Skip this contact.
|
|
continue;
|
|
}
|
|
|
|
if (contact->IsTouching() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
contacts[count] = contact;
|
|
++count;
|
|
}
|
|
|
|
// Reduce the TOI body's overlap with the contact island.
|
|
b2TOISolver solver(&m_stackAllocator);
|
|
solver.Initialize(contacts, count, body);
|
|
|
|
const float32 k_toiBaumgarte = 0.75f;
|
|
bool solved = false;
|
|
for (int32 i = 0; i < 20; ++i)
|
|
{
|
|
bool contactsOkay = solver.Solve(k_toiBaumgarte);
|
|
if (contactsOkay)
|
|
{
|
|
solved = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (toiOther->GetType() != b2_staticBody)
|
|
{
|
|
toiContact->m_flags |= b2Contact::e_bulletHitFlag;
|
|
}
|
|
}
|
|
|
|
// Sequentially solve TOIs for each body. We bring each
|
|
// body to the time of contact and perform some position correction.
|
|
// Time is not conserved.
|
|
void b2World::SolveTOI()
|
|
{
|
|
// Prepare all contacts.
|
|
for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next)
|
|
{
|
|
// Enable the contact
|
|
c->m_flags |= b2Contact::e_enabledFlag;
|
|
|
|
// Set the number of TOI events for this contact to zero.
|
|
c->m_toiCount = 0;
|
|
}
|
|
|
|
// Initialize the TOI flag.
|
|
for (b2Body* body = m_bodyList; body; body = body->m_next)
|
|
{
|
|
// Kinematic, and static bodies will not be affected by the TOI event.
|
|
// If a body was not in an island then it did not move.
|
|
if ((body->m_flags & b2Body::e_islandFlag) == 0 || body->GetType() == b2_kinematicBody || body->GetType() == b2_staticBody)
|
|
{
|
|
body->m_flags |= b2Body::e_toiFlag;
|
|
}
|
|
else
|
|
{
|
|
body->m_flags &= ~b2Body::e_toiFlag;
|
|
}
|
|
}
|
|
|
|
// Collide non-bullets.
|
|
for (b2Body* body = m_bodyList; body; body = body->m_next)
|
|
{
|
|
if (body->m_flags & b2Body::e_toiFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (body->IsBullet() == true)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
SolveTOI(body);
|
|
|
|
body->m_flags |= b2Body::e_toiFlag;
|
|
}
|
|
|
|
// Collide bullets.
|
|
for (b2Body* body = m_bodyList; body; body = body->m_next)
|
|
{
|
|
if (body->m_flags & b2Body::e_toiFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (body->IsBullet() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
SolveTOI(body);
|
|
|
|
body->m_flags |= b2Body::e_toiFlag;
|
|
}
|
|
}
|
|
|
|
void b2World::Step(float32 dt, int32 velocityIterations, int32 positionIterations)
|
|
{
|
|
// If new fixtures were added, we need to find the new contacts.
|
|
if (m_flags & e_newFixture)
|
|
{
|
|
m_contactManager.FindNewContacts();
|
|
m_flags &= ~e_newFixture;
|
|
}
|
|
|
|
m_flags |= e_locked;
|
|
|
|
b2TimeStep step;
|
|
step.dt = dt;
|
|
step.velocityIterations = velocityIterations;
|
|
step.positionIterations = positionIterations;
|
|
if (dt > 0.0f)
|
|
{
|
|
step.inv_dt = 1.0f / dt;
|
|
}
|
|
else
|
|
{
|
|
step.inv_dt = 0.0f;
|
|
}
|
|
|
|
step.dtRatio = m_inv_dt0 * dt;
|
|
|
|
step.warmStarting = m_warmStarting;
|
|
|
|
// Update contacts. This is where some contacts are destroyed.
|
|
m_contactManager.Collide();
|
|
|
|
// Integrate velocities, solve velocity constraints, and integrate positions.
|
|
if (step.dt > 0.0f)
|
|
{
|
|
Solve(step);
|
|
}
|
|
|
|
// Handle TOI events.
|
|
if (m_continuousPhysics && step.dt > 0.0f)
|
|
{
|
|
SolveTOI();
|
|
}
|
|
|
|
if (step.dt > 0.0f)
|
|
{
|
|
m_inv_dt0 = step.inv_dt;
|
|
}
|
|
|
|
if (m_flags & e_clearForces)
|
|
{
|
|
ClearForces();
|
|
}
|
|
|
|
m_flags &= ~e_locked;
|
|
}
|
|
|
|
void b2World::ClearForces()
|
|
{
|
|
for (b2Body* body = m_bodyList; body; body = body->GetNext())
|
|
{
|
|
body->m_force.SetZero();
|
|
body->m_torque = 0.0f;
|
|
}
|
|
}
|
|
|
|
struct b2WorldQueryWrapper
|
|
{
|
|
bool QueryCallback(int32 proxyId)
|
|
{
|
|
b2Fixture* fixture = (b2Fixture*)broadPhase->GetUserData(proxyId);
|
|
return callback->ReportFixture(fixture);
|
|
}
|
|
|
|
const b2BroadPhase* broadPhase;
|
|
b2QueryCallback* callback;
|
|
};
|
|
|
|
void b2World::QueryAABB(b2QueryCallback* callback, const b2AABB& aabb) const
|
|
{
|
|
b2WorldQueryWrapper wrapper;
|
|
wrapper.broadPhase = &m_contactManager.m_broadPhase;
|
|
wrapper.callback = callback;
|
|
m_contactManager.m_broadPhase.Query(&wrapper, aabb);
|
|
}
|
|
|
|
struct b2WorldRayCastWrapper
|
|
{
|
|
float32 RayCastCallback(const b2RayCastInput& input, int32 proxyId)
|
|
{
|
|
void* userData = broadPhase->GetUserData(proxyId);
|
|
b2Fixture* fixture = (b2Fixture*)userData;
|
|
b2RayCastOutput output;
|
|
bool hit = fixture->RayCast(&output, input);
|
|
|
|
if (hit)
|
|
{
|
|
float32 fraction = output.fraction;
|
|
b2Vec2 point = (1.0f - fraction) * input.p1 + fraction * input.p2;
|
|
return callback->ReportFixture(fixture, point, output.normal, fraction);
|
|
}
|
|
|
|
return input.maxFraction;
|
|
}
|
|
|
|
const b2BroadPhase* broadPhase;
|
|
b2RayCastCallback* callback;
|
|
};
|
|
|
|
void b2World::RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2) const
|
|
{
|
|
b2WorldRayCastWrapper wrapper;
|
|
wrapper.broadPhase = &m_contactManager.m_broadPhase;
|
|
wrapper.callback = callback;
|
|
b2RayCastInput input;
|
|
input.maxFraction = 1.0f;
|
|
input.p1 = point1;
|
|
input.p2 = point2;
|
|
m_contactManager.m_broadPhase.RayCast(&wrapper, input);
|
|
}
|
|
|
|
void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color& color)
|
|
{
|
|
switch (fixture->GetType())
|
|
{
|
|
case b2Shape::e_circle:
|
|
{
|
|
b2CircleShape* circle = (b2CircleShape*)fixture->GetShape();
|
|
|
|
b2Vec2 center = b2Mul(xf, circle->m_p);
|
|
float32 radius = circle->m_radius;
|
|
b2Vec2 axis = xf.R.col1;
|
|
|
|
m_debugDraw->DrawSolidCircle(center, radius, axis, color);
|
|
}
|
|
break;
|
|
|
|
case b2Shape::e_polygon:
|
|
{
|
|
b2PolygonShape* poly = (b2PolygonShape*)fixture->GetShape();
|
|
int32 vertexCount = poly->m_vertexCount;
|
|
b2Assert(vertexCount <= b2_maxPolygonVertices);
|
|
b2Vec2 vertices[b2_maxPolygonVertices];
|
|
|
|
for (int32 i = 0; i < vertexCount; ++i)
|
|
{
|
|
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
|
|
}
|
|
|
|
m_debugDraw->DrawSolidPolygon(vertices, vertexCount, color);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void b2World::DrawJoint(b2Joint* joint)
|
|
{
|
|
b2Body* bodyA = joint->GetBodyA();
|
|
b2Body* bodyB = joint->GetBodyB();
|
|
const b2Transform& xf1 = bodyA->GetTransform();
|
|
const b2Transform& xf2 = bodyB->GetTransform();
|
|
b2Vec2 x1 = xf1.position;
|
|
b2Vec2 x2 = xf2.position;
|
|
b2Vec2 p1 = joint->GetAnchorA();
|
|
b2Vec2 p2 = joint->GetAnchorB();
|
|
|
|
b2Color color(0.5f, 0.8f, 0.8f);
|
|
|
|
switch (joint->GetType())
|
|
{
|
|
case e_distanceJoint:
|
|
m_debugDraw->DrawSegment(p1, p2, color);
|
|
break;
|
|
|
|
case e_pulleyJoint:
|
|
{
|
|
b2PulleyJoint* pulley = (b2PulleyJoint*)joint;
|
|
b2Vec2 s1 = pulley->GetGroundAnchorA();
|
|
b2Vec2 s2 = pulley->GetGroundAnchorB();
|
|
m_debugDraw->DrawSegment(s1, p1, color);
|
|
m_debugDraw->DrawSegment(s2, p2, color);
|
|
m_debugDraw->DrawSegment(s1, s2, color);
|
|
}
|
|
break;
|
|
|
|
case e_mouseJoint:
|
|
// don't draw this
|
|
break;
|
|
|
|
default:
|
|
m_debugDraw->DrawSegment(x1, p1, color);
|
|
m_debugDraw->DrawSegment(p1, p2, color);
|
|
m_debugDraw->DrawSegment(x2, p2, color);
|
|
}
|
|
}
|
|
|
|
void b2World::DrawDebugData()
|
|
{
|
|
if (m_debugDraw == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint32 flags = m_debugDraw->GetFlags();
|
|
|
|
if (flags & b2DebugDraw::e_shapeBit)
|
|
{
|
|
for (b2Body* b = m_bodyList; b; b = b->GetNext())
|
|
{
|
|
const b2Transform& xf = b->GetTransform();
|
|
for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext())
|
|
{
|
|
if (b->IsActive() == false)
|
|
{
|
|
DrawShape(f, xf, b2Color(0.5f, 0.5f, 0.3f));
|
|
}
|
|
else if (b->GetType() == b2_staticBody)
|
|
{
|
|
DrawShape(f, xf, b2Color(0.5f, 0.9f, 0.5f));
|
|
}
|
|
else if (b->GetType() == b2_kinematicBody)
|
|
{
|
|
DrawShape(f, xf, b2Color(0.5f, 0.5f, 0.9f));
|
|
}
|
|
else if (b->IsAwake() == false)
|
|
{
|
|
DrawShape(f, xf, b2Color(0.6f, 0.6f, 0.6f));
|
|
}
|
|
else
|
|
{
|
|
DrawShape(f, xf, b2Color(0.9f, 0.7f, 0.7f));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & b2DebugDraw::e_jointBit)
|
|
{
|
|
for (b2Joint* j = m_jointList; j; j = j->GetNext())
|
|
{
|
|
DrawJoint(j);
|
|
}
|
|
}
|
|
|
|
if (flags & b2DebugDraw::e_pairBit)
|
|
{
|
|
b2Color color(0.3f, 0.9f, 0.9f);
|
|
for (b2Contact* c = m_contactManager.m_contactList; c; c = c->GetNext())
|
|
{
|
|
b2Fixture* fixtureA = c->GetFixtureA();
|
|
b2Fixture* fixtureB = c->GetFixtureB();
|
|
|
|
b2Vec2 cA = fixtureA->GetAABB().GetCenter();
|
|
b2Vec2 cB = fixtureB->GetAABB().GetCenter();
|
|
|
|
m_debugDraw->DrawSegment(cA, cB, color);
|
|
}
|
|
}
|
|
|
|
if (flags & b2DebugDraw::e_aabbBit)
|
|
{
|
|
b2Color color(0.9f, 0.3f, 0.9f);
|
|
b2BroadPhase* bp = &m_contactManager.m_broadPhase;
|
|
|
|
for (b2Body* b = m_bodyList; b; b = b->GetNext())
|
|
{
|
|
if (b->IsActive() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext())
|
|
{
|
|
b2AABB aabb = bp->GetFatAABB(f->m_proxyId);
|
|
b2Vec2 vs[4];
|
|
vs[0].Set(aabb.lowerBound.x, aabb.lowerBound.y);
|
|
vs[1].Set(aabb.upperBound.x, aabb.lowerBound.y);
|
|
vs[2].Set(aabb.upperBound.x, aabb.upperBound.y);
|
|
vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y);
|
|
|
|
m_debugDraw->DrawPolygon(vs, 4, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & b2DebugDraw::e_centerOfMassBit)
|
|
{
|
|
for (b2Body* b = m_bodyList; b; b = b->GetNext())
|
|
{
|
|
b2Transform xf = b->GetTransform();
|
|
xf.position = b->GetWorldCenter();
|
|
m_debugDraw->DrawTransform(xf);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 b2World::GetProxyCount() const
|
|
{
|
|
return m_contactManager.m_broadPhase.GetProxyCount();
|
|
}
|