File indexing completed on 2025-08-03 03:50:01

0001 /*
0002 * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
0003 *
0004 * This software is provided 'as-is', without any express or implied
0005 * warranty.  In no event will the authors be held liable for any damages
0006 * arising from the use of this software.
0007 * Permission is granted to anyone to use this software for any purpose,
0008 * including commercial applications, and to alter it and redistribute it
0009 * freely, subject to the following restrictions:
0010 * 1. The origin of this software must not be misrepresented; you must not
0011 * claim that you wrote the original software. If you use this software
0012 * in a product, an acknowledgment in the product documentation would be
0013 * appreciated but is not required.
0014 * 2. Altered source versions must be plainly marked as such, and must not be
0015 * misrepresented as being the original software.
0016 * 3. This notice may not be removed or altered from any source distribution.
0017 */
0018 
0019 #include <Box2D/Collision/b2Distance.h>
0020 #include <Box2D/Dynamics/b2Island.h>
0021 #include <Box2D/Dynamics/b2Fixture.h>
0022 #include <Box2D/Dynamics/b2World.h>
0023 #include <Box2D/Dynamics/Contacts/b2Contact.h>
0024 #include <Box2D/Dynamics/Contacts/b2ContactSolver.h>
0025 #include <Box2D/Dynamics/Joints/b2Joint.h>
0026 #include <Box2D/Common/b2StackAllocator.h>
0027 
0028 /*
0029 Position Correction Notes
0030 =========================
0031 I tried the several algorithms for position correction of the 2D revolute joint.
0032 I looked at these systems:
0033 - simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s.
0034 - suspension bridge with 30 1m long planks of length 1m.
0035 - multi-link chain with 30 1m long links.
0036 
0037 Here are the algorithms:
0038 
0039 Baumgarte - A fraction of the position error is added to the velocity error. There is no
0040 separate position solver.
0041 
0042 Pseudo Velocities - After the velocity solver and position integration,
0043 the position error, Jacobian, and effective mass are recomputed. Then
0044 the velocity constraints are solved with pseudo velocities and a fraction
0045 of the position error is added to the pseudo velocity error. The pseudo
0046 velocities are initialized to zero and there is no warm-starting. After
0047 the position solver, the pseudo velocities are added to the positions.
0048 This is also called the First Order World method or the Position LCP method.
0049 
0050 Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the
0051 position error is re-computed for each constraint and the positions are updated
0052 after the constraint is solved. The radius vectors (aka Jacobians) are
0053 re-computed too (otherwise the algorithm has horrible instability). The pseudo
0054 velocity states are not needed because they are effectively zero at the beginning
0055 of each iteration. Since we have the current position error, we allow the
0056 iterations to terminate early if the error becomes smaller than b2_linearSlop.
0057 
0058 Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed
0059 each time a constraint is solved.
0060 
0061 Here are the results:
0062 Baumgarte - this is the cheapest algorithm but it has some stability problems,
0063 especially with the bridge. The chain links separate easily close to the root
0064 and they jitter as they struggle to pull together. This is one of the most common
0065 methods in the field. The big drawback is that the position correction artificially
0066 affects the momentum, thus leading to instabilities and false bounce. I used a
0067 bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller
0068 factor makes joints and contacts more spongy.
0069 
0070 Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is
0071 stable. However, joints still separate with large angular velocities. Drag the
0072 simple pendulum in a circle quickly and the joint will separate. The chain separates
0073 easily and does not recover. I used a bias factor of 0.2. A larger value lead to
0074 the bridge collapsing when a heavy cube drops on it.
0075 
0076 Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo
0077 Velocities, but in other ways it is worse. The bridge and chain are much more
0078 stable, but the simple pendulum goes unstable at high angular velocities.
0079 
0080 Full NGS - stable in all tests. The joints display good stiffness. The bridge
0081 still sags, but this is better than infinite forces.
0082 
0083 Recommendations
0084 Pseudo Velocities are not really worthwhile because the bridge and chain cannot
0085 recover from joint separation. In other cases the benefit over Baumgarte is small.
0086 
0087 Modified NGS is not a robust method for the revolute joint due to the violent
0088 instability seen in the simple pendulum. Perhaps it is viable with other constraint
0089 types, especially scalar constraints where the effective mass is a scalar.
0090 
0091 This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities
0092 and is very fast. I don't think we can escape Baumgarte, especially in highly
0093 demanding cases where high constraint fidelity is not needed.
0094 
0095 Full NGS is robust and easy on the eyes. I recommend this as an option for
0096 higher fidelity simulation and certainly for suspension bridges and long chains.
0097 Full NGS might be a good choice for ragdolls, especially motorized ragdolls where
0098 joint separation can be problematic. The number of NGS iterations can be reduced
0099 for better performance without harming robustness much.
0100 
0101 Each joint in a can be handled differently in the position solver. So I recommend
0102 a system where the user can select the algorithm on a per joint basis. I would
0103 probably default to the slower Full NGS and let the user select the faster
0104 Baumgarte method in performance critical scenarios.
0105 */
0106 
0107 /*
0108 Cache Performance
0109 
0110 The Box2D solvers are dominated by cache misses. Data structures are designed
0111 to increase the number of cache hits. Much of misses are due to random access
0112 to body data. The constraint structures are iterated over linearly, which leads
0113 to few cache misses.
0114 
0115 The bodies are not accessed during iteration. Instead read only data, such as
0116 the mass values are stored with the constraints. The mutable data are the constraint
0117 impulses and the bodies velocities/positions. The impulses are held inside the
0118 constraint structures. The body velocities/positions are held in compact, temporary
0119 arrays to increase the number of cache hits. Linear and angular velocity are
0120 stored in a single array since multiple arrays lead to multiple misses.
0121 */
0122 
0123 /*
0124 2D Rotation
0125 
0126 R = [cos(theta) -sin(theta)]
0127     [sin(theta) cos(theta) ]
0128 
0129 thetaDot = omega
0130 
0131 Let q1 = cos(theta), q2 = sin(theta).
0132 R = [q1 -q2]
0133     [q2  q1]
0134 
0135 q1Dot = -thetaDot * q2
0136 q2Dot = thetaDot * q1
0137 
0138 q1_new = q1_old - dt * w * q2
0139 q2_new = q2_old + dt * w * q1
0140 then normalize.
0141 
0142 This might be faster than computing sin+cos.
0143 However, we can compute sin+cos of the same angle fast.
0144 */
0145 
0146 b2Island::b2Island(
0147     int32 bodyCapacity,
0148     int32 contactCapacity,
0149     int32 jointCapacity,
0150     b2StackAllocator* allocator,
0151     b2ContactListener* listener)
0152 {
0153     m_bodyCapacity = bodyCapacity;
0154     m_contactCapacity = contactCapacity;
0155     m_jointCapacity  = jointCapacity;
0156     m_bodyCount = 0;
0157     m_contactCount = 0;
0158     m_jointCount = 0;
0159 
0160     m_allocator = allocator;
0161     m_listener = listener;
0162 
0163     m_bodies = (b2Body**)m_allocator->Allocate(bodyCapacity * sizeof(b2Body*));
0164     m_contacts = (b2Contact**)m_allocator->Allocate(contactCapacity  * sizeof(b2Contact*));
0165     m_joints = (b2Joint**)m_allocator->Allocate(jointCapacity * sizeof(b2Joint*));
0166 
0167     m_velocities = (b2Velocity*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Velocity));
0168     m_positions = (b2Position*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Position));
0169 }
0170 
0171 b2Island::~b2Island()
0172 {
0173     // Warning: the order should reverse the constructor order.
0174     m_allocator->Free(m_positions);
0175     m_allocator->Free(m_velocities);
0176     m_allocator->Free(m_joints);
0177     m_allocator->Free(m_contacts);
0178     m_allocator->Free(m_bodies);
0179 }
0180 
0181 void b2Island::Solve(const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep)
0182 {
0183     // Integrate velocities and apply damping.
0184     for (int32 i = 0; i < m_bodyCount; ++i)
0185     {
0186         b2Body* b = m_bodies[i];
0187 
0188         if (b->GetType() != b2_dynamicBody)
0189         {
0190             continue;
0191         }
0192 
0193         // Integrate velocities.
0194         b->m_linearVelocity += step.dt * (gravity + b->m_invMass * b->m_force);
0195         b->m_angularVelocity += step.dt * b->m_invI * b->m_torque;
0196 
0197         // Apply damping.
0198         // ODE: dv/dt + c * v = 0
0199         // Solution: v(t) = v0 * exp(-c * t)
0200         // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
0201         // v2 = exp(-c * dt) * v1
0202         // Taylor expansion:
0203         // v2 = (1.0f - c * dt) * v1
0204         b->m_linearVelocity *= b2Clamp(1.0f - step.dt * b->m_linearDamping, 0.0f, 1.0f);
0205         b->m_angularVelocity *= b2Clamp(1.0f - step.dt * b->m_angularDamping, 0.0f, 1.0f);
0206     }
0207 
0208     // Partition contacts so that contacts with static bodies are solved last.
0209     int32 i1 = -1;
0210     for (int32 i2 = 0; i2 < m_contactCount; ++i2)
0211     {
0212         b2Fixture* fixtureA = m_contacts[i2]->GetFixtureA();
0213         b2Fixture* fixtureB = m_contacts[i2]->GetFixtureB();
0214         b2Body* bodyA = fixtureA->GetBody();
0215         b2Body* bodyB = fixtureB->GetBody();
0216         bool nonStatic = bodyA->GetType() != b2_staticBody && bodyB->GetType() != b2_staticBody;
0217         if (nonStatic)
0218         {
0219             ++i1;
0220             b2Swap(m_contacts[i1], m_contacts[i2]);
0221         }
0222     }
0223 
0224     // Initialize velocity constraints.
0225     b2ContactSolverDef solverDef;
0226     solverDef.contacts = m_contacts;
0227     solverDef.count = m_contactCount;
0228     solverDef.allocator = m_allocator;
0229     solverDef.impulseRatio = step.dtRatio;
0230     solverDef.warmStarting = step.warmStarting;
0231 
0232     b2ContactSolver contactSolver(&solverDef);
0233 
0234     contactSolver.InitializeVelocityConstraints();
0235 
0236     if (step.warmStarting)
0237     {
0238         contactSolver.WarmStart();
0239     }
0240     
0241     for (int32 i = 0; i < m_jointCount; ++i)
0242     {
0243         m_joints[i]->InitVelocityConstraints(step);
0244     }
0245 
0246     // Solve velocity constraints.
0247     for (int32 i = 0; i < step.velocityIterations; ++i)
0248     {
0249         for (int32 j = 0; j < m_jointCount; ++j)
0250         {
0251             m_joints[j]->SolveVelocityConstraints(step);
0252         }
0253 
0254         contactSolver.SolveVelocityConstraints();
0255     }
0256 
0257     // Post-solve (store impulses for warm starting).
0258     contactSolver.StoreImpulses();
0259 
0260     // Integrate positions.
0261     for (int32 i = 0; i < m_bodyCount; ++i)
0262     {
0263         b2Body* b = m_bodies[i];
0264 
0265         if (b->GetType() == b2_staticBody)
0266         {
0267             continue;
0268         }
0269 
0270         // Check for large velocities.
0271         b2Vec2 translation = step.dt * b->m_linearVelocity;
0272         if (b2Dot(translation, translation) > b2_maxTranslationSquared)
0273         {
0274             qreal ratio = b2_maxTranslation / translation.Length();
0275             b->m_linearVelocity *= ratio;
0276         }
0277 
0278         qreal rotation = step.dt * b->m_angularVelocity;
0279         if (rotation * rotation > b2_maxRotationSquared)
0280         {
0281             qreal ratio = b2_maxRotation / b2Abs(rotation);
0282             b->m_angularVelocity *= ratio;
0283         }
0284 
0285         // Store positions for continuous collision.
0286         b->m_sweep.c0 = b->m_sweep.c;
0287         b->m_sweep.a0 = b->m_sweep.a;
0288 
0289         // Integrate
0290         b->m_sweep.c += step.dt * b->m_linearVelocity;
0291         b->m_sweep.a += step.dt * b->m_angularVelocity;
0292 
0293         // Compute new transform
0294         b->SynchronizeTransform();
0295 
0296         // Note: shapes are synchronized later.
0297     }
0298 
0299     // Iterate over constraints.
0300     for (int32 i = 0; i < step.positionIterations; ++i)
0301     {
0302         bool contactsOkay = contactSolver.SolvePositionConstraints(b2_contactBaumgarte);
0303 
0304         bool jointsOkay = true;
0305         for (int32 i = 0; i < m_jointCount; ++i)
0306         {
0307             bool jointOkay = m_joints[i]->SolvePositionConstraints(b2_contactBaumgarte);
0308             jointsOkay = jointsOkay && jointOkay;
0309         }
0310 
0311         if (contactsOkay && jointsOkay)
0312         {
0313             // Exit early if the position errors are small.
0314             break;
0315         }
0316     }
0317 
0318     Report(contactSolver.m_constraints);
0319 
0320     if (allowSleep)
0321     {
0322         qreal minSleepTime = b2_maxFloat;
0323 
0324         const qreal linTolSqr = b2_linearSleepTolerance * b2_linearSleepTolerance;
0325         const qreal angTolSqr = b2_angularSleepTolerance * b2_angularSleepTolerance;
0326 
0327         for (int32 i = 0; i < m_bodyCount; ++i)
0328         {
0329             b2Body* b = m_bodies[i];
0330             if (b->GetType() == b2_staticBody)
0331             {
0332                 continue;
0333             }
0334 
0335             if ((b->m_flags & b2Body::e_autoSleepFlag) == 0)
0336             {
0337                 b->m_sleepTime = 0.0f;
0338                 minSleepTime = 0.0f;
0339             }
0340 
0341             if ((b->m_flags & b2Body::e_autoSleepFlag) == 0 ||
0342                 b->m_angularVelocity * b->m_angularVelocity > angTolSqr ||
0343                 b2Dot(b->m_linearVelocity, b->m_linearVelocity) > linTolSqr)
0344             {
0345                 b->m_sleepTime = 0.0f;
0346                 minSleepTime = 0.0f;
0347             }
0348             else
0349             {
0350                 b->m_sleepTime += step.dt;
0351                 minSleepTime = b2Min(minSleepTime, b->m_sleepTime);
0352             }
0353         }
0354 
0355         if (minSleepTime >= b2_timeToSleep)
0356         {
0357             for (int32 i = 0; i < m_bodyCount; ++i)
0358             {
0359                 b2Body* b = m_bodies[i];
0360                 b->SetAwake(false);
0361             }
0362         }
0363     }
0364 }
0365 
0366 void b2Island::SolveTOI(const b2TimeStep& subStep, const b2Body* bodyA, const b2Body* bodyB)
0367 {
0368     b2ContactSolverDef solverDef;
0369     solverDef.contacts = m_contacts;
0370     solverDef.count = m_contactCount;
0371     solverDef.allocator = m_allocator;
0372     solverDef.impulseRatio = subStep.dtRatio;
0373     solverDef.warmStarting = subStep.warmStarting;
0374     b2ContactSolver contactSolver(&solverDef);
0375 
0376     // Solve position constraints.
0377     const qreal k_toiBaumgarte = 0.75f;
0378     for (int32 i = 0; i < subStep.positionIterations; ++i)
0379     {
0380         bool contactsOkay = contactSolver.SolveTOIPositionConstraints(k_toiBaumgarte, bodyA, bodyB);
0381         if (contactsOkay)
0382         {
0383             break;
0384         }
0385 
0386         if (i == subStep.positionIterations - 1)
0387         {
0388             i += 0;
0389         }
0390     }
0391 
0392 #if 0
0393     // Is the new position really safe?
0394     for (int32 i = 0; i < m_contactCount; ++i)
0395     {
0396         b2Contact* c = m_contacts[i];
0397         b2Fixture* fA = c->GetFixtureA();
0398         b2Fixture* fB = c->GetFixtureB();
0399 
0400         b2Body* bA = fA->GetBody();
0401         b2Body* bB = fB->GetBody();
0402 
0403         int32 indexA = c->GetChildIndexA();
0404         int32 indexB = c->GetChildIndexB();
0405 
0406         b2DistanceInput input;
0407         input.proxyA.Set(fA->GetShape(), indexA);
0408         input.proxyB.Set(fB->GetShape(), indexB);
0409         input.transformA = bA->GetTransform();
0410         input.transformB = bB->GetTransform();
0411         input.useRadii = false;
0412 
0413         b2DistanceOutput output;
0414         b2SimplexCache cache;
0415         cache.count = 0;
0416         b2Distance(&output, &cache, &input);
0417 
0418         if (output.distance == 0 || cache.count == 3)
0419         {
0420             cache.count += 0;
0421         }
0422     }
0423 #endif
0424 
0425     // Leap of faith to new safe state.
0426     for (int32 i = 0; i < m_bodyCount; ++i)
0427     {
0428         m_bodies[i]->m_sweep.a0 = m_bodies[i]->m_sweep.a;
0429         m_bodies[i]->m_sweep.c0 = m_bodies[i]->m_sweep.c;
0430     }
0431 
0432     // No warm starting is needed for TOI events because warm
0433     // starting impulses were applied in the discrete solver.
0434     contactSolver.InitializeVelocityConstraints();
0435 
0436     // Solve velocity constraints.
0437     for (int32 i = 0; i < subStep.velocityIterations; ++i)
0438     {
0439         contactSolver.SolveVelocityConstraints();
0440     }
0441 
0442     // Don't store the TOI contact forces for warm starting
0443     // because they can be quite large.
0444 
0445     // Integrate positions.
0446     for (int32 i = 0; i < m_bodyCount; ++i)
0447     {
0448         b2Body* b = m_bodies[i];
0449 
0450         if (b->GetType() == b2_staticBody)
0451         {
0452             continue;
0453         }
0454 
0455         // Check for large velocities.
0456         b2Vec2 translation = subStep.dt * b->m_linearVelocity;
0457         if (b2Dot(translation, translation) > b2_maxTranslationSquared)
0458         {
0459             translation.Normalize();
0460             b->m_linearVelocity = (b2_maxTranslation * subStep.inv_dt) * translation;
0461         }
0462 
0463         qreal rotation = subStep.dt * b->m_angularVelocity;
0464         if (rotation * rotation > b2_maxRotationSquared)
0465         {
0466             if (rotation < 0.0)
0467             {
0468                 b->m_angularVelocity = -subStep.inv_dt * b2_maxRotation;
0469             }
0470             else
0471             {
0472                 b->m_angularVelocity = subStep.inv_dt * b2_maxRotation;
0473             }
0474         }
0475 
0476         // Integrate
0477         b->m_sweep.c += subStep.dt * b->m_linearVelocity;
0478         b->m_sweep.a += subStep.dt * b->m_angularVelocity;
0479 
0480         // Compute new transform
0481         b->SynchronizeTransform();
0482 
0483         // Note: shapes are synchronized later.
0484     }
0485 
0486     Report(contactSolver.m_constraints);
0487 }
0488 
0489 void b2Island::Report(const b2ContactConstraint* constraints)
0490 {
0491     if (m_listener == NULL)
0492     {
0493         return;
0494     }
0495 
0496     for (int32 i = 0; i < m_contactCount; ++i)
0497     {
0498         b2Contact* c = m_contacts[i];
0499 
0500         const b2ContactConstraint* cc = constraints + i;
0501         
0502         b2ContactImpulse impulse;
0503         for (int32 j = 0; j < cc->pointCount; ++j)
0504         {
0505             impulse.normalImpulses[j] = cc->points[j].normalImpulse;
0506             impulse.tangentImpulses[j] = cc->points[j].tangentImpulse;
0507         }
0508 
0509         m_listener->PostSolve(c, &impulse);
0510     }
0511 }