/* * 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.Dynamics.*; import Box2D.Dynamics.Joints.*; import Box2D.Dynamics.Contacts.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import Box2D.Common.*; /// A rigid body. public class b2Body { /// Creates a shape and attach it to this body. /// @param shapeDef the shape definition. /// @warning This function is locked during callbacks. public function CreateShape(def:b2ShapeDef) : b2Shape{ //b2Settings.b2Assert(m_world.m_lock == false); if (m_world.m_lock == true) { return null; } var s:b2Shape = b2Shape.Create(def, m_world.m_blockAllocator); s.m_next = m_shapeList; m_shapeList = s; ++m_shapeCount; s.m_body = this; // Add the shape to the world's broad-phase. s.CreateProxy(m_world.m_broadPhase, m_xf); // Compute the sweep radius for CCD. s.UpdateSweepRadius(m_sweep.localCenter); return s; } /// Destroy a shape. This removes the shape from the broad-phase and /// therefore destroys any contacts associated with this shape. All shapes /// attached to a body are implicitly destroyed when the body is destroyed. /// @param shape the shape to be removed. /// @warning This function is locked during callbacks. public function DestroyShape(s:b2Shape) : void{ //b2Settings.b2Assert(m_world.m_lock == false); if (m_world.m_lock == true) { return; } //b2Settings.b2Assert(s.m_body == this); s.DestroyProxy(m_world.m_broadPhase); //b2Settings.b2Assert(m_shapeCount > 0); //b2Shape** node = &m_shapeList; var node:b2Shape = m_shapeList; var ppS:b2Shape = null; // Fix pointer-pointer stuff var found:Boolean = false; while (node != null) { if (node == s) { if (ppS) ppS.m_next = s.m_next; else m_shapeList = s.m_next; //node = s.m_next; found = true; break; } ppS = node; node = node.m_next; } // You tried to remove a shape that is not attached to this body. //b2Settings.b2Assert(found); s.m_body = null; s.m_next = null; --m_shapeCount; b2Shape.Destroy(s, m_world.m_blockAllocator); } /// Set the mass properties. Note that this changes the center of mass position. /// If you are not sure how to compute mass properties, use SetMassFromShapes. /// The inertia tensor is assumed to be relative to the center of mass. /// @param massData the mass properties. public function SetMass(massData:b2MassData) : void{ var s:b2Shape; //b2Settings.b2Assert(m_world.m_lock == false); if (m_world.m_lock == true) { return; } m_invMass = 0.0; m_I = 0.0; m_invI = 0.0; m_mass = massData.mass; if (m_mass > 0.0) { m_invMass = 1.0 / m_mass; } if ((m_flags & b2Body.e_fixedRotationFlag) == 0) { m_I = massData.I; } if (m_I > 0.0) { m_invI = 1.0 / m_I; } // Move center of mass. m_sweep.localCenter.SetV(massData.center); //m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); //b2MulMV(m_xf.R, m_sweep.localCenter); var tMat:b2Mat22 = m_xf.R; var tVec:b2Vec2 = m_sweep.localCenter; // (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y) m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); // (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y) m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); //return T.position + b2Mul(T.R, v); m_sweep.c.x += m_xf.position.x; m_sweep.c.y += m_xf.position.y; //m_sweep.c0 = m_sweep.c m_sweep.c0.SetV(m_sweep.c); // Update the sweep radii of all child shapes. for (s = m_shapeList; s; s = s.m_next) { s.UpdateSweepRadius(m_sweep.localCenter); } var oldType:int = m_type; if (m_invMass == 0.0 && m_invI == 0.0) { m_type = e_staticType; } else { m_type = e_dynamicType; } // If the body type changed, we need to refilter the broad-phase proxies. if (oldType != m_type) { for (s = m_shapeList; s; s = s.m_next) { s.RefilterProxy(m_world.m_broadPhase, m_xf); } } } /// Compute the mass properties from the attached shapes. You typically call this /// after adding all the shapes. If you add or remove shapes later, you may want /// to call this again. Note that this changes the center of mass position. static private var s_massData:b2MassData = new b2MassData(); public function SetMassFromShapes() : void{ var s:b2Shape; //b2Settings.b2Assert(m_world.m_lock == false); if (m_world.m_lock == true) { return; } // Compute mass data from shapes. Each shape has its own density. m_mass = 0.0; m_invMass = 0.0; m_I = 0.0; m_invI = 0.0; //b2Vec2 center = b2Vec2_zero; var centerX:Number = 0.0; var centerY:Number = 0.0; var massData:b2MassData = s_massData; for (s = m_shapeList; s; s = s.m_next) { s.ComputeMass(massData); if(massData.mass < 1e-10) continue; // CHRIS EDIT: if mass is 0, center gets set to NaN m_mass += massData.mass; //center += massData.mass * massData.center; centerX += massData.mass * massData.center.x; centerY += massData.mass * massData.center.y; m_I += massData.I; } // Compute center of mass, and shift the origin to the COM. if (m_mass > 0.0) { m_invMass = 1.0 / m_mass; centerX *= m_invMass; centerY *= m_invMass; } if (m_I > 0.0 && (m_flags & e_fixedRotationFlag) == 0) { // Center the inertia about the center of mass. //m_I -= m_mass * b2Dot(center, center); m_I -= m_mass * (centerX * centerX + centerY * centerY); //b2Settings.b2Assert(m_I > 0.0); m_invI = 1.0 / m_I; } else { m_I = 0.0; m_invI = 0.0; } // Move center of mass. m_sweep.localCenter.Set(centerX, centerY); //m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); //b2MulMV(m_xf.R, m_sweep.localCenter); var tMat:b2Mat22 = m_xf.R; var tVec:b2Vec2 = m_sweep.localCenter; // (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y) m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); // (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y) m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); //return T.position + b2Mul(T.R, v); m_sweep.c.x += m_xf.position.x; m_sweep.c.y += m_xf.position.y; //m_sweep.c0 = m_sweep.c m_sweep.c0.SetV(m_sweep.c); // Update the sweep radii of all child shapes. for (s = m_shapeList; s; s = s.m_next) { s.UpdateSweepRadius(m_sweep.localCenter); } var oldType:int = m_type; if (m_invMass == 0.0 && m_invI == 0.0) { m_type = e_staticType; } else { m_type = e_dynamicType; } // If the body type changed, we need to refilter the broad-phase proxies. if (oldType != m_type) { for (s = m_shapeList; s; s = s.m_next) { s.RefilterProxy(m_world.m_broadPhase, m_xf); } } } /// Set the position of the body's origin and rotation (radians). /// This breaks any contacts and wakes the other bodies. /// @param position the new world position of the body's origin (not necessarily /// the center of mass). /// @param angle the new world rotation angle of the body in radians. /// @return false if the movement put a shape outside the world. In this case the /// body is automatically frozen. public function SetXForm(position:b2Vec2, angle:Number) : Boolean{ var s:b2Shape; //b2Settings.b2Assert(m_world.m_lock == false); if (m_world.m_lock == true) { return true; } if (IsFrozen()) { return false; } m_xf.R.Set(angle); m_xf.position.SetV(position); //m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); //b2MulMV(m_xf.R, m_sweep.localCenter); var tMat:b2Mat22 = m_xf.R; var tVec:b2Vec2 = m_sweep.localCenter; // (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y) m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); // (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y) m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); //return T.position + b2Mul(T.R, v); m_sweep.c.x += m_xf.position.x; m_sweep.c.y += m_xf.position.y; //m_sweep.c0 = m_sweep.c m_sweep.c0.SetV(m_sweep.c); m_sweep.a0 = m_sweep.a = angle; var freeze:Boolean = false; for (s = m_shapeList; s; s = s.m_next) { var inRange:Boolean = s.Synchronize(m_world.m_broadPhase, m_xf, m_xf); if (inRange == false) { freeze = true; break; } } if (freeze == true) { m_flags |= e_frozenFlag; m_linearVelocity.SetZero(); m_angularVelocity = 0.0; for (s = m_shapeList; s; s = s.m_next) { s.DestroyProxy(m_world.m_broadPhase); } // Failure return false; } // Success m_world.m_broadPhase.Commit(); return true; } /// Get the body transform for the body's origin. /// @return the world transform of the body's origin. public function GetXForm() : b2XForm{ return m_xf; } /// Get the world body origin position. /// @return the world position of the body's origin. public function GetPosition() : b2Vec2{ return m_xf.position; } /// Get the angle in radians. /// @return the current world rotation angle in radians. public function GetAngle() : Number{ return m_sweep.a; } /// Get the world position of the center of mass. public function GetWorldCenter() : b2Vec2{ return m_sweep.c; } /// Get the local position of the center of mass. public function GetLocalCenter() : b2Vec2{ return m_sweep.localCenter; } /// Set the linear velocity of the center of mass. /// @param v the new linear velocity of the center of mass. public function SetLinearVelocity(v:b2Vec2) : void{ m_linearVelocity.SetV(v); } /// Get the linear velocity of the center of mass. /// @return the linear velocity of the center of mass. public function GetLinearVelocity() : b2Vec2{ return m_linearVelocity; } /// Set the angular velocity. /// @param omega the new angular velocity in radians/second. public function SetAngularVelocity(omega:Number) : void{ m_angularVelocity = omega; } /// Get the angular velocity. /// @return the angular velocity in radians/second. public function GetAngularVelocity() : Number{ return m_angularVelocity; } /// Apply a force at a world point. If the force is not /// applied at the center of mass, it will generate a torque and /// affect the angular velocity. This wakes up the body. /// @param force the world force vector, usually in Newtons (N). /// @param point the world position of the point of application. public function ApplyForce(force:b2Vec2, point:b2Vec2) : void{ if (IsSleeping()) { WakeUp(); } //m_force += force; m_force.x += force.x; m_force.y += force.y; //m_torque += b2Cross(point - m_sweep.c, force); m_torque += ((point.x - m_sweep.c.x) * force.y - (point.y - m_sweep.c.y) * force.x); } /// Apply a torque. This affects the angular velocity /// without affecting the linear velocity of the center of mass. /// This wakes up the body. /// @param torque about the z-axis (out of the screen), usually in N-m. public function ApplyTorque(torque:Number) : void{ if (IsSleeping()) { WakeUp(); } m_torque += torque; } /// Apply an impulse at a point. This immediately modifies the velocity. /// It also modifies the angular velocity if the point of application /// is not at the center of mass. This wakes up the body. /// @param impulse the world impulse vector, usually in N-seconds or kg-m/s. /// @param point the world position of the point of application. public function ApplyImpulse(impulse:b2Vec2, point:b2Vec2) : void{ if (IsSleeping()) { WakeUp(); } //m_linearVelocity += m_invMass * impulse; m_linearVelocity.x += m_invMass * impulse.x; m_linearVelocity.y += m_invMass * impulse.y; //m_angularVelocity += m_invI * b2Cross(point - m_sweep.c, impulse); m_angularVelocity += m_invI * ((point.x - m_sweep.c.x) * impulse.y - (point.y - m_sweep.c.y) * impulse.x); } /// Get the total mass of the body. /// @return the mass, usually in kilograms (kg). public function GetMass() : Number{ return m_mass; } /// Get the central rotational inertia of the body. /// @return the rotational inertia, usually in kg-m^2. public function GetInertia() : Number{ return m_I; } /// Get the world coordinates of a point given the local coordinates. /// @param localPoint a point on the body measured relative the the body's origin. /// @return the same point expressed in world coordinates. public function GetWorldPoint(localPoint:b2Vec2) : b2Vec2{ //return b2Math.b2MulX(m_xf, localPoint); var A:b2Mat22 = m_xf.R; var u:b2Vec2 = new b2Vec2(A.col1.x * localPoint.x + A.col2.x * localPoint.y, A.col1.y * localPoint.x + A.col2.y * localPoint.y); u.x += m_xf.position.x; u.y += m_xf.position.y; return u; } /// Get the world coordinates of a vector given the local coordinates. /// @param localVector a vector fixed in the body. /// @return the same vector expressed in world coordinates. public function GetWorldVector(localVector:b2Vec2) : b2Vec2{ return b2Math.b2MulMV(m_xf.R, localVector); } /// Gets a local point relative to the body's origin given a world point. /// @param a point in world coordinates. /// @return the corresponding local point relative to the body's origin. public function GetLocalPoint(worldPoint:b2Vec2) : b2Vec2{ return b2Math.b2MulXT(m_xf, worldPoint); } /// Gets a local vector given a world vector. /// @param a vector in world coordinates. /// @return the corresponding local vector. public function GetLocalVector(worldVector:b2Vec2) : b2Vec2{ return b2Math.b2MulTMV(m_xf.R, worldVector); } /// Get the world linear velocity of a world point attached to this body. /// @param a point in world coordinates. /// @return the world velocity of a point. public function GetLinearVelocityFromWorldPoint(worldPoint:b2Vec2) : b2Vec2 { //return m_linearVelocity + b2Cross(m_angularVelocity, worldPoint - m_sweep.c); return new b2Vec2(m_linearVelocity.x + m_angularVelocity * (worldPoint.y - m_sweep.c.y), m_linearVelocity.y - m_angularVelocity * (worldPoint.x - m_sweep.c.x)); } /// Get the world velocity of a local point. /// @param a point in local coordinates. /// @return the world velocity of a point. public function GetLinearVelocityFromLocalPoint(localPoint:b2Vec2) : b2Vec2 { //return GetLinearVelocityFromWorldPoint(GetWorldPoint(localPoint)); var A:b2Mat22 = m_xf.R; var worldPoint:b2Vec2 = new b2Vec2(A.col1.x * localPoint.x + A.col2.x * localPoint.y, A.col1.y * localPoint.x + A.col2.y * localPoint.y); worldPoint.x += m_xf.position.x; worldPoint.y += m_xf.position.y; return new b2Vec2(m_linearVelocity.x + m_angularVelocity * (worldPoint.y - m_sweep.c.y), m_linearVelocity.y - m_angularVelocity * (worldPoint.x - m_sweep.c.x)); } /// Is this body treated like a bullet for continuous collision detection? public function IsBullet() : Boolean{ return (m_flags & e_bulletFlag) == e_bulletFlag; } /// Should this body be treated like a bullet for continuous collision detection? public function SetBullet(flag:Boolean) : void{ if (flag) { m_flags |= e_bulletFlag; } else { m_flags &= ~e_bulletFlag; } } /// Is this body static (immovable)? public function IsStatic() : Boolean{ return m_type == e_staticType; } /// Is this body dynamic (movable)? public function IsDynamic() :Boolean{ return m_type == e_dynamicType; } /// Is this body frozen? public function IsFrozen() : Boolean{ return (m_flags & e_frozenFlag) == e_frozenFlag; } /// Is this body sleeping (not simulating). public function IsSleeping() : Boolean{ return (m_flags & e_sleepFlag) == e_sleepFlag; } /// You can disable sleeping on this body. public function AllowSleeping(flag:Boolean) : void{ if (flag) { m_flags |= e_allowSleepFlag; } else { m_flags &= ~e_allowSleepFlag; WakeUp(); } } /// Wake up this body so it will begin simulating. public function WakeUp() : void{ m_flags &= ~e_sleepFlag; m_sleepTime = 0.0; } /// Put this body to sleep so it will stop simulating. /// This also sets the velocity to zero. public function PutToSleep() : void { m_flags |= e_sleepFlag; m_sleepTime = 0.0; m_linearVelocity.SetZero(); m_angularVelocity = 0.0; m_force.SetZero(); m_torque = 0.0; } /// Get the list of all shapes attached to this body. public function GetShapeList() : b2Shape{ return m_shapeList; } /// Get the list of all joints attached to this body. public function GetJointList() : b2JointEdge{ return m_jointList; } /// Get the next body in the world's body list. public function GetNext() : b2Body{ return m_next; } /// Get the user data pointer that was provided in the body definition. public function GetUserData() : *{ return m_userData; } /// Set the user data. Use this to store your application specific data. public function SetUserData(data:*) : void { m_userData = data; } /// Get the parent world of this body. public function GetWorld(): b2World { return m_world; } //--------------- Internals Below ------------------- // Constructor public function b2Body(bd:b2BodyDef, world:b2World){ //b2Settings.b2Assert(world.m_lock == false); m_flags = 0; if (bd.isBullet) { m_flags |= e_bulletFlag; } if (bd.fixedRotation) { m_flags |= e_fixedRotationFlag; } if (bd.allowSleep) { m_flags |= e_allowSleepFlag; } if (bd.isSleeping) { m_flags |= e_sleepFlag; } m_world = world; m_xf.position.SetV(bd.position); m_xf.R.Set(bd.angle); m_sweep.localCenter.SetV(bd.massData.center); m_sweep.t0 = 1.0; m_sweep.a0 = m_sweep.a = bd.angle; //m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); //b2MulMV(m_xf.R, m_sweep.localCenter); var tMat:b2Mat22 = m_xf.R; var tVec:b2Vec2 = m_sweep.localCenter; // (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y) m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); // (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y) m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); //return T.position + b2Mul(T.R, v); m_sweep.c.x += m_xf.position.x; m_sweep.c.y += m_xf.position.y; //m_sweep.c0 = m_sweep.c m_sweep.c0.SetV(m_sweep.c); m_jointList = null; m_contactList = null; m_prev = null; m_next = null; m_linearDamping = bd.linearDamping; m_angularDamping = bd.angularDamping; m_force.Set(0.0, 0.0); m_torque = 0.0; m_linearVelocity.SetZero(); m_angularVelocity = 0.0; m_sleepTime = 0.0; m_invMass = 0.0; m_I = 0.0; m_invI = 0.0; m_mass = bd.massData.mass; if (m_mass > 0.0) { m_invMass = 1.0 / m_mass; } if ((m_flags & b2Body.e_fixedRotationFlag) == 0) { m_I = bd.massData.I; } if (m_I > 0.0) { m_invI = 1.0 / m_I; } if (m_invMass == 0.0 && m_invI == 0.0) { m_type = e_staticType; } else { m_type = e_dynamicType; } m_userData = bd.userData; m_shapeList = null; m_shapeCount = 0; } // Destructor //~b2Body(); // static private var s_xf1:b2XForm = new b2XForm(); // public function SynchronizeShapes() : Boolean{ var xf1:b2XForm = s_xf1; xf1.R.Set(m_sweep.a0); //xf1.position = m_sweep.c0 - b2Mul(xf1.R, m_sweep.localCenter); var tMat:b2Mat22 = xf1.R; var tVec:b2Vec2 = m_sweep.localCenter; xf1.position.x = m_sweep.c0.x - (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); xf1.position.y = m_sweep.c0.y - (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); var s:b2Shape; var inRange:Boolean = true; for (s = m_shapeList; s; s = s.m_next) { inRange = s.Synchronize(m_world.m_broadPhase, xf1, m_xf); if (inRange == false) { break; } } if (inRange == false) { m_flags |= e_frozenFlag; m_linearVelocity.SetZero(); m_angularVelocity = 0.0; for (s = m_shapeList; s; s = s.m_next) { s.DestroyProxy(m_world.m_broadPhase); } // Failure return false; } // Success return true; } public function SynchronizeTransform() : void{ m_xf.R.Set(m_sweep.a); //m_xf.position = m_sweep.c - b2Mul(m_xf.R, m_sweep.localCenter); var tMat:b2Mat22 = m_xf.R; var tVec:b2Vec2 = m_sweep.localCenter; m_xf.position.x = m_sweep.c.x - (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); m_xf.position.y = m_sweep.c.y - (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); } // This is used to prevent connected bodies from colliding. // It may lie, depending on the collideConnected flag. public function IsConnected(other:b2Body) : Boolean{ for (var jn:b2JointEdge = m_jointList; jn; jn = jn.next) { if (jn.other == other) return jn.joint.m_collideConnected == false; } return false; } public function Advance(t:Number) : void{ // Advance to the new safe time. m_sweep.Advance(t); m_sweep.c.SetV(m_sweep.c0); m_sweep.a = m_sweep.a0; SynchronizeTransform(); } public var m_flags:uint; public var m_type:int; public var m_xf:b2XForm = new b2XForm(); // the body origin transform public var m_sweep:b2Sweep = new b2Sweep(); // the swept motion for CCD public var m_linearVelocity:b2Vec2 = new b2Vec2(); public var m_angularVelocity:Number; public var m_force:b2Vec2 = new b2Vec2(); public var m_torque:Number; public var m_world:b2World; public var m_prev:b2Body; public var m_next:b2Body; public var m_shapeList:b2Shape; public var m_shapeCount:int; public var m_jointList:b2JointEdge; public var m_contactList:b2ContactEdge; public var m_mass:Number, m_invMass:Number; public var m_I:Number, m_invI:Number; public var m_linearDamping:Number; public var m_angularDamping:Number; public var m_sleepTime:Number; public var m_userData:*; // m_flags //enum //{ static public var e_frozenFlag:uint = 0x0002; static public var e_islandFlag:uint = 0x0004; static public var e_sleepFlag:uint = 0x0008; static public var e_allowSleepFlag:uint = 0x0010; static public var e_bulletFlag:uint = 0x0020; static public var e_fixedRotationFlag:uint = 0x0040; //}; // m_type //enum //{ static public var e_staticType:uint = 1; static public var e_dynamicType:uint = 2; static public var e_maxTypes:uint = 3; //}; }; }