/// <summary> /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. /// </summary> /// <param name="b">The b.</param> /// <returns></returns> public FVector3 Solve33(FVector3 b) { float det = FVector3.Dot(ex, FVector3.Cross(ey, ez)); if (det != 0.0f) { det = 1.0f / det; } return(new FVector3(det * FVector3.Dot(b, FVector3.Cross(ey, ez)), det * FVector3.Dot(ex, FVector3.Cross(b, ez)), det * FVector3.Dot(ex, FVector3.Cross(ey, b)))); }
/// <summary> /// Initialize the bodies and local anchor. /// This requires defining an /// anchor point where the bodies are joined. The definition /// uses local anchor points so that the initial configuration /// can violate the constraint slightly. You also need to /// specify the initial relative angle for joint limits. This /// helps when saving and loading a game. /// The local anchor points are measured from the body's origin /// rather than the center of mass because: /// 1. you might not know where the center of mass will be. /// 2. if you add/remove shapes from a body and recompute the mass, /// the joints will be broken. /// </summary> /// <param name="bodyA">The first body.</param> /// <param name="bodyB">The second body.</param> /// <param name="localAnchorA">The first body anchor.</param> /// <param name="localAnchorB">The second anchor.</param> public FSRevoluteJoint(FSBody bodyA, FSBody bodyB, FVector2 localAnchorA, FVector2 localAnchorB) : base(bodyA, bodyB) { JointType = JointType.Revolute; // Changed to local coordinates. LocalAnchorA = localAnchorA; LocalAnchorB = localAnchorB; ReferenceAngle = BodyB.Rotation - BodyA.Rotation; _impulse = FVector3.Zero; _limitState = LimitState.Inactive; }
/// <summary> /// Initialize the bodies and world anchor. /// </summary> /// <param name="bodyA">The first body.</param> /// <param name="bodyB">The second body.</param> /// <param name="worldAnchor">The world coordinate anchor.</param> public RevoluteJoint(Body bodyA, Body bodyB, FVector2 worldAnchor) : base(bodyA, bodyB) { JointType = JointType.Revolute; // Changed to local coordinates. LocalAnchorA = bodyA.GetLocalPoint(worldAnchor); LocalAnchorB = bodyB.GetLocalPoint(worldAnchor); ReferenceAngle = BodyB.Rotation - BodyA.Rotation; _impulse = FVector3.Zero; _limitState = LimitState.Inactive; }
internal override bool SolvePositionConstraints(ref SolverData data) { FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA), qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; FVector2 rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); FVector2 rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); float positionError, angularError; Mat33 K = new Mat33(); K.ex.X = mA + mB + rA.Y * rA.Y * iA + rB.Y * rB.Y * iB; K.ey.X = -rA.Y * rA.X * iA - rB.Y * rB.X * iB; K.ez.X = -rA.Y * iA - rB.Y * iB; K.ex.Y = K.ey.X; K.ey.Y = mA + mB + rA.X * rA.X * iA + rB.X * rB.X * iB; K.ez.Y = rA.X * iA + rB.X * iB; K.ex.Z = K.ez.X; K.ey.Z = K.ez.Y; K.ez.Z = iA + iB; if (m_frequencyHz > 0.0f) { FVector2 C1 = cB + rB - cA - rA; positionError = C1.Length(); angularError = 0.0f; FVector2 P = -K.Solve22(C1); cA -= mA * P; aA -= iA * MathUtils.Cross(rA, P); cB += mB * P; aB += iB * MathUtils.Cross(rB, P); } else { FVector2 C1 = cB + rB - cA - rA; float C2 = aB - aA - ReferenceAngle; positionError = C1.Length(); angularError = Math.Abs(C2); FVector3 C = new FVector3(C1.X, C1.Y, C2); FVector3 impulse = -K.Solve33(C); FVector2 P = new FVector2(impulse.X, impulse.Y); cA -= mA * P; aA -= iA * (MathUtils.Cross(rA, P) + impulse.Z); cB += mB * P; aB += iB * (MathUtils.Cross(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 <= Settings.LinearSlop && angularError <= Settings.AngularSlop); }
internal override void InitVelocityConstraints(ref SolverData data) { m_indexA = BodyA.IslandIndex; m_indexB = BodyB.IslandIndex; m_localCenterA = BodyA.Sweep.LocalCenter; m_localCenterB = BodyB.Sweep.LocalCenter; m_invMassA = BodyA.InvMass; m_invMassB = BodyB.InvMass; m_invIA = BodyA.InvI; m_invIB = BodyB.InvI; //FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; //FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Rot qA = new Rot(aA), qB = new Rot(aB); m_rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); m_rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; bool fixedRotation = (iA + iB == 0.0f); m_mass.ex.X = mA + mB + m_rA.Y * m_rA.Y * iA + m_rB.Y * m_rB.Y * iB; m_mass.ey.X = -m_rA.Y * m_rA.X * iA - m_rB.Y * m_rB.X * iB; m_mass.ez.X = -m_rA.Y * iA - m_rB.Y * iB; m_mass.ex.Y = m_mass.ey.X; m_mass.ey.Y = mA + mB + m_rA.X * m_rA.X * iA + m_rB.X * m_rB.X * iB; m_mass.ez.Y = m_rA.X * iA + m_rB.X * iB; m_mass.ex.Z = m_mass.ez.X; m_mass.ey.Z = m_mass.ez.Y; m_mass.ez.Z = iA + iB; m_motorMass = iA + iB; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } if (_enableMotor == false || fixedRotation) { _motorImpulse = 0.0f; } if (_enableLimit && fixedRotation == false) { float jointAngle = aB - aA - ReferenceAngle; if (Math.Abs(_upperAngle - _lowerAngle) < 2.0f * Settings.AngularSlop) { _limitState = LimitState.Equal; } else if (jointAngle <= _lowerAngle) { if (_limitState != LimitState.AtLower) { _impulse.Z = 0.0f; } _limitState = LimitState.AtLower; } else if (jointAngle >= _upperAngle) { if (_limitState != LimitState.AtUpper) { _impulse.Z = 0.0f; } _limitState = LimitState.AtUpper; } else { _limitState = LimitState.Inactive; _impulse.Z = 0.0f; } } else { _limitState = LimitState.Inactive; } if (Settings.EnableWarmstarting) { // Scale impulses to support a variable time step. _impulse *= data.step.dtRatio; _motorImpulse *= data.step.dtRatio; FVector2 P = new FVector2(_impulse.X, _impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + MotorImpulse + _impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + MotorImpulse + _impulse.Z); } else { _impulse = FVector3.Zero; _motorImpulse = 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; }
internal override bool SolvePositionConstraints(ref SolverData data) { FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA), qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; FVector2 rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); FVector2 rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); float positionError, angularError; Mat33 K = new Mat33(); K.ex.X = mA + mB + rA.Y * rA.Y * iA + rB.Y * rB.Y * iB; K.ey.X = -rA.Y * rA.X * iA - rB.Y * rB.X * iB; K.ez.X = -rA.Y * iA - rB.Y * iB; K.ex.Y = K.ey.X; K.ey.Y = mA + mB + rA.X * rA.X * iA + rB.X * rB.X * iB; K.ez.Y = rA.X * iA + rB.X * iB; K.ex.Z = K.ez.X; K.ey.Z = K.ez.Y; K.ez.Z = iA + iB; if (m_frequencyHz > 0.0f) { FVector2 C1 = cB + rB - cA - rA; positionError = C1.Length(); angularError = 0.0f; FVector2 P = -K.Solve22(C1); cA -= mA * P; aA -= iA * MathUtils.Cross(rA, P); cB += mB * P; aB += iB * MathUtils.Cross(rB, P); } else { FVector2 C1 = cB + rB - cA - rA; float C2 = aB - aA - ReferenceAngle; positionError = C1.Length(); angularError = Math.Abs(C2); FVector3 C = new FVector3(C1.X, C1.Y, C2); FVector3 impulse = -K.Solve33(C); FVector2 P = new FVector2(impulse.X, impulse.Y); cA -= mA * P; aA -= iA * (MathUtils.Cross(rA, P) + impulse.Z); cB += mB * P; aB += iB * (MathUtils.Cross(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 <= FSSettings.LinearSlop && angularError <= FSSettings.AngularSlop; }
internal override void InitVelocityConstraints(ref SolverData data) { m_indexA = BodyA.IslandIndex; m_indexB = BodyB.IslandIndex; m_localCenterA = BodyA.Sweep.LocalCenter; m_localCenterB = BodyB.Sweep.LocalCenter; m_invMassA = BodyA.InvMass; m_invMassB = BodyB.InvMass; m_invIA = BodyA.InvI; m_invIB = BodyB.InvI; //FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; //FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Rot qA = new Rot(aA), qB = new Rot(aB); m_rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); m_rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; Mat33 K = new Mat33(); K.ex.X = mA + mB + m_rA.Y * m_rA.Y * iA + m_rB.Y * m_rB.Y * iB; K.ey.X = -m_rA.Y * m_rA.X * iA - m_rB.Y * m_rB.X * iB; K.ez.X = -m_rA.Y * iA - m_rB.Y * iB; K.ex.Y = K.ey.X; K.ey.Y = mA + mB + m_rA.X * m_rA.X * iA + m_rB.X * m_rB.X * iB; K.ez.Y = m_rA.X * iA + m_rB.X * iB; K.ex.Z = K.ez.X; K.ey.Z = K.ez.Y; K.ez.Z = iA + iB; if (m_frequencyHz > 0.0f) { K.GetInverse22(ref _mass); float invM = iA + iB; float m = invM > 0.0f ? 1.0f / invM : 0.0f; float C = aB - aA - ReferenceAngle; // Frequency float omega = 2.0f * Settings.Pi * m_frequencyHz; // Damping coefficient float d = 2.0f * m * m_dampingRatio * omega; // Spring stiffness float k = m * 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; invM += m_gamma; _mass.ez.Z = invM != 0.0f ? 1.0f / invM : 0.0f; } else { K.GetSymInverse33(ref _mass); m_gamma = 0.0f; m_bias = 0.0f; } if (Settings.EnableWarmstarting) { // Scale impulses to support a variable time step. _impulse *= data.step.dtRatio; FVector2 P = new FVector2(_impulse.X, _impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + _impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + _impulse.Z); } else { _impulse = FVector3.Zero; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
/// <summary> /// Set this matrix to all zeros. /// </summary> public void SetZero() { ex = FVector3.Zero; ey = FVector3.Zero; ez = FVector3.Zero; }
internal override void InitVelocityConstraints(ref SolverData data) { m_indexA = BodyA.IslandIndex; m_indexB = BodyB.IslandIndex; m_localCenterA = BodyA.Sweep.LocalCenter; m_localCenterB = BodyB.Sweep.LocalCenter; m_invMassA = BodyA.InvMass; m_invMassB = BodyB.InvMass; m_invIA = BodyA.InvI; m_invIB = BodyB.InvI; //FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; //FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Rot qA = new Rot(aA), qB = new Rot(aB); m_rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); m_rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; Mat33 K = new Mat33(); K.ex.X = mA + mB + m_rA.Y * m_rA.Y * iA + m_rB.Y * m_rB.Y * iB; K.ey.X = -m_rA.Y * m_rA.X * iA - m_rB.Y * m_rB.X * iB; K.ez.X = -m_rA.Y * iA - m_rB.Y * iB; K.ex.Y = K.ey.X; K.ey.Y = mA + mB + m_rA.X * m_rA.X * iA + m_rB.X * m_rB.X * iB; K.ez.Y = m_rA.X * iA + m_rB.X * iB; K.ex.Z = K.ez.X; K.ey.Z = K.ez.Y; K.ez.Z = iA + iB; if (m_frequencyHz > 0.0f) { K.GetInverse22(ref _mass); float invM = iA + iB; float m = invM > 0.0f ? 1.0f / invM : 0.0f; float C = aB - aA - ReferenceAngle; // Frequency float omega = 2.0f * FSSettings.Pi * m_frequencyHz; // Damping coefficient float d = 2.0f * m * m_dampingRatio * omega; // Spring stiffness float k = m * 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; invM += m_gamma; _mass.ez.Z = invM != 0.0f ? 1.0f / invM : 0.0f; } else { K.GetSymInverse33(ref _mass); m_gamma = 0.0f; m_bias = 0.0f; } if (FSSettings.EnableWarmstarting) { // Scale impulses to support a variable time step. _impulse *= data.step.dtRatio; FVector2 P = new FVector2(_impulse.X, _impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + _impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + _impulse.Z); } else { _impulse = FVector3.Zero; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
/// Multiply a matrix times a vector. public static FVector3 Mul(Mat33 A, FVector3 v) { return(v.X * A.ex + v.Y * A.ey + v.Z * A.ez); }
internal override void InitVelocityConstraints(ref SolverData data) { m_indexA = BodyA.IslandIndex; m_indexB = BodyB.IslandIndex; m_localCenterA = BodyA.Sweep.LocalCenter; m_localCenterB = BodyB.Sweep.LocalCenter; m_invMassA = BodyA.InvMass; m_invMassB = BodyB.InvMass; m_invIA = BodyA.InvI; m_invIB = BodyB.InvI; FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Rot qA = new Rot(aA), qB = new Rot(aB); // Compute the effective masses. FVector2 rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); FVector2 rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); FVector2 d = (cB - cA) + rB - rA; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Compute motor Jacobian and effective mass. { m_axis = MathUtils.Mul(qA, LocalXAxisA); m_a1 = MathUtils.Cross(d + rA, m_axis); m_a2 = MathUtils.Cross(rB, m_axis); m_motorMass = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } } // Prismatic constraint. { m_perp = MathUtils.Mul(qA, _localYAxisA); m_s1 = MathUtils.Cross(d + rA, m_perp); m_s2 = MathUtils.Cross(rB, m_perp); float k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2; float k12 = iA * m_s1 + iB * m_s2; float k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2; float k22 = iA + iB; if (k22 == 0.0f) { // For bodies with fixed rotation. k22 = 1.0f; } float k23 = iA * m_a1 + iB * m_a2; float k33 = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; m_K.ex = new FVector3(k11, k12, k13); m_K.ey = new FVector3(k12, k22, k23); m_K.ez = new FVector3(k13, k23, k33); } // Compute motor and limit terms. if (_enableLimit) { float jointTranslation = FVector2.Dot(m_axis, d); if (Math.Abs(_upperTranslation - _lowerTranslation) < 2.0f * FSSettings.LinearSlop) { _limitState = LimitState.Equal; } else if (jointTranslation <= _lowerTranslation) { if (_limitState != LimitState.AtLower) { _limitState = LimitState.AtLower; _impulse.Z = 0.0f; } } else if (jointTranslation >= _upperTranslation) { if (_limitState != LimitState.AtUpper) { _limitState = LimitState.AtUpper; _impulse.Z = 0.0f; } } else { _limitState = LimitState.Inactive; _impulse.Z = 0.0f; } } else { _limitState = LimitState.Inactive; } if (_enableMotor == false) { _motorImpulse = 0.0f; } if (FSSettings.EnableWarmstarting) { // Account for variable time step. _impulse *= data.step.dtRatio; _motorImpulse *= data.step.dtRatio; FVector2 P = _impulse.X * m_perp + (_motorImpulse + _impulse.Z) * m_axis; float LA = _impulse.X * m_s1 + _impulse.Y + (_motorImpulse + _impulse.Z) * m_a1; float LB = _impulse.X * m_s2 + _impulse.Y + (_motorImpulse + _impulse.Z) * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } else { _impulse = FVector3.Zero; _motorImpulse = 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; }
internal override bool SolvePositionConstraints(ref SolverData data) { FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA), qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Compute fresh Jacobians FVector2 rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); FVector2 rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); FVector2 d = cB + rB - cA - rA; FVector2 axis = MathUtils.Mul(qA, LocalXAxisA); float a1 = MathUtils.Cross(d + rA, axis); float a2 = MathUtils.Cross(rB, axis); FVector2 perp = MathUtils.Mul(qA, _localYAxisA); float s1 = MathUtils.Cross(d + rA, perp); float s2 = MathUtils.Cross(rB, perp); FVector3 impulse; FVector2 C1 = new FVector2(); C1.X = FVector2.Dot(perp, d); C1.Y = aB - aA - m_referenceAngle; float linearError = Math.Abs(C1.X); float angularError = Math.Abs(C1.Y); bool active = false; float C2 = 0.0f; if (_enableLimit) { float translation = FVector2.Dot(axis, d); if (Math.Abs(_upperTranslation - _lowerTranslation) < 2.0f * Settings.LinearSlop) { // Prevent large angular corrections C2 = MathUtils.Clamp(translation, -Settings.MaxLinearCorrection, Settings.MaxLinearCorrection); linearError = Math.Max(linearError, Math.Abs(translation)); active = true; } else if (translation <= _lowerTranslation) { // Prevent large linear corrections and allow some slop. C2 = MathUtils.Clamp(translation - _lowerTranslation + Settings.LinearSlop, -Settings.MaxLinearCorrection, 0.0f); linearError = Math.Max(linearError, _lowerTranslation - translation); active = true; } else if (translation >= _upperTranslation) { // Prevent large linear corrections and allow some slop. C2 = MathUtils.Clamp(translation - _upperTranslation - Settings.LinearSlop, 0.0f, Settings.MaxLinearCorrection); linearError = Math.Max(linearError, translation - _upperTranslation); active = true; } } if (active) { float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; float k12 = iA * s1 + iB * s2; float k13 = iA * s1 * a1 + iB * s2 * a2; float k22 = iA + iB; if (k22 == 0.0f) { // For fixed rotation k22 = 1.0f; } float k23 = iA * a1 + iB * a2; float k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2; Mat33 K = new Mat33(); K.ex = new FVector3(k11, k12, k13); K.ey = new FVector3(k12, k22, k23); K.ez = new FVector3(k13, k23, k33); FVector3 C = new FVector3(); C.X = C1.X; C.Y = C1.Y; C.Z = C2; impulse = K.Solve33(-C); } else { float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; float k12 = iA * s1 + iB * s2; float k22 = iA + iB; if (k22 == 0.0f) { k22 = 1.0f; } Mat22 K = new Mat22(); K.ex = new FVector2(k11, k12); K.ey = new FVector2(k12, k22); FVector2 impulse1 = K.Solve(-C1); impulse = new FVector3(); impulse.X = impulse1.X; impulse.Y = impulse1.Y; impulse.Z = 0.0f; } FVector2 P = impulse.X * perp + impulse.Z * axis; float LA = impulse.X * s1 + impulse.Y + impulse.Z * a1; float LB = impulse.X * s2 + impulse.Y + impulse.Z * a2; cA -= mA * P; aA -= iA * LA; cB += mB * P; aB += iB * LB; 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 <= Settings.LinearSlop && angularError <= Settings.AngularSlop; }
internal override void SolveVelocityConstraints(ref SolverData data) { FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Solve linear motor constraint. if (_enableMotor && _limitState != LimitState.Equal) { float Cdot = FVector2.Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; float impulse = m_motorMass * (_motorSpeed - Cdot); float oldImpulse = _motorImpulse; float maxImpulse = data.step.dt * _maxMotorForce; _motorImpulse = MathUtils.Clamp(_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = _motorImpulse - oldImpulse; FVector2 P = impulse * m_axis; float LA = impulse * m_a1; float LB = impulse * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } FVector2 Cdot1 = new FVector2(); Cdot1.X = FVector2.Dot(m_perp, vB - vA) + m_s2 * wB - m_s1 * wA; Cdot1.Y = wB - wA; if (_enableLimit && _limitState != LimitState.Inactive) { // Solve prismatic and limit constraint in block form. float Cdot2; Cdot2 = FVector2.Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; FVector3 Cdot = new FVector3(Cdot1.X, Cdot1.Y, Cdot2); FVector3 f1 = _impulse; FVector3 df = m_K.Solve33(-Cdot); _impulse += df; if (_limitState == LimitState.AtLower) { _impulse.Z = Math.Max(_impulse.Z, 0.0f); } else if (_limitState == LimitState.AtUpper) { _impulse.Z = Math.Min(_impulse.Z, 0.0f); } // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2) FVector2 b = -Cdot1 - (_impulse.Z - f1.Z) * new FVector2(m_K.ez.X, m_K.ez.Y); FVector2 f2r = m_K.Solve22(b) + new FVector2(f1.X, f1.Y); _impulse.X = f2r.X; _impulse.Y = f2r.Y; df = _impulse - f1; FVector2 P = df.X * m_perp + df.Z * m_axis; float LA = df.X * m_s1 + df.Y + df.Z * m_a1; float LB = df.X * m_s2 + df.Y + df.Z * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } else { // Limit is inactive, just solve the prismatic constraint in block form. FVector2 df = m_K.Solve22(-Cdot1); _impulse.X += df.X; _impulse.Y += df.Y; FVector2 P = df.X * m_perp; float LA = df.X * m_s1 + df.Y; float LB = df.X * m_s2 + df.Y; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; //FVector2 Cdot10 = Cdot1; Cdot1.X = FVector2.Dot(m_perp, vB - vA) + m_s2 * wB - m_s1 * wA; Cdot1.Y = wB - wA; if (Math.Abs(Cdot1.X) > 0.01f || Math.Abs(Cdot1.Y) > 0.01f) { //FVector2 test = MathUtils.Mul22(m_K, df); Cdot1.X += 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; }
internal override void InitVelocityConstraints(ref SolverData data) { m_indexA = BodyA.IslandIndex; m_indexB = BodyB.IslandIndex; m_localCenterA = BodyA.Sweep.LocalCenter; m_localCenterB = BodyB.Sweep.LocalCenter; m_invMassA = BodyA.InvMass; m_invMassB = BodyB.InvMass; m_invIA = BodyA.InvI; m_invIB = BodyB.InvI; FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Rot qA = new Rot(aA), qB = new Rot(aB); // Compute the effective masses. FVector2 rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); FVector2 rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); FVector2 d = (cB - cA) + rB - rA; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Compute motor Jacobian and effective mass. { m_axis = MathUtils.Mul(qA, LocalXAxisA); m_a1 = MathUtils.Cross(d + rA, m_axis); m_a2 = MathUtils.Cross(rB, m_axis); m_motorMass = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } } // Prismatic constraint. { m_perp = MathUtils.Mul(qA, _localYAxisA); m_s1 = MathUtils.Cross(d + rA, m_perp); m_s2 = MathUtils.Cross(rB, m_perp); float k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2; float k12 = iA * m_s1 + iB * m_s2; float k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2; float k22 = iA + iB; if (k22 == 0.0f) { // For bodies with fixed rotation. k22 = 1.0f; } float k23 = iA * m_a1 + iB * m_a2; float k33 = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; m_K.ex = new FVector3(k11, k12, k13); m_K.ey = new FVector3(k12, k22, k23); m_K.ez = new FVector3(k13, k23, k33); } // Compute motor and limit terms. if (_enableLimit) { float jointTranslation = FVector2.Dot(m_axis, d); if (Math.Abs(_upperTranslation - _lowerTranslation) < 2.0f * Settings.LinearSlop) { _limitState = LimitState.Equal; } else if (jointTranslation <= _lowerTranslation) { if (_limitState != LimitState.AtLower) { _limitState = LimitState.AtLower; _impulse.Z = 0.0f; } } else if (jointTranslation >= _upperTranslation) { if (_limitState != LimitState.AtUpper) { _limitState = LimitState.AtUpper; _impulse.Z = 0.0f; } } else { _limitState = LimitState.Inactive; _impulse.Z = 0.0f; } } else { _limitState = LimitState.Inactive; } if (_enableMotor == false) { _motorImpulse = 0.0f; } if (Settings.EnableWarmstarting) { // Account for variable time step. _impulse *= data.step.dtRatio; _motorImpulse *= data.step.dtRatio; FVector2 P = _impulse.X * m_perp + (_motorImpulse + _impulse.Z) * m_axis; float LA = _impulse.X * m_s1 + _impulse.Y + (_motorImpulse + _impulse.Z) * m_a1; float LB = _impulse.X * m_s2 + _impulse.Y + (_motorImpulse + _impulse.Z) * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } else { _impulse = FVector3.Zero; _motorImpulse = 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; }
internal override bool SolvePositionConstraints(ref SolverData data) { FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA), qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Compute fresh Jacobians FVector2 rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); FVector2 rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); FVector2 d = cB + rB - cA - rA; FVector2 axis = MathUtils.Mul(qA, LocalXAxisA); float a1 = MathUtils.Cross(d + rA, axis); float a2 = MathUtils.Cross(rB, axis); FVector2 perp = MathUtils.Mul(qA, _localYAxisA); float s1 = MathUtils.Cross(d + rA, perp); float s2 = MathUtils.Cross(rB, perp); FVector3 impulse; FVector2 C1 = new FVector2(); C1.X = FVector2.Dot(perp, d); C1.Y = aB - aA - m_referenceAngle; float linearError = Math.Abs(C1.X); float angularError = Math.Abs(C1.Y); bool active = false; float C2 = 0.0f; if (_enableLimit) { float translation = FVector2.Dot(axis, d); if (Math.Abs(_upperTranslation - _lowerTranslation) < 2.0f * FSSettings.LinearSlop) { // Prevent large angular corrections C2 = MathUtils.Clamp(translation, -FSSettings.MaxLinearCorrection, FSSettings.MaxLinearCorrection); linearError = Math.Max(linearError, Math.Abs(translation)); active = true; } else if (translation <= _lowerTranslation) { // Prevent large linear corrections and allow some slop. C2 = MathUtils.Clamp(translation - _lowerTranslation + FSSettings.LinearSlop, -FSSettings.MaxLinearCorrection, 0.0f); linearError = Math.Max(linearError, _lowerTranslation - translation); active = true; } else if (translation >= _upperTranslation) { // Prevent large linear corrections and allow some slop. C2 = MathUtils.Clamp(translation - _upperTranslation - FSSettings.LinearSlop, 0.0f, FSSettings.MaxLinearCorrection); linearError = Math.Max(linearError, translation - _upperTranslation); active = true; } } if (active) { float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; float k12 = iA * s1 + iB * s2; float k13 = iA * s1 * a1 + iB * s2 * a2; float k22 = iA + iB; if (k22 == 0.0f) { // For fixed rotation k22 = 1.0f; } float k23 = iA * a1 + iB * a2; float k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2; Mat33 K = new Mat33(); K.ex = new FVector3(k11, k12, k13); K.ey = new FVector3(k12, k22, k23); K.ez = new FVector3(k13, k23, k33); FVector3 C = new FVector3(); C.X = C1.X; C.Y = C1.Y; C.Z = C2; impulse = K.Solve33(-C); } else { float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; float k12 = iA * s1 + iB * s2; float k22 = iA + iB; if (k22 == 0.0f) { k22 = 1.0f; } Mat22 K = new Mat22(); K.ex = new FVector2(k11, k12); K.ey = new FVector2(k12, k22); FVector2 impulse1 = K.Solve(-C1); impulse = new FVector3(); impulse.X = impulse1.X; impulse.Y = impulse1.Y; impulse.Z = 0.0f; } FVector2 P = impulse.X * perp + impulse.Z * axis; float LA = impulse.X * s1 + impulse.Y + impulse.Z * a1; float LB = impulse.X * s2 + impulse.Y + impulse.Z * a2; cA -= mA * P; aA -= iA * LA; cB += mB * P; aB += iB * LB; 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 <= FSSettings.LinearSlop && angularError <= FSSettings.AngularSlop); }
internal override void SolveVelocityConstraints(ref SolverData data) { //GABS: NOT A BOTTLENECK FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; bool fixedRotation = (iA + iB == 0.0f); // Solve motor constraint. if (_enableMotor && _limitState != LimitState.Equal && fixedRotation == false) { float Cdot = wB - wA - _motorSpeed; float impulse = m_motorMass * (-Cdot); float oldImpulse = _motorImpulse; float maxImpulse = data.step.dt * _maxMotorTorque; _motorImpulse = MathUtils.Clamp(_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = _motorImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve limit constraint. if (_enableLimit && _limitState != LimitState.Inactive && fixedRotation == false) { FVector2 Cdot1 = vB + MathUtils.Cross(wB, m_rB) - vA - MathUtils.Cross(wA, m_rA); float Cdot2 = wB - wA; FVector3 Cdot = new FVector3(Cdot1.X, Cdot1.Y, Cdot2); FVector3 impulse = -m_mass.Solve33(Cdot); if (_limitState == LimitState.Equal) { _impulse += impulse; } else if (_limitState == LimitState.AtLower) { float newImpulse = _impulse.Z + impulse.Z; if (newImpulse < 0.0f) { FVector2 rhs = -Cdot1 + _impulse.Z * new FVector2(m_mass.ez.X, m_mass.ez.Y); FVector2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -_impulse.Z; _impulse.X += reduced.X; _impulse.Y += reduced.Y; _impulse.Z = 0.0f; } else { _impulse += impulse; } } else if (_limitState == LimitState.AtUpper) { float newImpulse = _impulse.Z + impulse.Z; if (newImpulse > 0.0f) { FVector2 rhs = -Cdot1 + _impulse.Z * new FVector2(m_mass.ez.X, m_mass.ez.Y); FVector2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -_impulse.Z; _impulse.X += reduced.X; _impulse.Y += reduced.Y; _impulse.Z = 0.0f; } else { _impulse += impulse; } } FVector2 P = new FVector2(impulse.X, impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + impulse.Z); } else { // Solve point-to-point constraint FVector2 Cdot = vB + MathUtils.Cross(wB, m_rB) - vA - MathUtils.Cross(wA, m_rA); FVector2 impulse = m_mass.Solve22(-Cdot); _impulse.X += impulse.X; _impulse.Y += impulse.Y; vA -= mA * impulse; wA -= iA * MathUtils.Cross(m_rA, impulse); vB += mB * impulse; wB += iB * MathUtils.Cross(m_rB, impulse); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
/// Perform the cross product on two vectors. public static FVector3 Cross(FVector3 a, FVector3 b) { return(new FVector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X)); }
/// <summary> /// Initialize the bodies and local anchor. /// This requires defining an /// anchor point where the bodies are joined. The definition /// uses local anchor points so that the initial configuration /// can violate the constraint slightly. You also need to /// specify the initial relative angle for joint limits. This /// helps when saving and loading a game. /// The local anchor points are measured from the body's origin /// rather than the center of mass because: /// 1. you might not know where the center of mass will be. /// 2. if you add/remove shapes from a body and recompute the mass, /// the joints will be broken. /// </summary> /// <param name="bodyA">The first body.</param> /// <param name="bodyB">The second body.</param> /// <param name="localAnchorA">The first body anchor.</param> /// <param name="localAnchorB">The second anchor.</param> public RevoluteJoint(Body bodyA, Body bodyB, FVector2 localAnchorA, FVector2 localAnchorB) : base(bodyA, bodyB) { JointType = JointType.Revolute; // Changed to local coordinates. LocalAnchorA = localAnchorA; LocalAnchorB = localAnchorB; ReferenceAngle = BodyB.Rotation - BodyA.Rotation; _impulse = FVector3.Zero; _limitState = LimitState.Inactive; }
internal override void SolveVelocityConstraints(ref SolverData data) { FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; if (m_frequencyHz > 0.0f) { float Cdot2 = wB - wA; float impulse2 = -_mass.ez.Z * (Cdot2 + m_bias + m_gamma * _impulse.Z); _impulse.Z += impulse2; wA -= iA * impulse2; wB += iB * impulse2; FVector2 Cdot1 = vB + MathUtils.Cross(wB, m_rB) - vA - MathUtils.Cross(wA, m_rA); FVector2 impulse1 = -MathUtils.Mul22(_mass, Cdot1); _impulse.X += impulse1.X; _impulse.Y += impulse1.Y; FVector2 P = impulse1; vA -= mA * P; wA -= iA * MathUtils.Cross(m_rA, P); vB += mB * P; wB += iB * MathUtils.Cross(m_rB, P); } else { FVector2 Cdot1 = vB + MathUtils.Cross(wB, m_rB) - vA - MathUtils.Cross(wA, m_rA); float Cdot2 = wB - wA; FVector3 Cdot = new FVector3(Cdot1.X, Cdot1.Y, Cdot2); FVector3 impulse = -MathUtils.Mul(_mass, Cdot); _impulse += impulse; FVector2 P = new FVector2(impulse.X, impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + impulse.Z); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
/// Perform the dot product on two vectors. public static float Dot(FVector3 a, FVector3 b) { return(a.X * b.X + a.Y * b.Y + a.Z * b.Z); }
/// <summary> /// Initialize the bodies and world anchor. /// </summary> /// <param name="bodyA">The first body.</param> /// <param name="bodyB">The second body.</param> /// <param name="worldAnchor">The world coordinate anchor.</param> public FSRevoluteJoint(FSBody bodyA, FSBody bodyB, FVector2 worldAnchor) : base(bodyA, bodyB) { JointType = JointType.Revolute; // Changed to local coordinates. LocalAnchorA = bodyA.GetLocalPoint(worldAnchor); LocalAnchorB = bodyB.GetLocalPoint(worldAnchor); ReferenceAngle = BodyB.Rotation - BodyA.Rotation; _impulse = FVector3.Zero; _limitState = LimitState.Inactive; }
/// <summary> /// Construct this matrix using columns. /// </summary> /// <param name="c1">The c1.</param> /// <param name="c2">The c2.</param> /// <param name="c3">The c3.</param> public Mat33(FVector3 c1, FVector3 c2, FVector3 c3) { ex = c1; ey = c2; ez = c3; }
internal override void InitVelocityConstraints(ref SolverData data) { m_indexA = BodyA.IslandIndex; m_indexB = BodyB.IslandIndex; m_localCenterA = BodyA.Sweep.LocalCenter; m_localCenterB = BodyB.Sweep.LocalCenter; m_invMassA = BodyA.InvMass; m_invMassB = BodyB.InvMass; m_invIA = BodyA.InvI; m_invIB = BodyB.InvI; //FVector2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; //FVector2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Rot qA = new Rot(aA), qB = new Rot(aB); m_rA = MathUtils.Mul(qA, LocalAnchorA - m_localCenterA); m_rB = MathUtils.Mul(qB, LocalAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; bool fixedRotation = (iA + iB == 0.0f); m_mass.ex.X = mA + mB + m_rA.Y * m_rA.Y * iA + m_rB.Y * m_rB.Y * iB; m_mass.ey.X = -m_rA.Y * m_rA.X * iA - m_rB.Y * m_rB.X * iB; m_mass.ez.X = -m_rA.Y * iA - m_rB.Y * iB; m_mass.ex.Y = m_mass.ey.X; m_mass.ey.Y = mA + mB + m_rA.X * m_rA.X * iA + m_rB.X * m_rB.X * iB; m_mass.ez.Y = m_rA.X * iA + m_rB.X * iB; m_mass.ex.Z = m_mass.ez.X; m_mass.ey.Z = m_mass.ez.Y; m_mass.ez.Z = iA + iB; m_motorMass = iA + iB; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } if (_enableMotor == false || fixedRotation) { _motorImpulse = 0.0f; } if (_enableLimit && fixedRotation == false) { float jointAngle = aB - aA - ReferenceAngle; if (Math.Abs(_upperAngle - _lowerAngle) < 2.0f * FSSettings.AngularSlop) { _limitState = LimitState.Equal; } else if (jointAngle <= _lowerAngle) { if (_limitState != LimitState.AtLower) { _impulse.Z = 0.0f; } _limitState = LimitState.AtLower; } else if (jointAngle >= _upperAngle) { if (_limitState != LimitState.AtUpper) { _impulse.Z = 0.0f; } _limitState = LimitState.AtUpper; } else { _limitState = LimitState.Inactive; _impulse.Z = 0.0f; } } else { _limitState = LimitState.Inactive; } if (FSSettings.EnableWarmstarting) { // Scale impulses to support a variable time step. _impulse *= data.step.dtRatio; _motorImpulse *= data.step.dtRatio; FVector2 P = new FVector2(_impulse.X, _impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + MotorImpulse + _impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + MotorImpulse + _impulse.Z); } else { _impulse = FVector3.Zero; _motorImpulse = 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; }
/// <summary> /// Plot a line start/end point /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="z"></param> /// <param name="color"></param> protected void Plot(float x, float y, float z, FVector3 color) { lines.Add(new FVector3(x, y, z)); lines.Add(color); }
internal override void SolveVelocityConstraints(ref SolverData data) { FVector2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; FVector2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; bool fixedRotation = (iA + iB == 0.0f); // Solve motor constraint. if (_enableMotor && _limitState != LimitState.Equal && fixedRotation == false) { float Cdot = wB - wA - _motorSpeed; float impulse = m_motorMass * (-Cdot); float oldImpulse = _motorImpulse; float maxImpulse = data.step.dt * _maxMotorTorque; _motorImpulse = MathUtils.Clamp(_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = _motorImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve limit constraint. if (_enableLimit && _limitState != LimitState.Inactive && fixedRotation == false) { FVector2 Cdot1 = vB + MathUtils.Cross(wB, m_rB) - vA - MathUtils.Cross(wA, m_rA); float Cdot2 = wB - wA; FVector3 Cdot = new FVector3(Cdot1.X, Cdot1.Y, Cdot2); FVector3 impulse = -m_mass.Solve33(Cdot); if (_limitState == LimitState.Equal) { _impulse += impulse; } else if (_limitState == LimitState.AtLower) { float newImpulse = _impulse.Z + impulse.Z; if (newImpulse < 0.0f) { FVector2 rhs = -Cdot1 + _impulse.Z * new FVector2(m_mass.ez.X, m_mass.ez.Y); FVector2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -_impulse.Z; _impulse.X += reduced.X; _impulse.Y += reduced.Y; _impulse.Z = 0.0f; } else { _impulse += impulse; } } else if (_limitState == LimitState.AtUpper) { float newImpulse = _impulse.Z + impulse.Z; if (newImpulse > 0.0f) { FVector2 rhs = -Cdot1 + _impulse.Z * new FVector2(m_mass.ez.X, m_mass.ez.Y); FVector2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -_impulse.Z; _impulse.X += reduced.X; _impulse.Y += reduced.Y; _impulse.Z = 0.0f; } else { _impulse += impulse; } } FVector2 P = new FVector2(impulse.X, impulse.Y); vA -= mA * P; wA -= iA * (MathUtils.Cross(m_rA, P) + impulse.Z); vB += mB * P; wB += iB * (MathUtils.Cross(m_rB, P) + impulse.Z); } else { // Solve point-to-point constraint FVector2 Cdot = vB + MathUtils.Cross(wB, m_rB) - vA - MathUtils.Cross(wA, m_rA); FVector2 impulse = m_mass.Solve22(-Cdot); _impulse.X += impulse.X; _impulse.Y += impulse.Y; vA -= mA * impulse; wA -= iA * MathUtils.Cross(m_rA, impulse); vB += mB * impulse; wB += iB * MathUtils.Cross(m_rB, impulse); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }