/*
* Copyright (c) 2006-2007 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.
*/
package Box2D.Dynamics{
import Box2D.Common.Math.*;
import Box2D.Common.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Dynamics.*;
import Box2D.Dynamics.Contacts.*;
import Box2D.Dynamics.Controllers.b2Controller;
import Box2D.Dynamics.Controllers.b2ControllerEdge;
import Box2D.Dynamics.Joints.*;
import Box2D.Common.b2internal;
use namespace b2internal;
/**
* The world class manages all physics entities, dynamic simulation,
* and asynchronous queries.
*/
public class b2World
{
// Construct a world object.
/**
* @param gravity the world gravity vector.
* @param doSleep improve performance by not simulating inactive bodies.
*/
public function b2World(gravity:b2Vec2, doSleep:Boolean){
m_destructionListener = null;
m_debugDraw = null;
m_bodyList = null;
m_contactList = null;
m_jointList = null;
m_controllerList = null;
m_bodyCount = 0;
m_contactCount = 0;
m_jointCount = 0;
m_controllerCount = 0;
m_warmStarting = true;
m_continuousPhysics = true;
m_allowSleep = doSleep;
m_gravity = gravity;
m_inv_dt0 = 0.0;
m_contactManager.m_world = this;
var bd:b2BodyDef = new b2BodyDef();
m_groundBody = CreateBody(bd);
}
/**
* Destruct the world. All physics entities are destroyed and all heap memory is released.
*/
//~b2World();
/**
* Register a destruction listener.
*/
public function SetDestructionListener(listener:b2DestructionListener) : void{
m_destructionListener = listener;
}
/**
* Register a contact filter to provide specific control over collision.
* Otherwise the default filter is used (b2_defaultFilter).
*/
public function SetContactFilter(filter:b2ContactFilter) : void{
m_contactManager.m_contactFilter = filter;
}
/**
* Register a contact event listener
*/
public function SetContactListener(listener:b2ContactListener) : void{
m_contactManager.m_contactListener = listener;
}
/**
* Register a routine for debug drawing. The debug draw functions are called
* inside the b2World::Step method, so make sure your renderer is ready to
* consume draw commands when you call Step().
*/
public function SetDebugDraw(debugDraw:b2DebugDraw) : void{
m_debugDraw = debugDraw;
}
/**
* Use the given object as a broadphase.
* The old broadphase will not be cleanly emptied.
* @warning It is not recommended you call this except immediately after constructing the world.
* @warning This function is locked during callbacks.
*/
public function SetBroadPhase(broadPhase:IBroadPhase) : void {
var oldBroadPhase:IBroadPhase = m_contactManager.m_broadPhase;
m_contactManager.m_broadPhase = broadPhase;
for (var b:b2Body = m_bodyList; b; b = b.m_next)
{
for (var f:b2Fixture = b.m_fixtureList; f; f = f.m_next)
{
f.m_proxy = broadPhase.CreateProxy(oldBroadPhase.GetFatAABB(f.m_proxy), f);
}
}
}
/**
* Perform validation of internal data structures.
*/
public function Validate() : void
{
m_contactManager.m_broadPhase.Validate();
}
/**
* Get the number of broad-phase proxies.
*/
public function GetProxyCount() : int
{
return m_contactManager.m_broadPhase.GetProxyCount();
}
/**
* Create a rigid body given a definition. No reference to the definition
* is retained.
* @warning This function is locked during callbacks.
*/
public function CreateBody(def:b2BodyDef) : b2Body{
//b2Settings.b2Assert(m_lock == false);
if (IsLocked() == true)
{
return null;
}
//void* mem = m_blockAllocator.Allocate(sizeof(b2Body));
var b:b2Body = new 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;
}
/**
* Destroy a rigid body given a definition. No reference to the definition
* is retained. This function is locked during callbacks.
* @warning This automatically deletes all associated shapes and joints.
* @warning This function is locked during callbacks.
*/
public function DestroyBody(b:b2Body) : void{
//b2Settings.b2Assert(m_bodyCount > 0);
//b2Settings.b2Assert(m_lock == false);
if (IsLocked() == true)
{
return;
}
// Delete the attached joints.
var jn:b2JointEdge = b.m_jointList;
while (jn)
{
var jn0:b2JointEdge = jn;
jn = jn.next;
if (m_destructionListener)
{
m_destructionListener.SayGoodbyeJoint(jn0.joint);
}
DestroyJoint(jn0.joint);
}
// Detach controllers attached to this body
var coe:b2ControllerEdge = b.m_controllerList;
while (coe)
{
var coe0:b2ControllerEdge = coe;
coe = coe.nextController;
coe0.controller.RemoveBody(b);
}
// Delete the attached contacts.
var ce:b2ContactEdge = b.m_contactList;
while (ce)
{
var ce0:b2ContactEdge = ce;
ce = ce.next;
m_contactManager.Destroy(ce0.contact);
}
b.m_contactList = null;
// Delete the attached fixtures. This destroys broad-phase
// proxies.
var f:b2Fixture = b.m_fixtureList;
while (f)
{
var f0:b2Fixture = f;
f = f.m_next;
if (m_destructionListener)
{
m_destructionListener.SayGoodbyeFixture(f0);
}
f0.DestroyProxy(m_contactManager.m_broadPhase);
f0.Destroy();
//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));
}
/**
* Create a joint to constrain bodies together. No reference to the definition
* is retained. This may cause the connected bodies to cease colliding.
* @warning This function is locked during callbacks.
*/
public function CreateJoint(def:b2JointDef) : b2Joint{
//b2Settings.b2Assert(m_lock == false);
var j:b2Joint = b2Joint.Create(def, null);
// 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;
var bodyA:b2Body = def.bodyA;
var bodyB:b2Body = def.bodyB;
// If the joint prevents collisions, then flag any contacts for filtering.
if (def.collideConnected == false )
{
var edge:b2ContactEdge = 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;
}
/**
* Destroy a joint. This may cause the connected bodies to begin colliding.
* @warning This function is locked during callbacks.
*/
public function DestroyJoint(j:b2Joint) : void{
//b2Settings.b2Assert(m_lock == false);
var collideConnected:Boolean = 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.
var bodyA:b2Body = j.m_bodyA;
var bodyB:b2Body = 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, null);
//b2Settings.b2Assert(m_jointCount > 0);
--m_jointCount;
// If the joint prevents collisions, then flag any contacts for filtering.
if (collideConnected == false)
{
var edge:b2ContactEdge = 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;
}
}
}
/**
* Add a controller to the world list
*/
public function AddController(c:b2Controller) : b2Controller
{
c.m_next = m_controllerList;
c.m_prev = null;
m_controllerList = c;
c.m_world = this;
m_controllerCount++;
return c;
}
public function RemoveController(c:b2Controller) : void
{
//TODO: Remove bodies from controller
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 (m_controllerList == c)
m_controllerList = c.m_next;
m_controllerCount--;
}
public function CreateController(controller:b2Controller):b2Controller
{
if (controller.m_world != this)
throw new Error("Controller can only be a member of one world");
controller.m_next = m_controllerList;
controller.m_prev = null;
if (m_controllerList)
m_controllerList.m_prev = controller;
m_controllerList = controller;
++m_controllerCount;
controller.m_world = this;
return controller;
}
public function DestroyController(controller:b2Controller):void
{
//b2Settings.b2Assert(m_controllerCount > 0);
controller.Clear();
if (controller.m_next)
controller.m_next.m_prev = controller.m_prev;
if (controller.m_prev)
controller.m_prev.m_next = controller.m_next;
if (controller == m_controllerList)
m_controllerList = controller.m_next;
--m_controllerCount;
}
/**
* Enable/disable warm starting. For testing.
*/
public function SetWarmStarting(flag: Boolean) : void { m_warmStarting = flag; }
/**
* Enable/disable continuous physics. For testing.
*/
public function SetContinuousPhysics(flag: Boolean) : void { m_continuousPhysics = flag; }
/**
* Get the number of bodies.
*/
public function GetBodyCount() : int
{
return m_bodyCount;
}
/**
* Get the number of joints.
*/
public function GetJointCount() : int
{
return m_jointCount;
}
/**
* Get the number of contacts (each may have 0 or more contact points).
*/
public function GetContactCount() : int
{
return m_contactCount;
}
/**
* Change the global gravity vector.
*/
public function SetGravity(gravity: b2Vec2): void
{
m_gravity = gravity;
}
/**
* Get the global gravity vector.
*/
public function GetGravity():b2Vec2{
return m_gravity;
}
/**
* The world provides a single static ground body with no collision shapes.
* You can use this to simplify the creation of joints and static shapes.
*/
public function GetGroundBody() : b2Body{
return m_groundBody;
}
private static var s_timestep2:b2TimeStep = new b2TimeStep();
/**
* Take a time step. This performs collision detection, integration,
* and constraint solution.
* @param timeStep the amount of time to simulate, this should not vary.
* @param velocityIterations for the velocity constraint solver.
* @param positionIterations for the position constraint solver.
*/
public function Step(dt:number, velocityIterations:int, positionIterations:int) : void{
if (m_flags & e_newFixture)
{
m_contactManager.FindNewContacts();
m_flags &= ~e_newFixture;
}
m_flags |= e_locked;
var step:b2TimeStep = s_timestep2;
step.dt = dt;
step.velocityIterations = velocityIterations;
step.positionIterations = positionIterations;
if (dt > 0.0)
{
step.inv_dt = 1.0 / dt;
}
else
{
step.inv_dt = 0.0;
}
step.dtRatio = m_inv_dt0 * dt;
step.warmStarting = m_warmStarting;
// Update contacts.
m_contactManager.Collide();
// Integrate velocities, solve velocity constraints, and integrate positions.
if (step.dt > 0.0)
{
Solve(step);
}
// Handle TOI events.
if (m_continuousPhysics && step.dt > 0.0)
{
SolveTOI(step);
}
if (step.dt > 0.0)
{
m_inv_dt0 = step.inv_dt;
}
m_flags &= ~e_locked;
}
/**
* Call this after you are done with time steps to clear the forces. You normally
* call this after each call to Step, unless you are performing sub-steps.
*/
public function ClearForces() : void
{
for (var body:b2Body = m_bodyList; body; body = body.m_next)
{
body.m_force.SetZero();
body.m_torque = 0.0;
}
}
static private var s_xf:b2Transform = new b2Transform();
/**
* Call this to draw shapes and other debug draw data.
*/
public function DrawDebugData() : void{
if (m_debugDraw == null)
{
return;
}
m_debugDraw.m_sprite.graphics.clear();
var flags:uint = m_debugDraw.GetFlags();
var i:int;
var b:b2Body;
var f:b2Fixture;
var s:b2Shape;
var j:b2Joint;
var bp:IBroadPhase;
var invQ:b2Vec2 = new b2Vec2;
var x1:b2Vec2 = new b2Vec2;
var x2:b2Vec2 = new b2Vec2;
var xf:b2Transform;
var b1:b2AABB = new b2AABB();
var b2:b2AABB = new b2AABB();
var vs:Array = [new b2Vec2(), new b2Vec2(), new b2Vec2(), new b2Vec2()];
// Store color here and reuse, to reduce allocations
var color:b2Color = new b2Color(0, 0, 0);
if (flags & b2DebugDraw.e_shapeBit)
{
for (b = m_bodyList; b; b = b.m_next)
{
xf = b.m_xf;
for (f = b.GetFixtureList(); f; f = f.m_next)
{
s = f.GetShape();
if (b.IsActive() == false)
{
color.Set(0.5, 0.5, 0.3);
DrawShape(s, xf, color);
}
else if (b.GetType() == b2Body.b2_staticBody)
{
color.Set(0.5, 0.9, 0.5);
DrawShape(s, xf, color);
}
else if (b.GetType() == b2Body.b2_kinematicBody)
{
color.Set(0.5, 0.5, 0.9);
DrawShape(s, xf, color);
}
else if (b.IsAwake() == false)
{
color.Set(0.6, 0.6, 0.6);
DrawShape(s, xf, color);
}
else
{
color.Set(0.9, 0.7, 0.7);
DrawShape(s, xf, color);
}
}
}
}
if (flags & b2DebugDraw.e_jointBit)
{
for (j = m_jointList; j; j = j.m_next)
{
DrawJoint(j);
}
}
if (flags & b2DebugDraw.e_controllerBit)
{
for (var c:b2Controller = m_controllerList; c; c = c.m_next)
{
c.Draw(m_debugDraw);
}
}
if (flags & b2DebugDraw.e_pairBit)
{
color.Set(0.3, 0.9, 0.9);
for (var contact:b2Contact = m_contactManager.m_contactList; contact; contact = contact.GetNext())
{
var fixtureA:b2Fixture = contact.GetFixtureA();
var fixtureB:b2Fixture = contact.GetFixtureB();
var cA:b2Vec2 = fixtureA.GetAABB().GetCenter();
var cB:b2Vec2 = fixtureB.GetAABB().GetCenter();
m_debugDraw.DrawSegment(cA, cB, color);
}
}
if (flags & b2DebugDraw.e_aabbBit)
{
bp = m_contactManager.m_broadPhase;
vs = [new b2Vec2(),new b2Vec2(),new b2Vec2(),new b2Vec2()];
for (b= m_bodyList; b; b = b.GetNext())
{
if (b.IsActive() == false)
{
continue;
}
for (f = b.GetFixtureList(); f; f = f.GetNext())
{
var aabb:b2AABB = bp.GetFatAABB(f.m_proxy);
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 (b = m_bodyList; b; b = b.m_next)
{
xf = s_xf;
xf.R = b.m_xf.R;
xf.position = b.GetWorldCenter();
m_debugDraw.DrawTransform(xf);
}
}
}
/**
* Query the world for all fixtures that potentially overlap the
* provided AABB.
* @param callback a user implemented callback class. It should match signature
* function Callback(fixture:b2Fixture):Boolean
* Return true to continue to the next fixture.
* @param aabb the query box.
*/
public function QueryAABB(callback:Function, aabb:b2AABB):void
{
var broadPhase:IBroadPhase = m_contactManager.m_broadPhase;
function WorldQueryWrapper(proxy:*):Boolean
{
return callback(broadPhase.GetUserData(proxy));
}
broadPhase.Query(WorldQueryWrapper, aabb);
}
/**
* Query the world for all fixtures that precisely overlap the
* provided transformed shape.
* @param callback a user implemented callback class. It should match signature
* function Callback(fixture:b2Fixture):Boolean
* Return true to continue to the next fixture.
* @asonly
*/
public function QueryShape(callback:Function, shape:b2Shape, transform:b2Transform = null):void
{
if (transform == null)
{
transform = new b2Transform();
transform.SetIdentity();
}
var broadPhase:IBroadPhase = m_contactManager.m_broadPhase;
function WorldQueryWrapper(proxy:*):Boolean
{
var fixture:b2Fixture = broadPhase.GetUserData(proxy) as b2Fixture
if(b2Shape.TestOverlap(shape, transform, fixture.GetShape(), fixture.GetBody().GetTransform()))
return callback(fixture);
return true;
}
var aabb:b2AABB = new b2AABB();
shape.ComputeAABB(aabb, transform);
broadPhase.Query(WorldQueryWrapper, aabb);
}
/**
* Query the world for all fixtures that contain a point.
* @param callback a user implemented callback class. It should match signature
* function Callback(fixture:b2Fixture):Boolean
* Return true to continue to the next fixture.
* @asonly
*/
public function QueryPoint(callback:Function, p:b2Vec2):void
{
var broadPhase:IBroadPhase = m_contactManager.m_broadPhase;
function WorldQueryWrapper(proxy:*):Boolean
{
var fixture:b2Fixture = broadPhase.GetUserData(proxy) as b2Fixture
if(fixture.TestPoint(p))
return callback(fixture);
return true;
}
// Make a small box.
var aabb:b2AABB = new b2AABB();
aabb.lowerBound.Set(p.x - b2Settings.b2_linearSlop, p.y - b2Settings.b2_linearSlop);
aabb.upperBound.Set(p.x + b2Settings.b2_linearSlop, p.y + b2Settings.b2_linearSlop);
broadPhase.Query(WorldQueryWrapper, aabb);
}
/**
* Ray-cast the world for all fixtures in the path of the ray. Your callback
* Controls whether you get the closest point, any point, or n-points
* The ray-cast ignores shapes that contain the starting point
* @param callback A callback function which must be of signature:
* function Callback(fixture:b2Fixture, // The fixture hit by the ray
* point:b2Vec2, // The point of initial intersection
* normal:b2Vec2, // The normal vector at the point of intersection
* fraction:number // The fractional length along the ray of the intersection
* ):number
*
* Callback should return the new length of the ray as a fraction of the original length.
* By returning 0, you immediately terminate.
* By returning 1, you continue wiht the original ray.
* By returning the current fraction, you proceed to find the closest point.
* @param point1 the ray starting point
* @param point2 the ray ending point
*/
public function RayCast(callback:Function, point1:b2Vec2, point2:b2Vec2):void
{
var broadPhase:IBroadPhase = m_contactManager.m_broadPhase;
var output:b2RayCastOutput = new b2RayCastOutput;
function RayCastWrapper(input:b2RayCastInput, proxy:*):number
{
var userData:* = broadPhase.GetUserData(proxy);
var fixture:b2Fixture = userData as b2Fixture;
var hit:Boolean = fixture.RayCast(output, input);
if (hit)
{
var fraction:number = output.fraction;
var point:b2Vec2 = new b2Vec2(
(1.0 - fraction) * point1.x + fraction * point2.x,
(1.0 - fraction) * point1.y + fraction * point2.y);
return callback(fixture, point, output.normal, fraction);
}
return input.maxFraction;
}
var input:b2RayCastInput = new b2RayCastInput(point1, point2);
broadPhase.RayCast(RayCastWrapper, input);
}
public function RayCastOne(point1:b2Vec2, point2:b2Vec2):b2Fixture
{
var result:b2Fixture;
function RayCastOneWrapper(fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:number):number
{
result = fixture;
return fraction;
}
RayCast(RayCastOneWrapper, point1, point2);
return result;
}
public function RayCastAll(point1:b2Vec2, point2:b2Vec2):Vector.
{
var result:Vector. = new Vector.();
function RayCastAllWrapper(fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:number):number
{
result[result.length] = fixture;
return 1;
}
RayCast(RayCastAllWrapper, point1, point2);
return result;
}
/**
* Get the world body list. With the returned body, use b2Body::GetNext to get
* the next body in the world list. A NULL body indicates the end of the list.
* @return the head of the world body list.
*/
public function GetBodyList() : b2Body{
return m_bodyList;
}
/**
* Get the world joint list. With the returned joint, use b2Joint::GetNext to get
* the next joint in the world list. A NULL joint indicates the end of the list.
* @return the head of the world joint list.
*/
public function GetJointList() : b2Joint{
return m_jointList;
}
/**
* Get the world contact list. With the returned contact, use b2Contact::GetNext to get
* the next contact in the world list. A NULL contact indicates the end of the list.
* @return the head of the world contact list.
* @warning contacts are
*/
public function GetContactList():b2Contact
{
return m_contactList;
}
/**
* Is the world locked (in the middle of a time step).
*/
public function IsLocked():Boolean
{
return (m_flags & e_locked) > 0;
}
//--------------- Internals Below -------------------
// Internal yet public to make life easier.
// Find islands, integrate and solve constraints, solve position constraints
private var s_stack:Vector. = new Vector.();
public function Solve(step:b2TimeStep) : void{
var b:b2Body;
// Step all controllers
for(var controller:b2Controller= m_controllerList;controller;controller=controller.m_next)
{
controller.Step(step);
}
// Size the island for the worst case.
var island:b2Island = m_island;
island.Initialize(m_bodyCount, m_contactCount, m_jointCount, null, m_contactManager.m_contactListener, m_contactSolver);
// Clear all the island flags.
for (b = m_bodyList; b; b = b.m_next)
{
b.m_flags &= ~b2Body.e_islandFlag;
}
for (var c:b2Contact = m_contactList; c; c = c.m_next)
{
c.m_flags &= ~b2Contact.e_islandFlag;
}
for (var j:b2Joint = m_jointList; j; j = j.m_next)
{
j.m_islandFlag = false;
}
// Build and simulate all awake islands.
var stackSize:int = m_bodyCount;
//b2Body** stack = (b2Body**)m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
var stack:Vector. = s_stack;
for (var seed:b2Body = 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() == b2Body.b2_staticBody)
{
continue;
}
// Reset island and stack.
island.Clear();
var stackCount:int = 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.
b = stack[--stackCount];
//b2Assert(b.IsActive() == true);
island.AddBody(b);
// Make sure the body is awake.
if (b.IsAwake() == false)
{
b.SetAwake(true);
}
// To keep islands as small as possible, we don't
// propagate islands across static bodies.
if (b.GetType() == b2Body.b2_staticBody)
{
continue;
}
var other:b2Body;
// Search all contacts connected to this body.
for (var ce:b2ContactEdge = b.m_contactList; ce; ce = ce.next)
{
// Has this contact already been added to an island?
if (ce.contact.m_flags & b2Contact.e_islandFlag)
{
continue;
}
// Is this contact solid and touching?
if (ce.contact.IsSensor() == true ||
ce.contact.IsEnabled() == false ||
ce.contact.IsTouching() == false)
{
continue;
}
island.AddContact(ce.contact);
ce.contact.m_flags |= b2Contact.e_islandFlag;
//var other:b2Body = ce.other;
other = ce.other;
// Was the other body already added to this island?
if (other.m_flags & b2Body.e_islandFlag)
{
continue;
}
//b2Settings.b2Assert(stackCount < stackSize);
stack[stackCount++] = other;
other.m_flags |= b2Body.e_islandFlag;
}
// Search all joints connect to this body.
for (var jn:b2JointEdge = b.m_jointList; jn; jn = jn.next)
{
if (jn.joint.m_islandFlag == true)
{
continue;
}
other = jn.other;
// Don't simulate joints connected to inactive bodies.
if (other.IsActive() == false)
{
continue;
}
island.AddJoint(jn.joint);
jn.joint.m_islandFlag = true;
if (other.m_flags & b2Body.e_islandFlag)
{
continue;
}
//b2Settings.b2Assert(stackCount < stackSize);
stack[stackCount++] = other;
other.m_flags |= b2Body.e_islandFlag;
}
}
island.Solve(step, m_gravity, m_allowSleep);
// Post solve cleanup.
for (var i:int = 0; i < island.m_bodyCount; ++i)
{
// Allow static bodies to participate in other islands.
b = island.m_bodies[i];
if (b.GetType() == b2Body.b2_staticBody)
{
b.m_flags &= ~b2Body.e_islandFlag;
}
}
}
//m_stackAllocator.Free(stack);
for (i = 0; i < stack.length;++i)
{
if (!stack[i]) break;
stack[i] = null;
}
// Synchronize fixutres, check for out of range bodies.
for (b = m_bodyList; b; b = b.m_next)
{
if (b.IsAwake() == false || b.IsActive() == false)
{
continue;
}
if (b.GetType() == b2Body.b2_staticBody)
{
continue;
}
// Update fixtures (for broad-phase).
b.SynchronizeFixtures();
}
// Look for new contacts.
m_contactManager.FindNewContacts();
}
private static var s_backupA:b2Sweep = new b2Sweep();
private static var s_backupB:b2Sweep = new b2Sweep();
private static var s_timestep:b2TimeStep = new b2TimeStep();
private static var s_queue:Vector. = new Vector.();
// Find TOI contacts and solve them.
public function SolveTOI(step:b2TimeStep) : void{
var b:b2Body;
var fA:b2Fixture;
var fB:b2Fixture;
var bA:b2Body;
var bB:b2Body;
var cEdge:b2ContactEdge;
var j:b2Joint;
// Reserve an island and a queue for TOI island solution.
var island:b2Island = m_island;
island.Initialize(m_bodyCount, b2Settings.b2_maxTOIContactsPerIsland, b2Settings.b2_maxTOIJointsPerIsland, null, m_contactManager.m_contactListener, m_contactSolver);
//Simple one pass queue
//Relies on the fact that we're only making one pass
//through and each body can only be pushed/popped one.
//To push:
// queue[queueStart+queueSize++] = newElement;
//To pop:
// poppedElement = queue[queueStart++];
// --queueSize;
var queue:Vector. = s_queue;
for (b = m_bodyList; b; b = b.m_next)
{
b.m_flags &= ~b2Body.e_islandFlag;
b.m_sweep.t0 = 0.0;
}
var c:b2Contact;
for (c = m_contactList; c; c = c.m_next)
{
// Invalidate TOI
c.m_flags &= ~(b2Contact.e_toiFlag | b2Contact.e_islandFlag);
}
for (j = m_jointList; j; j = j.m_next)
{
j.m_islandFlag = false;
}
// Find TOI events and solve them.
for (;;)
{
// Find the first TOI.
var minContact:b2Contact = null;
var minTOI:number = 1.0;
for (c = m_contactList; c; c = c.m_next)
{
// Can this contact generate a solid TOI contact?
if (c.IsSensor() == true ||
c.IsEnabled() == false ||
c.IsContinuous() == false)
{
continue;
}
// TODO_ERIN keep a counter on the contact, only respond to M TOIs per contact.
var toi:number = 1.0;
if (c.m_flags & b2Contact.e_toiFlag)
{
// This contact has a valid cached TOI.
toi = c.m_toi;
}
else
{
// Compute the TOI for this contact.
fA = c.m_fixtureA;
fB = c.m_fixtureB;
bA = fA.m_body;
bB = fB.m_body;
if ((bA.GetType() != b2Body.b2_dynamicBody || bA.IsAwake() == false) &&
(bB.GetType() != b2Body.b2_dynamicBody || bB.IsAwake() == false))
{
continue;
}
// Put the sweeps onto the same time interval.
var t0:number = bA.m_sweep.t0;
if (bA.m_sweep.t0 < bB.m_sweep.t0)
{
t0 = bB.m_sweep.t0;
bA.m_sweep.Advance(t0);
}
else if (bB.m_sweep.t0 < bA.m_sweep.t0)
{
t0 = bA.m_sweep.t0;
bB.m_sweep.Advance(t0);
}
//b2Settings.b2Assert(t0 < 1.0f);
// Compute the time of impact.
toi = c.ComputeTOI(bA.m_sweep, bB.m_sweep);
b2Settings.b2Assert(0.0 <= toi && toi <= 1.0);
// If the TOI is in range ...
if (toi > 0.0 && toi < 1.0)
{
// Interpolate on the actual range.
//toi = Math.min((1.0 - toi) * t0 + toi, 1.0);
toi = (1.0 - toi) * t0 + toi;
if (toi > 1) toi = 1;
}
c.m_toi = toi;
c.m_flags |= b2Contact.e_toiFlag;
}
if (Number.MIN_VALUE < toi && toi < minTOI)
{
// This is the minimum TOI found so far.
minContact = c;
minTOI = toi;
}
}
if (minContact == null || 1.0 - 100.0 * Number.MIN_VALUE < minTOI)
{
// No more TOI events. Done!
break;
}
// Advance the bodies to the TOI.
fA = minContact.m_fixtureA;
fB = minContact.m_fixtureB;
bA = fA.m_body;
bB = fB.m_body;
s_backupA.Set(bA.m_sweep);
s_backupB.Set(bB.m_sweep);
bA.Advance(minTOI);
bB.Advance(minTOI);
// The TOI contact likely has some new contact points.
minContact.Update(m_contactManager.m_contactListener);
minContact.m_flags &= ~b2Contact.e_toiFlag;
// Is the contact solid?
if (minContact.IsSensor() == true ||
minContact.IsEnabled() == false)
{
// Restore the sweeps
bA.m_sweep.Set(s_backupA);
bB.m_sweep.Set(s_backupB);
bA.SynchronizeTransform();
bB.SynchronizeTransform();
continue;
}
// Did numerical issues prevent;,ontact pointjrom being generated
if (minContact.IsTouching() == false)
{
// Give up on this TOI
continue;
}
// Build the TOI island. We need a dynamic seed.
var seed:b2Body = bA;
if (seed.GetType() != b2Body.b2_dynamicBody)
{
seed = bB;
}
// Reset island and queue.
island.Clear();
var queueStart:int = 0; //start index for queue
var queueSize:int = 0; //elements in queue
queue[queueStart + queueSize++] = seed;
seed.m_flags |= b2Body.e_islandFlag;
// Perform a breadth first search (BFS) on the contact graph.
while (queueSize > 0)
{
// Grab the next body off the stack and add it to the island.
b = queue[queueStart++];
--queueSize;
island.AddBody(b);
// Make sure the body is awake.
if (b.IsAwake() == false)
{
b.SetAwake(true);
}
// To keep islands as small as possible, we don't
// propagate islands across static or kinematic bodies.
if (b.GetType() != b2Body.b2_dynamicBody)
{
continue;
}
// Search all contacts connected to this body.
for (cEdge = b.m_contactList; cEdge; cEdge = cEdge.next)
{
// Does the TOI island still have space for contacts?
if (island.m_contactCount == island.m_contactCapacity)
{
break;
}
// Has this contact already been added to an island?
if (cEdge.contact.m_flags & b2Contact.e_islandFlag)
{
continue;
}
// Skip sperate, sensor, or disabled contacts.
if (cEdge.contact.IsSensor() == true ||
cEdge.contact.IsEnabled() == false ||
cEdge.contact.IsTouching() == false)
{
continue;
}
island.AddContact(cEdge.contact);
cEdge.contact.m_flags |= b2Contact.e_islandFlag;
// Update other body.
var other:b2Body = cEdge.other;
// Was the other body already added to this island?
if (other.m_flags & b2Body.e_islandFlag)
{
continue;
}
// Synchronize the connected body.
if (other.GetType() != b2Body.b2_staticBody)
{
other.Advance(minTOI);
other.SetAwake(true);
}
//b2Settings.b2Assert(queueStart + queueSize < queueCapacity);
queue[queueStart + queueSize] = other;
++queueSize;
other.m_flags |= b2Body.e_islandFlag;
}
for (var jEdge:b2JointEdge = b.m_jointList; jEdge; jEdge = jEdge.next)
{
if (island.m_jointCount == island.m_jointCapacity)
continue;
if (jEdge.joint.m_islandFlag == true)
continue;
other = jEdge.other;
if (other.IsActive() == false)
{
continue;
}
island.AddJoint(jEdge.joint);
jEdge.joint.m_islandFlag = true;
if (other.m_flags & b2Body.e_islandFlag)
continue;
// Synchronize the connected body.
if (other.GetType() != b2Body.b2_staticBody)
{
other.Advance(minTOI);
other.SetAwake(true);
}
//b2Settings.b2Assert(queueStart + queueSize < queueCapacity);
queue[queueStart + queueSize] = other;
++queueSize;
other.m_flags |= b2Body.e_islandFlag;
}
}
var subStep:b2TimeStep = s_timestep;
subStep.warmStarting = false;
subStep.dt = (1.0 - minTOI) * step.dt;
subStep.inv_dt = 1.0 / subStep.dt;
subStep.dtRatio = 0.0;
subStep.velocityIterations = step.velocityIterations;
subStep.positionIterations = step.positionIterations;
island.SolveTOI(subStep);
var i:int;
// Post solve cleanup.
for (i = 0; i < island.m_bodyCount; ++i)
{
// Allow bodies to participate in future TOI islands.
b = island.m_bodies[i];
b.m_flags &= ~b2Body.e_islandFlag;
if (b.IsAwake() == false)
{
continue;
}
if (b.GetType() != b2Body.b2_dynamicBody)
{
continue;
}
// Update fixtures (for broad-phase).
b.SynchronizeFixtures();
// Invalidate all contact TOIs associated with this body. Some of these
// may not be in the island because they were not touching.
for (cEdge = b.m_contactList; cEdge; cEdge = cEdge.next)
{
cEdge.contact.m_flags &= ~b2Contact.e_toiFlag;
}
}
for (i = 0; i < island.m_contactCount; ++i)
{
// Allow contacts to participate in future TOI islands.
c = island.m_contacts[i];
c.m_flags &= ~(b2Contact.e_toiFlag | b2Contact.e_islandFlag);
}
for (i = 0; i < island.m_jointCount;++i)
{
// Allow joints to participate in future TOI islands
j = island.m_joints[i];
j.m_islandFlag = false;
}
// Commit fixture proxy movements to the broad-phase so that new contacts are created.
// Also, some contacts can be destroyed.
m_contactManager.FindNewContacts();
}
//m_stackAllocator.Free(queue);
}
static private var s_jointColor:b2Color = new b2Color(0.5, 0.8, 0.8);
//
public function DrawJoint(joint:b2Joint) : void{
var b1:b2Body = joint.GetBodyA();
var b2:b2Body = joint.GetBodyB();
var xf1:b2Transform = b1.m_xf;
var xf2:b2Transform = b2.m_xf;
var x1:b2Vec2 = xf1.position;
var x2:b2Vec2 = xf2.position;
var p1:b2Vec2 = joint.GetAnchorA();
var p2:b2Vec2 = joint.GetAnchorB();
//b2Color color(0.5f, 0.8f, 0.8f);
var color:b2Color = s_jointColor;
switch (joint.m_type)
{
case b2Joint.e_distanceJoint:
m_debugDraw.DrawSegment(p1, p2, color);
break;
case b2Joint.e_pulleyJoint:
{
var pulley:b2PulleyJoint = (joint as b2PulleyJoint);
var s1:b2Vec2 = pulley.GetGroundAnchorA();
var s2:b2Vec2 = pulley.GetGroundAnchorB();
m_debugDraw.DrawSegment(s1, p1, color);
m_debugDraw.DrawSegment(s2, p2, color);
m_debugDraw.DrawSegment(s1, s2, color);
}
break;
case b2Joint.e_mouseJoint:
m_debugDraw.DrawSegment(p1, p2, color);
break;
default:
if (b1 != m_groundBody)
m_debugDraw.DrawSegment(x1, p1, color);
m_debugDraw.DrawSegment(p1, p2, color);
if (b2 != m_groundBody)
m_debugDraw.DrawSegment(x2, p2, color);
}
}
public function DrawShape(shape:b2Shape, xf:b2Transform, color:b2Color) : void{
switch (shape.m_type)
{
case b2Shape.e_circleShape:
{
var circle:b2CircleShape = (shape as b2CircleShape);
var center:b2Vec2 = b2Math.MulX(xf, circle.m_p);
var radius:number = circle.m_radius;
var axis:b2Vec2 = xf.R.col1;
m_debugDraw.DrawSolidCircle(center, radius, axis, color);
}
break;
case b2Shape.e_polygonShape:
{
var i:int;
var poly:b2PolygonShape = (shape as b2PolygonShape);
var vertexCount:int = poly.GetVertexCount();
var localVertices:Vector. = poly.GetVertices();
var vertices:Vector. = new Vector.(vertexCount);
for (i = 0; i < vertexCount; ++i)
{
vertices[i] = b2Math.MulX(xf, localVertices[i]);
}
m_debugDraw.DrawSolidPolygon(vertices, vertexCount, color);
}
break;
case b2Shape.e_edgeShape:
{
var edge: b2EdgeShape = shape as b2EdgeShape;
m_debugDraw.DrawSegment(b2Math.MulX(xf, edge.GetVertex1()), b2Math.MulX(xf, edge.GetVertex2()), color);
}
break;
}
}
public var m_flags:int;
public var m_contactManager:b2ContactManager = new b2ContactManager();
// These two are stored purely for efficiency purposes, they don't maintain
// any data outside of a call to Step
private var m_contactSolver:b2ContactSolver = new b2ContactSolver();
private var m_island:b2Island = new b2Island();
public var m_bodyList:b2Body;
private var m_jointList:b2Joint;
public var m_contactList:b2Contact;
private var m_bodyCount:int;
public var m_contactCount:int;
private var m_jointCount:int;
private var m_controllerList:b2Controller;
private var m_controllerCount:int;
private var m_gravity:b2Vec2;
private var m_allowSleep:Boolean;
public var m_groundBody:b2Body;
private var m_destructionListener:b2DestructionListener;
private var m_debugDraw:b2DebugDraw;
// This is used to compute the time step ratio to support a variable time step.
private var m_inv_dt0:number;
// This is for debugging the solver.
static private var m_warmStarting:Boolean;
// This is for debugging the solver.
static private var m_continuousPhysics:Boolean;
// m_flags
public static const e_newFixture:int = 0x0001;
public static const e_locked:int = 0x0002;
};
}