123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- /*
- * 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;
- }
|