first commit
[blok] / Box2D / Source / Dynamics / Contacts / b2ContactSolver.cpp
diff --git a/Box2D/Source/Dynamics/Contacts/b2ContactSolver.cpp b/Box2D/Source/Dynamics/Contacts/b2ContactSolver.cpp
new file mode 100644 (file)
index 0000000..e481546
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+* 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.
+*/
+
+#include "b2ContactSolver.h"
+#include "b2Contact.h"
+#include "../b2Body.h"
+#include "../b2World.h"
+#include "../../Common/b2StackAllocator.h"
+
+b2ContactSolver::b2ContactSolver(const b2TimeStep& step, b2Contact** contacts, int32 contactCount, b2StackAllocator* allocator)
+{
+       m_step = step;
+       m_allocator = allocator;
+
+       m_constraintCount = 0;
+       for (int32 i = 0; i < contactCount; ++i)
+       {
+               b2Assert(contacts[i]->IsSolid());
+               m_constraintCount += contacts[i]->GetManifoldCount();
+       }
+
+       m_constraints = (b2ContactConstraint*)m_allocator->Allocate(m_constraintCount * sizeof(b2ContactConstraint));
+
+       int32 count = 0;
+       for (int32 i = 0; i < contactCount; ++i)
+       {
+               b2Contact* contact = contacts[i];
+
+               b2Body* b1 = contact->m_shape1->GetBody();
+               b2Body* b2 = contact->m_shape2->GetBody();
+               int32 manifoldCount = contact->GetManifoldCount();
+               b2Manifold* manifolds = contact->GetManifolds();
+               float32 friction = contact->m_friction;
+               float32 restitution = contact->m_restitution;
+
+               b2Vec2 v1 = b1->m_linearVelocity;
+               b2Vec2 v2 = b2->m_linearVelocity;
+               float32 w1 = b1->m_angularVelocity;
+               float32 w2 = b2->m_angularVelocity;
+
+               for (int32 j = 0; j < manifoldCount; ++j)
+               {
+                       b2Manifold* manifold = manifolds + j;
+
+                       b2Assert(manifold->pointCount > 0);
+
+                       const b2Vec2 normal = manifold->normal;
+
+                       b2Assert(count < m_constraintCount);
+                       b2ContactConstraint* c = m_constraints + count;
+                       c->body1 = b1;
+                       c->body2 = b2;
+                       c->manifold = manifold;
+                       c->normal = normal;
+                       c->pointCount = manifold->pointCount;
+                       c->friction = friction;
+                       c->restitution = restitution;
+
+                       for (int32 k = 0; k < c->pointCount; ++k)
+                       {
+                               b2ManifoldPoint* cp = manifold->points + k;
+                               b2ContactConstraintPoint* ccp = c->points + k;
+
+                               ccp->normalImpulse = cp->normalImpulse;
+                               ccp->tangentImpulse = cp->tangentImpulse;
+                               ccp->separation = cp->separation;
+                               ccp->positionImpulse = 0.0f;
+
+                               ccp->localAnchor1 = cp->localPoint1;
+                               ccp->localAnchor2 = cp->localPoint2;
+                               ccp->r1 = b2Mul(b1->GetXForm().R, cp->localPoint1 - b1->GetLocalCenter());
+                               ccp->r2 = b2Mul(b2->GetXForm().R, cp->localPoint2 - b2->GetLocalCenter());
+
+                               float32 r1Sqr = b2Dot(ccp->r1, ccp->r1);
+                               float32 r2Sqr = b2Dot(ccp->r2, ccp->r2);
+                               float32 rn1 = b2Dot(ccp->r1, normal);
+                               float32 rn2 = b2Dot(ccp->r2, normal);
+
+                               float32 kNormal = b1->m_invMass + b2->m_invMass;
+                               kNormal += b1->m_invI * (r1Sqr - rn1 * rn1) + b2->m_invI * (r2Sqr - rn2 * rn2);
+
+                               b2Assert(kNormal > B2_FLT_EPSILON);
+                               ccp->normalMass = 1.0f / kNormal;
+
+                               float32 kEqualized = b1->m_mass * b1->m_invMass + b2->m_mass * b2->m_invMass;
+                               kEqualized += b1->m_mass * b1->m_invI * (r1Sqr - rn1 * rn1) + b2->m_mass * b2->m_invI * (r2Sqr - rn2 * rn2);
+
+                               b2Assert(kEqualized > B2_FLT_EPSILON);
+                               ccp->equalizedMass = 1.0f / kEqualized;
+
+                               b2Vec2 tangent = b2Cross(normal, 1.0f);
+
+                               float32 rt1 = b2Dot(ccp->r1, tangent);
+                               float32 rt2 = b2Dot(ccp->r2, tangent);
+                               float32 kTangent = b1->m_invMass + b2->m_invMass;
+                               kTangent += b1->m_invI * (r1Sqr - rt1 * rt1) + b2->m_invI * (r2Sqr - rt2 * rt2);
+
+                               b2Assert(kTangent > B2_FLT_EPSILON);
+                               ccp->tangentMass = 1.0f /  kTangent;
+
+                               // Setup a velocity bias for restitution.
+                               ccp->velocityBias = 0.0f;
+                               if (ccp->separation > 0.0f)
+                               {
+                                       ccp->velocityBias = -60.0f * ccp->separation; // TODO_ERIN b2TimeStep
+                               }
+
+                               float32 vRel = b2Dot(c->normal, v2 + b2Cross(w2, ccp->r2) - v1 - b2Cross(w1, ccp->r1));
+                               if (vRel < -b2_velocityThreshold)
+                               {
+                                       ccp->velocityBias += -c->restitution * vRel;
+                               }
+                       }
+
+                       ++count;
+               }
+       }
+
+       b2Assert(count == m_constraintCount);
+}
+
+b2ContactSolver::~b2ContactSolver()
+{
+       m_allocator->Free(m_constraints);
+}
+
+void b2ContactSolver::InitVelocityConstraints(const b2TimeStep& step)
+{
+       // Warm start.
+       for (int32 i = 0; i < m_constraintCount; ++i)
+       {
+               b2ContactConstraint* c = m_constraints + i;
+
+               b2Body* b1 = c->body1;
+               b2Body* b2 = c->body2;
+               float32 invMass1 = b1->m_invMass;
+               float32 invI1 = b1->m_invI;
+               float32 invMass2 = b2->m_invMass;
+               float32 invI2 = b2->m_invI;
+               b2Vec2 normal = c->normal;
+               b2Vec2 tangent = b2Cross(normal, 1.0f);
+
+               if (step.warmStarting)
+               {
+                       for (int32 j = 0; j < c->pointCount; ++j)
+                       {
+                               b2ContactConstraintPoint* ccp = c->points + j;
+                               ccp->normalImpulse *= step.dtRatio;
+                               ccp->tangentImpulse *= step.dtRatio;
+                               b2Vec2 P = ccp->normalImpulse * normal + ccp->tangentImpulse * tangent;
+                               b1->m_angularVelocity -= invI1 * b2Cross(ccp->r1, P);
+                               b1->m_linearVelocity -= invMass1 * P;
+                               b2->m_angularVelocity += invI2 * b2Cross(ccp->r2, P);
+                               b2->m_linearVelocity += invMass2 * P;
+                       }
+               }
+               else
+               {
+                       for (int32 j = 0; j < c->pointCount; ++j)
+                       {
+                               b2ContactConstraintPoint* ccp = c->points + j;
+                               ccp->normalImpulse = 0.0f;
+                               ccp->tangentImpulse = 0.0f;
+                       }
+               }
+       }
+}
+
+void b2ContactSolver::SolveVelocityConstraints()
+{
+       for (int32 i = 0; i < m_constraintCount; ++i)
+       {
+               b2ContactConstraint* c = m_constraints + i;
+               b2Body* b1 = c->body1;
+               b2Body* b2 = c->body2;
+               float32 w1 = b1->m_angularVelocity;
+               float32 w2 = b2->m_angularVelocity;
+               b2Vec2 v1 = b1->m_linearVelocity;
+               b2Vec2 v2 = b2->m_linearVelocity;
+               float32 invMass1 = b1->m_invMass;
+               float32 invI1 = b1->m_invI;
+               float32 invMass2 = b2->m_invMass;
+               float32 invI2 = b2->m_invI;
+               b2Vec2 normal = c->normal;
+               b2Vec2 tangent = b2Cross(normal, 1.0f);
+               float32 friction = c->friction;
+//#define DEFERRED_UPDATE
+#ifdef DEFERRED_UPDATE
+               b2Vec2 b1_linearVelocity = b1->m_linearVelocity;
+               float32 b1_angularVelocity = b1->m_angularVelocity;
+               b2Vec2 b2_linearVelocity = b2->m_linearVelocity;
+               float32 b2_angularVelocity = b2->m_angularVelocity;
+#endif
+               // Solve normal constraints
+               for (int32 j = 0; j < c->pointCount; ++j)
+               {
+                       b2ContactConstraintPoint* ccp = c->points + j;
+
+                       // Relative velocity at contact
+                       b2Vec2 dv = v2 + b2Cross(w2, ccp->r2) - v1 - b2Cross(w1, ccp->r1);
+
+                       // Compute normal impulse
+                       float32 vn = b2Dot(dv, normal);
+                       float32 lambda = -ccp->normalMass * (vn - ccp->velocityBias);
+
+                       // b2Clamp the accumulated impulse
+                       float32 newImpulse = b2Max(ccp->normalImpulse + lambda, 0.0f);
+                       lambda = newImpulse - ccp->normalImpulse;
+
+                       // Apply contact impulse
+                       b2Vec2 P = lambda * normal;
+#ifdef DEFERRED_UPDATE
+                       b1_linearVelocity -= invMass1 * P;
+                       b1_angularVelocity -= invI1 * b2Cross(r1, P);
+
+                       b2_linearVelocity += invMass2 * P;
+                       b2_angularVelocity += invI2 * b2Cross(r2, P);
+#else
+                       v1 -= invMass1 * P;
+                       w1 -= invI1 * b2Cross(ccp->r1, P);
+
+                       v2 += invMass2 * P;
+                       w2 += invI2 * b2Cross(ccp->r2, P);
+#endif
+                       ccp->normalImpulse = newImpulse;
+               }
+
+#ifdef DEFERRED_UPDATE
+               b1->m_linearVelocity = b1_linearVelocity;
+               b1->m_angularVelocity = b1_angularVelocity;
+               b2->m_linearVelocity = b2_linearVelocity;
+               b2->m_angularVelocity = b2_angularVelocity;
+#endif
+               // Solve tangent constraints
+               for (int32 j = 0; j < c->pointCount; ++j)
+               {
+                       b2ContactConstraintPoint* ccp = c->points + j;
+
+                       // Relative velocity at contact
+                       b2Vec2 dv = v2 + b2Cross(w2, ccp->r2) - v1 - b2Cross(w1, ccp->r1);
+
+                       // Compute tangent force
+                       float32 vt = b2Dot(dv, tangent);
+                       float32 lambda = ccp->tangentMass * (-vt);
+
+                       // b2Clamp the accumulated force
+                       float32 maxFriction = friction * ccp->normalImpulse;
+                       float32 newImpulse = b2Clamp(ccp->tangentImpulse + lambda, -maxFriction, maxFriction);
+                       lambda = newImpulse - ccp->tangentImpulse;
+
+                       // Apply contact impulse
+                       b2Vec2 P = lambda * tangent;
+
+                       v1 -= invMass1 * P;
+                       w1 -= invI1 * b2Cross(ccp->r1, P);
+
+                       v2 += invMass2 * P;
+                       w2 += invI2 * b2Cross(ccp->r2, P);
+
+                       ccp->tangentImpulse = newImpulse;
+               }
+
+               b1->m_linearVelocity = v1;
+               b1->m_angularVelocity = w1;
+               b2->m_linearVelocity = v2;
+               b2->m_angularVelocity = w2;
+       }
+}
+
+void b2ContactSolver::FinalizeVelocityConstraints()
+{
+       for (int32 i = 0; i < m_constraintCount; ++i)
+       {
+               b2ContactConstraint* c = m_constraints + i;
+               b2Manifold* m = c->manifold;
+
+               for (int32 j = 0; j < c->pointCount; ++j)
+               {
+                       m->points[j].normalImpulse = c->points[j].normalImpulse;
+                       m->points[j].tangentImpulse = c->points[j].tangentImpulse;
+               }
+       }
+}
+
+bool b2ContactSolver::SolvePositionConstraints(float32 baumgarte)
+{
+       float32 minSeparation = 0.0f;
+
+       for (int32 i = 0; i < m_constraintCount; ++i)
+       {
+               b2ContactConstraint* c = m_constraints + i;
+               b2Body* b1 = c->body1;
+               b2Body* b2 = c->body2;
+               float32 invMass1 = b1->m_mass * b1->m_invMass;
+               float32 invI1 = b1->m_mass * b1->m_invI;
+               float32 invMass2 = b2->m_mass * b2->m_invMass;
+               float32 invI2 = b2->m_mass * b2->m_invI;
+               
+               b2Vec2 normal = c->normal;
+
+               // Solver normal constraints
+               for (int32 j = 0; j < c->pointCount; ++j)
+               {
+                       b2ContactConstraintPoint* ccp = c->points + j;
+
+                       b2Vec2 r1 = b2Mul(b1->GetXForm().R, ccp->localAnchor1 - b1->GetLocalCenter());
+                       b2Vec2 r2 = b2Mul(b2->GetXForm().R, ccp->localAnchor2 - b2->GetLocalCenter());
+
+                       b2Vec2 p1 = b1->m_sweep.c + r1;
+                       b2Vec2 p2 = b2->m_sweep.c + r2;
+                       b2Vec2 dp = p2 - p1;
+
+                       // Approximate the current separation.
+                       float32 separation = b2Dot(dp, normal) + ccp->separation;
+
+                       // Track max constraint error.
+                       minSeparation = b2Min(minSeparation, separation);
+
+                       // Prevent large corrections and allow slop.
+                       float32 C = baumgarte * b2Clamp(separation + b2_linearSlop, -b2_maxLinearCorrection, 0.0f);
+
+                       // Compute normal impulse
+                       float32 dImpulse = -ccp->equalizedMass * C;
+
+                       // b2Clamp the accumulated impulse
+                       float32 impulse0 = ccp->positionImpulse;
+                       ccp->positionImpulse = b2Max(impulse0 + dImpulse, 0.0f);
+                       dImpulse = ccp->positionImpulse - impulse0;
+
+                       b2Vec2 impulse = dImpulse * normal;
+
+                       b1->m_sweep.c -= invMass1 * impulse;
+                       b1->m_sweep.a -= invI1 * b2Cross(r1, impulse);
+                       b1->SynchronizeTransform();
+
+                       b2->m_sweep.c += invMass2 * impulse;
+                       b2->m_sweep.a += invI2 * b2Cross(r2, impulse);
+                       b2->SynchronizeTransform();
+               }
+       }
+
+       // We can't expect minSpeparation >= -b2_linearSlop because we don't
+       // push the separation above -b2_linearSlop.
+       return minSeparation >= -1.5f * b2_linearSlop;
+}