/// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors. // Pulley: // length1 = norm(p1 - s1) // length2 = norm(p2 - s2) // C0 = (length1 + ratio * length2)_initial // C = C0 - (length1 + ratio * length2) // u1 = (p1 - s1) / norm(p1 - s1) // u2 = (p2 - s2) / norm(p2 - s2) // Cdot = -dot(u1, v1 + cross(w1, r1)) - ratio * dot(u2, v2 + cross(w2, r2)) // J = -[u1 cross(r1, u1) ratio * u2 ratio * cross(r2, u2)] // K = J * invM * JT // = invMass1 + invI1 * cross(r1, u1)^2 + ratio^2 * (invMass2 + invI2 * cross(r2, u2)^2) public void Initialize(b2Body bA, b2Body bB, b2Vec2 groundA, b2Vec2 groundB, b2Vec2 anchorA, b2Vec2 anchorB, float r) { bodyA = bA; bodyB = bB; groundAnchorA = groundA; groundAnchorB = groundB; localAnchorA = bodyA.GetLocalPoint(anchorA); localAnchorB = bodyB.GetLocalPoint(anchorB); b2Vec2 dA = anchorA - groundA; lengthA = dA.Length(); b2Vec2 dB = anchorB - groundB; lengthB = dB.Length(); ratio = r; Debug.Assert(ratio > float.Epsilon); }
public override void SolveVelocityConstraints(b2SolverData data) { b2Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; // Cdot = v + cross(w, r) b2Vec2 Cdot = vB + b2Math.b2Cross(wB, m_rB); b2Vec2 impulse = b2Math.b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse)); b2Vec2 oldImpulse = m_impulse; m_impulse += impulse; float maxImpulse = data.step.dt * m_maxForce; if (m_impulse.LengthSquared() > maxImpulse * maxImpulse) { m_impulse *= maxImpulse / m_impulse.Length(); } impulse = m_impulse - oldImpulse; vB += m_invMassB * impulse; wB += m_invIB * b2Math.b2Cross(m_rB, impulse); data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
public virtual float GetLengthB() { b2Vec2 p = m_bodyB.GetWorldPoint(m_localAnchorB); b2Vec2 s = m_groundAnchorB; b2Vec2 d = p - s; return(d.Length()); }
/// Get the current length of the segment attached to bodyB. public float GetCurrentLengthB() { b2Vec2 p = m_bodyB.GetWorldPoint(m_localAnchorB); b2Vec2 s = new b2Vec2(m_groundAnchorB); b2Vec2 d = p - s; return(d.Length()); }
/// Initialize the bodies, anchors, and length using the world /// anchors. // 1-D constrained system // m (v2 - v1) = lambda // v2 + (beta/h) * x1 + gamma * lambda = 0, gamma has units of inverse mass. // x2 = x1 + h * v2 // 1-D mass-damper-spring system // m (v2 - v1) + h * d * v2 + h * k * // C = norm(p2 - p1) - L // u = (p2 - p1) / norm(p2 - p1) // Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1)) // J = [-u -cross(r1, u) u cross(r2, u)] // K = J * invM * JT // = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2 public void Initialize(b2Body b1, b2Body b2, b2Vec2 anchor1, b2Vec2 anchor2) { bodyA = b1; bodyB = b2; localAnchorA = bodyA.GetLocalPoint(anchor1); localAnchorB = bodyB.GetLocalPoint(anchor2); b2Vec2 d = anchor2 - anchor1; length = d.Length(); }
/// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors. public void Initialize(b2Body bA, b2Body bB, b2Vec2 groundA, b2Vec2 groundB, b2Vec2 anchorA, b2Vec2 anchorB, float r) { BodyA = bA; BodyB = bB; groundAnchorA = groundA; groundAnchorB = groundB; localAnchorA = BodyA.GetLocalPoint(anchorA); localAnchorB = BodyB.GetLocalPoint(anchorB); b2Vec2 dA = anchorA - groundA; lengthA = dA.Length(); b2Vec2 dB = anchorB - groundB; lengthB = dB.Length(); ratio = r; System.Diagnostics.Debug.Assert(ratio > b2Settings.b2_epsilon); }
public override void InitVelocityConstraints(b2SolverData data) { m_indexA = m_bodyA.IslandIndex; m_indexB = m_bodyB.IslandIndex; m_localCenterA = m_bodyA.Sweep.localCenter; m_localCenterB = m_bodyB.Sweep.localCenter; m_invMassA = m_bodyA.InvertedMass; m_invMassB = m_bodyB.InvertedMass; m_invIA = m_bodyA.InvertedI; m_invIB = m_bodyB.InvertedI; b2Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; b2Rot qA = new b2Rot(aA); b2Rot qB = new b2Rot(aB); m_rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB); m_u = cB + m_rB - cA - m_rA; m_length = m_u.Length(); float C = m_length - m_maxLength; if (C > 0.0f) { m_state = b2LimitState.e_atUpperLimit; } else { m_state = b2LimitState.e_inactiveLimit; } if (m_length > b2Settings.b2_linearSlop) { m_u *= 1.0f / m_length; } else { m_u.SetZero(); m_mass = 0.0f; m_impulse = 0.0f; return; } // Compute effective mass. float crA = b2Math.b2Cross(m_rA, m_u); float crB = b2Math.b2Cross(m_rB, m_u); float invMass = m_invMassA + m_invIA * crA * crA + m_invMassB + m_invIB * crB * crB; m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; if (data.step.warmStarting) { // Scale the impulse to support a variable time step. m_impulse *= data.step.dtRatio; b2Vec2 P = m_impulse * m_u; vA -= m_invMassA * P; wA -= m_invIA * b2Math.b2Cross(m_rA, P); vB += m_invMassB * P; wB += m_invIB * b2Math.b2Cross(m_rB, P); } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
public override bool SolvePositionConstraints(b2SolverData data) { b2Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; b2Rot qA = new b2Rot(aA); b2Rot qB = new b2Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; b2Vec2 rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB); float positionError, angularError; b2Vec3 ex = new b2Vec3(); b2Vec3 ey = new b2Vec3(); b2Vec3 ez = new b2Vec3(); ex.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; ey.x = -rA.y * rA.x * iA - rB.y * rB.x * iB; ez.x = -rA.y * iA - rB.y * iB; ex.y = ey.x; ey.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; ez.y = rA.x * iA + rB.x * iB; ex.z = ez.x; ey.z = ez.y; ez.z = iA + iB; b2Mat33 K = new b2Mat33(ex, ey, ez); if (m_frequencyHz > 0.0f) { b2Vec2 C1 = cB + rB - cA - rA; positionError = C1.Length(); angularError = 0.0f; b2Vec2 P = -K.Solve22(C1); cA -= mA * P; aA -= iA * b2Math.b2Cross(rA, P); cB += mB * P; aB += iB * b2Math.b2Cross(rB, P); } else { b2Vec2 C1 = cB + rB - cA - rA; float C2 = aB - aA - m_referenceAngle; positionError = C1.Length(); angularError = b2Math.b2Abs(C2); b2Vec3 C = new b2Vec3(C1.x, C1.y, C2); b2Vec3 impulse = -K.Solve33(C); b2Vec2 P = new b2Vec2(impulse.x, impulse.y); cA -= mA * P; aA -= iA * (b2Math.b2Cross(rA, P) + impulse.z); cB += mB * P; aB += iB * (b2Math.b2Cross(rB, P) + impulse.z); } data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return(positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop); }
public override void InitVelocityConstraints(b2SolverData data) { m_indexA = m_bodyA.IslandIndex; m_indexB = m_bodyB.IslandIndex; m_localCenterA = m_bodyA.Sweep.localCenter; m_localCenterB = m_bodyB.Sweep.localCenter; m_invMassA = m_bodyA.InvertedMass; m_invMassB = m_bodyB.InvertedMass; m_invIA = m_bodyA.InvertedI; m_invIB = m_bodyB.InvertedI; b2Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; b2Rot qA = new b2Rot(aA); b2Rot qB = new b2Rot(aB); m_rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB); // Get the pulley axes. m_uA = cA + m_rA - m_groundAnchorA; m_uB = cB + m_rB - m_groundAnchorB; float lengthA = m_uA.Length(); float lengthB = m_uB.Length(); if (lengthA > 10.0f * b2Settings.b2_linearSlop) { m_uA *= 1.0f / lengthA; } else { m_uA.SetZero(); } if (lengthB > 10.0f * b2Settings.b2_linearSlop) { m_uB *= 1.0f / lengthB; } else { m_uB.SetZero(); } // Compute effective mass. float ruA = b2Math.b2Cross(m_rA, m_uA); float ruB = b2Math.b2Cross(m_rB, m_uB); float mA = m_invMassA + m_invIA * ruA * ruA; float mB = m_invMassB + m_invIB * ruB * ruB; m_mass = mA + m_ratio * m_ratio * mB; if (m_mass > 0.0f) { m_mass = 1.0f / m_mass; } if (data.step.warmStarting) { // Scale impulses to support variable time steps. m_impulse *= data.step.dtRatio; // Warm starting. b2Vec2 PA = -(m_impulse) * m_uA; b2Vec2 PB = (-m_ratio * m_impulse) * m_uB; vA += m_invMassA * PA; wA += m_invIA * b2Math.b2Cross(m_rA, PA); vB += m_invMassB * PB; wB += m_invIB * b2Math.b2Cross(m_rB, PB); } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
public override bool SolvePositionConstraints(b2SolverData data) { b2Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; b2Rot qA = new b2Rot(aA); b2Rot qB = new b2Rot(aB); b2Vec2 rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB); // Get the pulley axes. b2Vec2 uA = cA + rA - m_groundAnchorA; b2Vec2 uB = cB + rB - m_groundAnchorB; float lengthA = uA.Length(); float lengthB = uB.Length(); if (lengthA > 10.0f * b2Settings.b2_linearSlop) { uA *= 1.0f / lengthA; } else { uA.SetZero(); } if (lengthB > 10.0f * b2Settings.b2_linearSlop) { uB *= 1.0f / lengthB; } else { uB.SetZero(); } // Compute effective mass. float ruA = b2Math.b2Cross(rA, uA); float ruB = b2Math.b2Cross(rB, uB); float mA = m_invMassA + m_invIA * ruA * ruA; float mB = m_invMassB + m_invIB * ruB * ruB; float mass = mA + m_ratio * m_ratio * mB; if (mass > 0.0f) { mass = 1.0f / mass; } float C = m_constant - lengthA - m_ratio * lengthB; float linearError = b2Math.b2Abs(C); float impulse = -mass * C; b2Vec2 PA = -impulse * uA; b2Vec2 PB = -m_ratio * impulse * uB; cA += m_invMassA * PA; aA += m_invIA * b2Math.b2Cross(rA, PA); cB += m_invMassB * PB; aB += m_invIB * b2Math.b2Cross(rB, PB); data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return(linearError < b2Settings.b2_linearSlop); }
public static float Distance(b2Vec2 a, b2Vec2 b) { b2Vec2 c = a - b; return(c.Length()); }
internal override bool SolvePositionConstraints(b2SolverData data) { b2Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; b2Rot qA = new b2Rot(aA); b2Rot qB = new b2Rot(aB); float angularError = 0.0f; float positionError = 0.0f; bool fixedRotation = (m_invIA + m_invIB == 0.0f); // Solve angular limit constraint. if (m_enableLimit && m_limitState != b2LimitState.e_inactiveLimit && fixedRotation == false) { float angle = aB - aA - m_referenceAngle; float limitImpulse = 0.0f; if (m_limitState == b2LimitState.e_equalLimits) { // Prevent large angular corrections float C = Utils.b2Clamp(angle - m_lowerAngle, -Settings.b2_maxAngularCorrection, Settings.b2_maxAngularCorrection); limitImpulse = -m_motorMass * C; angularError = Utils.b2Abs(C); } else if (m_limitState == b2LimitState.e_atLowerLimit) { float C = angle - m_lowerAngle; angularError = -C; // Prevent large angular corrections and allow some slop. C = Utils.b2Clamp(C + Settings.b2_angularSlop, -Settings.b2_maxAngularCorrection, 0.0f); limitImpulse = -m_motorMass * C; } else if (m_limitState == b2LimitState.e_atUpperLimit) { float C = angle - m_upperAngle; angularError = C; // Prevent large angular corrections and allow some slop. C = Utils.b2Clamp(C - Settings.b2_angularSlop, 0.0f, Settings.b2_maxAngularCorrection); limitImpulse = -m_motorMass * C; } aA -= m_invIA * limitImpulse; aB += m_invIB * limitImpulse; } { // Solve point-to-point constraint. qA.Set(aA); qB.Set(aB); b2Vec2 rA = Utils.b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = Utils.b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 C = cB + rB - cA - rA; positionError = C.Length(); float mA = m_invMassA; float mB = m_invMassB; float iA = m_invIA; float iB = m_invIB; b2Mat22 K = new b2Mat22(); K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y; K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y; K.ey.x = K.ex.y; K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x; b2Vec2 impulse = -K.Solve(C); cA -= mA * impulse; aA -= iA * Utils.b2Cross(rA, impulse); cB += mB * impulse; aB += iB * Utils.b2Cross(rB, impulse); } data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return(positionError <= Settings.b2_linearSlop && angularError <= Settings.b2_angularSlop); }
public void SolveTOI(b2TimeStep subStep, int toiIndexA, int toiIndexB) { Debug.Assert(toiIndexA < m_bodyCount); Debug.Assert(toiIndexB < m_bodyCount); // Initialize the body state. for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; m_positions[i].c = b.m_sweep.c; m_positions[i].a = b.m_sweep.a; m_velocities[i].v = b.m_linearVelocity; m_velocities[i].w = b.m_angularVelocity; } b2ContactSolverDef contactSolverDef = new b2ContactSolverDef(); contactSolverDef.contacts = m_contacts; contactSolverDef.count = m_contactCount; contactSolverDef.step = subStep; contactSolverDef.positions = m_positions; contactSolverDef.velocities = m_velocities; b2ContactSolver contactSolver = new b2ContactSolver(contactSolverDef); // Solve position constraints. for (int i = 0; i < subStep.positionIterations; ++i) { bool contactsOkay = contactSolver.SolveTOIPositionConstraints(toiIndexA, toiIndexB); if (contactsOkay) { break; } } #if false // // Is the new position really safe? // for (int32 i = 0; i < m_contactCount; ++i) // { // b2Contact* c = m_contacts[i]; // b2Fixture* fA = c->GetFixtureA(); // b2Fixture* fB = c->GetFixtureB(); // // b2Body* bA = fA->GetBody(); // b2Body* bB = fB->GetBody(); // // int32 indexA = c->GetChildIndexA(); // int32 indexB = c->GetChildIndexB(); // // b2DistanceInput input; // input.proxyA.Set(fA->GetShape(), indexA); // input.proxyB.Set(fB->GetShape(), indexB); // input.transformA = bA->GetTransform(); // input.transformB = bB->GetTransform(); // input.useRadii = false; // // b2DistanceOutput output; // b2SimplexCache cache; // cache.count = 0; // b2Distance(&output, &cache, &input); // // if (output.distance == 0 || cache.count == 3) // { // cache.count += 0; // } // } #endif // Leap of faith to new safe state. m_bodies[toiIndexA].m_sweep.c0 = m_positions[toiIndexA].c; m_bodies[toiIndexA].m_sweep.a0 = m_positions[toiIndexA].a; m_bodies[toiIndexB].m_sweep.c0 = m_positions[toiIndexB].c; m_bodies[toiIndexB].m_sweep.a0 = m_positions[toiIndexB].a; // No warm starting is needed for TOI events because warm // starting impulses were applied in the discrete solver. contactSolver.InitializeVelocityConstraints(); // Solve velocity constraints. for (int i = 0; i < subStep.velocityIterations; ++i) { contactSolver.SolveVelocityConstraints(); } // Don't store the TOI contact forces for warm starting // because they can be quite large. float h = subStep.dt; // Integrate positions for (int i = 0; i < m_bodyCount; ++i) { b2Vec2 c = m_positions[i].c; float a = m_positions[i].a; b2Vec2 v = m_velocities[i].v; float w = m_velocities[i].w; // Check for large velocities b2Vec2 translation = h * v; if (Utils.b2Dot(translation, translation) > (Settings.b2_maxTranslationSquared)) { float ratio = Settings.b2_maxTranslation / translation.Length(); v *= ratio; } float rotation = h * w; if (rotation * rotation > Settings.b2_maxRotationSquared) { float ratio = (0.5f * Settings.b2_pi) / Utils.b2Abs(rotation); w *= ratio; } // Integrate c += h * v; a += h * w; m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; // Sync bodies b2Body body = m_bodies[i]; body.m_sweep.c = c; body.m_sweep.a = a; body.m_linearVelocity = v; body.m_angularVelocity = w; body.SynchronizeTransform(); } Report(contactSolver.m_velocityConstraints); }
public void Solve(b2Profile profile, b2TimeStep step, b2Vec2 gravity, bool allowSleep) { b2Timer timer = new b2Timer(); float h = step.dt; // Integrate velocities and apply damping. Initialize the body state. for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; b2Vec2 c = new b2Vec2(b.m_sweep.c); float a = b.m_sweep.a; b2Vec2 v = new b2Vec2(b.m_linearVelocity); float w = b.m_angularVelocity; // Store positions for continuous collision. b.m_sweep.c0 = b.m_sweep.c; b.m_sweep.a0 = b.m_sweep.a; if (b.m_type == BodyType.b2_dynamicBody) { // Integrate velocities. v += h * (b.m_gravityScale * gravity + b.m_invMass * b.m_force); w += h * b.m_invI * b.m_torque; // Apply damping. // ODE: dv/dt + c * v = 0 // Solution: v(t) = v0 * exp(-c * t) // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) // v2 = exp(-c * dt) * v1 // Pade approximation: // v2 = v1 * 1 / (1 + c * dt) v *= 1.0f / (1.0f + h * b.m_linearDamping); w *= 1.0f / (1.0f + h * b.m_angularDamping); } m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; } timer.Reset(); // Solver data b2SolverData solverData = new b2SolverData(); solverData.step = step; solverData.positions = m_positions; solverData.velocities = m_velocities; // Initialize velocity constraints. b2ContactSolverDef contactSolverDef = new b2ContactSolverDef(); contactSolverDef.step = step; contactSolverDef.contacts = m_contacts; contactSolverDef.count = m_contactCount; contactSolverDef.positions = m_positions; contactSolverDef.velocities = m_velocities; b2ContactSolver contactSolver = new b2ContactSolver(contactSolverDef); contactSolver.InitializeVelocityConstraints(); if (step.warmStarting) { contactSolver.WarmStart(); } for (int i = 0; i < m_jointCount; ++i) { m_joints[i].InitVelocityConstraints(solverData); } profile.solveInit = timer.GetMilliseconds(); // Solve velocity constraints timer.Reset(); for (int i = 0; i < step.velocityIterations; ++i) { for (int j = 0; j < m_jointCount; ++j) { m_joints[j].SolveVelocityConstraints(solverData); } contactSolver.SolveVelocityConstraints(); } // Store impulses for warm starting contactSolver.StoreImpulses(); profile.solveVelocity = timer.GetMilliseconds(); // Integrate positions for (int i = 0; i < m_bodyCount; ++i) { b2Vec2 c = m_positions[i].c; float a = m_positions[i].a; b2Vec2 v = m_velocities[i].v; float w = m_velocities[i].w; // Check for large velocities b2Vec2 translation = h * v; if (Utils.b2Dot(translation, translation) > (Settings.b2_maxTranslation * Settings.b2_maxTranslation)) { float ratio = Settings.b2_maxTranslation / translation.Length(); v *= ratio; } float rotation = h * w; if (rotation * rotation > Settings.b2_maxRotationSquared) { float ratio = (0.5f * Settings.b2_pi) / Utils.b2Abs(rotation); w *= ratio; } // Integrate c += h * v; a += h * w; m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; } // Solve position constraints timer.Reset(); bool positionSolved = false; for (int i = 0; i < step.positionIterations; ++i) { bool contactsOkay = contactSolver.SolvePositionConstraints(); bool jointsOkay = true; for (int j = 0; j < m_jointCount; ++j) { bool jointOkay = m_joints[j].SolvePositionConstraints(solverData); jointsOkay = jointsOkay && jointOkay; } if (contactsOkay && jointsOkay) { // Exit early if the position errors are small. positionSolved = true; break; } } // Copy state buffers back to the bodies for (int i = 0; i < m_bodyCount; ++i) { b2Body body = m_bodies[i]; body.m_sweep.c = m_positions[i].c; body.m_sweep.a = m_positions[i].a; body.m_linearVelocity = m_velocities[i].v; body.m_angularVelocity = m_velocities[i].w; body.SynchronizeTransform(); } profile.solvePosition = timer.GetMilliseconds(); Report(contactSolver.m_velocityConstraints); if (allowSleep) { float minSleepTime = float.MaxValue; const float linTolSqr = Settings.b2_linearSleepTolerance * Settings.b2_linearSleepTolerance; float angTolSqr = Settings.b2_angularSlop * Settings.b2_angularSlop; for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; if (b.GetType() == BodyType.b2_staticBody) { continue; } if ((b.m_flags & b2Body.BodyFlags.e_autoSleepFlag) == 0 || b.m_angularVelocity * b.m_angularVelocity > angTolSqr || Utils.b2Dot(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr) { b.m_sleepTime = 0.0f; minSleepTime = 0.0f; } else { b.m_sleepTime += h; minSleepTime = Utils.b2Min(minSleepTime, b.m_sleepTime); } } if (minSleepTime >= Settings.b2_timeToSleep && positionSolved) { for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; b.SetAwake(false); } } } }
public override void InitVelocityConstraints(b2SolverData data) { m_indexA = m_bodyA.IslandIndex; m_indexB = m_bodyB.IslandIndex; m_localCenterA = m_bodyA.Sweep.localCenter; m_localCenterB = m_bodyB.Sweep.localCenter; m_invMassA = m_bodyA.InvertedMass; m_invMassB = m_bodyB.InvertedMass; m_invIA = m_bodyA.InvertedI; m_invIB = m_bodyB.InvertedI; b2Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; b2Rot qA = new b2Rot(aA); b2Rot qB = new b2Rot(aB); m_rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB); m_u = cB + m_rB - cA - m_rA; // Handle singularity. float length = m_u.Length(); if (length > b2Settings.b2_linearSlop) { m_u *= 1.0f / length; } else { m_u.Set(0.0f, 0.0f); } float crAu = b2Math.b2Cross(m_rA, m_u); float crBu = b2Math.b2Cross(m_rB, m_u); float invMass = m_invMassA + m_invIA * crAu * crAu + m_invMassB + m_invIB * crBu * crBu; // Compute the effective mass matrix. m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; if (m_frequencyHz > 0.0f) { float C = length - m_length; // Frequency float omega = 2.0f * (float)Math.PI * m_frequencyHz; // Damping coefficient float d = 2.0f * m_mass * m_dampingRatio * omega; // Spring stiffness float k = m_mass * omega * omega; // magic formulas float h = data.step.dt; m_gamma = h * (d + h * k); m_gamma = m_gamma != 0.0f ? 1.0f / m_gamma : 0.0f; m_bias = C * h * k * m_gamma; invMass += m_gamma; m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; } else { m_gamma = 0.0f; m_bias = 0.0f; } if (data.step.warmStarting) { // Scale the impulse to support a variable time step. m_impulse *= data.step.dtRatio; b2Vec2 P = m_impulse * m_u; vA -= m_invMassA * P; wA -= m_invIA * b2Math.b2Cross(m_rA, P); vB += m_invMassB * P; wB += m_invIB * b2Math.b2Cross(m_rB, P); } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
public void Solve(ref b2Profile profile, b2TimeStep step, b2Vec2 gravity, bool allowSleep) { b2Timer timer = new b2Timer(); float h = step.dt; // Integrate velocities and apply damping. Initialize the body state. for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; b2Vec2 c = b.Sweep.c; float a = b.Sweep.a; b2Vec2 v = b.LinearVelocity; float w = b.AngularVelocity; // Store positions for continuous collision. b.Sweep.c0 = b.Sweep.c; b.Sweep.a0 = b.Sweep.a; if (b.BodyType == b2BodyType.b2_dynamicBody) { // Integrate velocities. v += h * (b.GravityScale * gravity + b.InvertedMass * b.Force); w += h * b.InvertedI * b.Torque; // Apply damping. // ODE: dv/dt + c * v = 0 // Solution: v(t) = v0 * exp(-c * t) // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) // v2 = exp(-c * dt) * v1 // Taylor expansion: // v2 = (1.0f - c * dt) * v1 v *= b2Math.b2Clamp(1.0f - h * b.LinearDamping, 0.0f, 1.0f); w *= b2Math.b2Clamp(1.0f - h * b.AngularDamping, 0.0f, 1.0f); } m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; } timer.Reset(); // Solver data b2SolverData solverData = new b2SolverData(); solverData.step = step; solverData.positions = m_positions; solverData.velocities = m_velocities; // Initialize velocity constraints. b2ContactSolverDef contactSolverDef; contactSolverDef.step = step; contactSolverDef.contacts = m_contacts; contactSolverDef.count = m_contactCount; contactSolverDef.positions = m_positions; contactSolverDef.velocities = m_velocities; b2ContactSolver contactSolver = new b2ContactSolver(contactSolverDef); contactSolver.InitializeVelocityConstraints(); if (step.warmStarting) { contactSolver.WarmStart(); } for (int i = 0; i < m_jointCount; ++i) { m_joints[i].InitVelocityConstraints(solverData); } profile.solveInit = timer.GetMilliseconds(); // Solve velocity constraints timer.Reset(); for (int i = 0; i < step.velocityIterations; ++i) { for (int j = 0; j < m_jointCount; ++j) { m_joints[j].SolveVelocityConstraints(solverData); } contactSolver.SolveVelocityConstraints(); } // Store impulses for warm starting contactSolver.StoreImpulses(); profile.solveVelocity = timer.GetMilliseconds(); // Integrate positions for (int i = 0; i < m_bodyCount; ++i) { b2Vec2 c = m_positions[i].c; float a = m_positions[i].a; b2Vec2 v = m_velocities[i].v; float w = m_velocities[i].w; // Check for large velocities b2Vec2 translation = h * v; if (b2Math.b2Dot(translation, translation) > b2Settings.b2_maxTranslationSquared) { float ratio = b2Settings.b2_maxTranslation / translation.Length(); v *= ratio; } float rotation = h * w; if (rotation * rotation > b2Settings.b2_maxRotationSquared) { float ratio = b2Settings.b2_maxRotation / Math.Abs(rotation); w *= ratio; } // Integrate c += h * v; a += h * w; m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; } // Solve position constraints timer.Reset(); bool positionSolved = false; for (int i = 0; i < step.positionIterations; ++i) { bool contactsOkay = contactSolver.SolvePositionConstraints(); bool jointsOkay = true; for (int i2 = 0; i2 < m_jointCount; ++i2) { bool jointOkay = m_joints[i2].SolvePositionConstraints(solverData); jointsOkay = jointsOkay && jointOkay; } if (contactsOkay && jointsOkay) { // Exit early if the position errors are small. positionSolved = true; break; } } // Copy state buffers back to the bodies for (int i = 0; i < m_bodyCount; ++i) { b2Body body = m_bodies[i]; body.Sweep.c = m_positions[i].c; body.Sweep.a = m_positions[i].a; body.LinearVelocity = m_velocities[i].v; body.AngularVelocity = m_velocities[i].w; body.SynchronizeTransform(); } profile.solvePosition = timer.GetMilliseconds(); Report(contactSolver.m_velocityConstraints); if (allowSleep) { float minSleepTime = b2Settings.b2_maxFloat; float linTolSqr = b2Settings.b2_linearSleepTolerance * b2Settings.b2_linearSleepTolerance; float angTolSqr = b2Settings.b2_angularSleepTolerance * b2Settings.b2_angularSleepTolerance; for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; if (b.BodyType == b2BodyType.b2_staticBody) { continue; } if (!(b.BodyFlags.HasFlag(b2BodyFlags.e_autoSleepFlag)) || b.AngularVelocity * b.AngularVelocity > angTolSqr || b2Math.b2Dot(b.LinearVelocity, b.LinearVelocity) > linTolSqr) { b.SleepTime = 0.0f; minSleepTime = 0.0f; } else { b.SleepTime += h; minSleepTime = Math.Min(minSleepTime, b.SleepTime); } } if (minSleepTime >= b2Settings.b2_timeToSleep && positionSolved) { for (int i = 0; i < m_bodyCount; ++i) { b2Body b = m_bodies[i]; b.SetAwake(false); } } } }