/// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. public Vec3 Solve33(Vec3 b) { float det = Utilities.Dot(ex, Utilities.Cross(ey, ez)); if (det != 0.0f) { det = 1.0f / det; } Vec3 x; x.X = det * Utilities.Dot(b, Utilities.Cross(ey, ez)); x.Y = det * Utilities.Dot(ex, Utilities.Cross(b, ez)); x.Z = det * Utilities.Dot(ex, Utilities.Cross(ey, b)); return x; }
/// Negate this vector. public static Vec3 operator -(Vec3 other) { Vec3 v = new Vec3(); v.Set(-other.X, -other.Y, -other.Z); return v; }
/// Construct this matrix using columns. public Mat33(Vec3 c1, Vec3 c2, Vec3 c3) { ex = c1; ey = c2; ez = c3; }
internal override void SolveVelocityConstraints(SolverData data) { Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; Vec2 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 (m_enableMotor && m_limitState != LimitState.e_equalLimits && fixedRotation == false) { float Cdot = wB - wA - m_motorSpeed; float impulse = -m_motorMass * Cdot; float oldImpulse = m_motorImpulse; float maxImpulse = data.step.dt * m_maxMotorTorque; m_motorImpulse = Utilities.Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve limit constraint. if (m_enableLimit && m_limitState != LimitState.e_inactiveLimit && fixedRotation == false) { Vec2 Cdot1 = vB + Utilities.Cross(wB, m_rB) - vA - Utilities.Cross(wA, m_rA); float Cdot2 = wB - wA; Vec3 Cdot = new Vec3(Cdot1.X, Cdot1.Y, Cdot2); Vec3 impulse = -m_mass.Solve33(Cdot); if (m_limitState == LimitState.e_equalLimits) { m_impulse += impulse; } else if (m_limitState == LimitState.e_atLowerLimit) { float newImpulse = m_impulse.Z + impulse.Z; if (newImpulse < 0.0f) { Vec2 rhs = -Cdot1 + m_impulse.Z * new Vec2(m_mass.ez.X, m_mass.ez.Y); Vec2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -m_impulse.Z; m_impulse.X += reduced.X; m_impulse.Y += reduced.Y; m_impulse.Z = 0.0f; } else { m_impulse += impulse; } } else if (m_limitState == LimitState.e_atUpperLimit) { float newImpulse = m_impulse.Z + impulse.Z; if (newImpulse > 0.0f) { Vec2 rhs = -Cdot1 + m_impulse.Z * new Vec2(m_mass.ez.X, m_mass.ez.Y); Vec2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -m_impulse.Z; m_impulse.X += reduced.X; m_impulse.Y += reduced.Y; m_impulse.Z = 0.0f; } else { m_impulse += impulse; } } Vec2 P = new Vec2(impulse.X, impulse.Y); vA -= mA * P; wA -= iA * (Utilities.Cross(m_rA, P) + impulse.Z); vB += mB * P; wB += iB * (Utilities.Cross(m_rB, P) + impulse.Z); } else { // Solve point-to-point constraint Vec2 Cdot = vB + Utilities.Cross(wB, m_rB) - vA - Utilities.Cross(wA, m_rA); Vec2 impulse = m_mass.Solve22(-Cdot); m_impulse.X += impulse.X; m_impulse.Y += impulse.Y; vA -= mA * impulse; wA -= iA * Utilities.Cross(m_rA, impulse); vB += mB * impulse; wB += iB * Utilities.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; }
internal override void SolveVelocityConstraints(SolverData data){ Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; Vec2 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 (m_enableMotor && m_limitState !=LimitState.e_equalLimits && fixedRotation == false) { float Cdot = wB - wA - m_motorSpeed; float impulse = -m_motorMass * Cdot; float oldImpulse = m_motorImpulse; float maxImpulse = data.step.dt * m_maxMotorTorque; m_motorImpulse = Utilities.Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve limit constraint. if (m_enableLimit && m_limitState != LimitState.e_inactiveLimit && fixedRotation == false) { Vec2 Cdot1 = vB + Utilities.Cross(wB, m_rB) - vA - Utilities.Cross(wA, m_rA); float Cdot2 = wB - wA; Vec3 Cdot = new Vec3(Cdot1.X, Cdot1.Y, Cdot2); Vec3 impulse = -m_mass.Solve33(Cdot); if (m_limitState ==LimitState.e_equalLimits) { m_impulse += impulse; } else if (m_limitState == LimitState.e_atLowerLimit) { float newImpulse = m_impulse.Z + impulse.Z; if (newImpulse < 0.0f) { Vec2 rhs = -Cdot1 + m_impulse.Z * new Vec2(m_mass.ez.X, m_mass.ez.Y); Vec2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -m_impulse.Z; m_impulse.X += reduced.X; m_impulse.Y += reduced.Y; m_impulse.Z = 0.0f; } else { m_impulse += impulse; } } else if (m_limitState == LimitState.e_atUpperLimit) { float newImpulse = m_impulse.Z + impulse.Z; if (newImpulse > 0.0f) { Vec2 rhs = -Cdot1 + m_impulse.Z * new Vec2(m_mass.ez.X, m_mass.ez.Y); Vec2 reduced = m_mass.Solve22(rhs); impulse.X = reduced.X; impulse.Y = reduced.Y; impulse.Z = -m_impulse.Z; m_impulse.X += reduced.X; m_impulse.Y += reduced.Y; m_impulse.Z = 0.0f; } else { m_impulse += impulse; } } Vec2 P = new Vec2(impulse.X, impulse.Y); vA -= mA * P; wA -= iA * (Utilities.Cross(m_rA, P) + impulse.Z); vB += mB * P; wB += iB * (Utilities.Cross(m_rB, P) + impulse.Z); } else { // Solve point-to-point constraint Vec2 Cdot = vB + Utilities.Cross(wB, m_rB) - vA - Utilities.Cross(wA, m_rA); Vec2 impulse = m_mass.Solve22(-Cdot); m_impulse.X += impulse.X; m_impulse.Y += impulse.Y; vA -= mA * impulse; wA -= iA * Utilities.Cross(m_rA, impulse); vB += mB * impulse; wB += iB * Utilities.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 dot product on two vectors. public static float Dot(Vec3 a, Vec3 b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; }
/// Perform the cross product on two vectors. public static Vec3 Cross(Vec3 a, Vec3 b) { return new Vec3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); }
internal override bool SolvePositionConstraints(SolverData data) { Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA); Rot qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Compute fresh Jacobians Vec2 rA = Utilities.Mul(qA, m_localAnchorA - m_localCenterA); Vec2 rB = Utilities.Mul(qB, m_localAnchorB - m_localCenterB); Vec2 d = cB + rB - cA - rA; Vec2 axis = Utilities.Mul(qA, m_localXAxisA); float a1 = Utilities.Cross(d + rA, axis); float a2 = Utilities.Cross(rB, axis); Vec2 perp = Utilities.Mul(qA, m_localYAxisA); float s1 = Utilities.Cross(d + rA, perp); float s2 = Utilities.Cross(rB, perp); Vec3 impulse; Vec2 C1; C1.X = Utilities.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 (m_enableLimit) { float translation = Utilities.Dot(axis, d); if (Math.Abs(m_upperTranslation - m_lowerTranslation) < 2.0f *Settings._linearSlop) { // Prevent large angular corrections C2 = Utilities.Clamp(translation, -Settings._maxLinearCorrection, Settings._maxLinearCorrection); linearError = Math.Max(linearError, Math.Abs(translation)); active = true; } else if (translation <= m_lowerTranslation) { // Prevent large linear corrections and allow some slop. C2 = Utilities.Clamp(translation - m_lowerTranslation + Settings._linearSlop, -Settings._maxLinearCorrection, 0.0f); linearError = Math.Max(linearError, m_lowerTranslation - translation); active = true; } else if (translation >= m_upperTranslation) { // Prevent large linear corrections and allow some slop. C2 = Utilities.Clamp(translation - m_upperTranslation - Settings._linearSlop, 0.0f, Settings._maxLinearCorrection); linearError = Math.Max(linearError, translation - m_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.Set(k11, k12, k13); K.ey.Set(k12, k22, k23); K.ez.Set(k13, k23, k33); Vec3 C = new Vec3(); 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.Set(k11, k12); K.ey.Set(k12, k22); Vec2 impulse1 = K.Solve(-C1); impulse.X = impulse1.X; impulse.Y = impulse1.Y; impulse.Z = 0.0f; } Vec2 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; }
/// Multiply a matrix times a vector. public static Vec3 Mul(Mat33 A, Vec3 v) { return v.X * A.ex + v.Y * A.ey + v.Z * A.ez; }
internal override void SolveVelocityConstraints(SolverData data) { Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; Vec2 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 (m_enableMotor && m_limitState != LimitState.e_equalLimits) { float Cdot = Utilities.Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; float impulse = m_motorMass * (m_motorSpeed - Cdot); float oldImpulse = m_motorImpulse; float maxImpulse = data.step.dt * m_maxMotorForce; m_motorImpulse = Utilities.Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; Vec2 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; } Vec2 Cdot1; Cdot1.X = Utilities.Dot(m_perp, vB - vA) + m_s2 * wB - m_s1 * wA; Cdot1.Y = wB - wA; if (m_enableLimit && m_limitState != LimitState.e_inactiveLimit) { // Solve prismatic and limit constraint in block form. float Cdot2; Cdot2 = Utilities.Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; Vec3 Cdot = new Vec3(Cdot1.X, Cdot1.Y, Cdot2); Vec3 f1 = m_impulse; Vec3 df = m_K.Solve33(-Cdot); m_impulse += df; if (m_limitState == LimitState.e_atLowerLimit) { m_impulse.Z = Math.Max(m_impulse.Z, 0.0f); } else if (m_limitState == LimitState.e_atUpperLimit) { m_impulse.Z = Math.Min(m_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) Vec2 b = -Cdot1 - (m_impulse.Z - f1.Z) * new Vec2(m_K.ez.X, m_K.ez.Y); Vec2 f2r = m_K.Solve22(b) + new Vec2(f1.X, f1.Y); m_impulse.X = f2r.X; m_impulse.Y = f2r.Y; df = m_impulse - f1; Vec2 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. Vec2 df = m_K.Solve22(-Cdot1); m_impulse.X += df.X; m_impulse.Y += df.Y; Vec2 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; } 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(SolverData data) { Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA); Rot qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; // Compute fresh Jacobians Vec2 rA = Utilities.Mul(qA, m_localAnchorA - m_localCenterA); Vec2 rB = Utilities.Mul(qB, m_localAnchorB - m_localCenterB); Vec2 d = cB + rB - cA - rA; Vec2 axis = Utilities.Mul(qA, m_localXAxisA); float a1 = Utilities.Cross(d + rA, axis); float a2 = Utilities.Cross(rB, axis); Vec2 perp = Utilities.Mul(qA, m_localYAxisA); float s1 = Utilities.Cross(d + rA, perp); float s2 = Utilities.Cross(rB, perp); Vec3 impulse; Vec2 C1; C1.X = Utilities.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 (m_enableLimit) { float translation = Utilities.Dot(axis, d); if (Math.Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * Settings._linearSlop) { // Prevent large angular corrections C2 = Utilities.Clamp(translation, -Settings._maxLinearCorrection, Settings._maxLinearCorrection); linearError = Math.Max(linearError, Math.Abs(translation)); active = true; } else if (translation <= m_lowerTranslation) { // Prevent large linear corrections and allow some slop. C2 = Utilities.Clamp(translation - m_lowerTranslation + Settings._linearSlop, -Settings._maxLinearCorrection, 0.0f); linearError = Math.Max(linearError, m_lowerTranslation - translation); active = true; } else if (translation >= m_upperTranslation) { // Prevent large linear corrections and allow some slop. C2 = Utilities.Clamp(translation - m_upperTranslation - Settings._linearSlop, 0.0f, Settings._maxLinearCorrection); linearError = Math.Max(linearError, translation - m_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.Set(k11, k12, k13); K.ey.Set(k12, k22, k23); K.ez.Set(k13, k23, k33); Vec3 C = new Vec3(); 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.Set(k11, k12); K.ey.Set(k12, k22); Vec2 impulse1 = K.Solve(-C1); impulse.X = impulse1.X; impulse.Y = impulse1.Y; impulse.Z = 0.0f; } Vec2 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 bool SolvePositionConstraints(SolverData data){ Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Rot qA = new Rot(aA); Rot qB = new Rot(aB); float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; Vec2 rA = Utilities.Mul(qA, m_localAnchorA - m_localCenterA); Vec2 rB = Utilities.Mul(qB, m_localAnchorB - m_localCenterB); float positionError, angularError; Mat33 K; 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) { Vec2 C1 = cB + rB - cA - rA; positionError = C1.Length(); angularError = 0.0f; Vec2 P = -K.Solve22(C1); cA -= mA * P; aA -= iA * Utilities.Cross(rA, P); cB += mB * P; aB += iB * Utilities.Cross(rB, P); } else { Vec2 C1 = cB + rB - cA - rA; float C2 = aB - aA - m_referenceAngle; positionError = C1.Length(); angularError = Math.Abs(C2); Vec3 C = new Vec3(C1.X, C1.Y, C2); Vec3 impulse = -K.Solve33(C); Vec2 P = new Vec2(impulse.X, impulse.Y); cA -= mA * P; aA -= iA * (Utilities.Cross(rA, P) + impulse.Z); cB += mB * P; aB += iB * (Utilities.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 SolveVelocityConstraints(SolverData data){ Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; Vec2 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 = -m_mass.ez.Z * (Cdot2 + m_bias + m_gamma * m_impulse.Z); m_impulse.Z += impulse2; wA -= iA * impulse2; wB += iB * impulse2; Vec2 Cdot1 = vB + Utilities.Cross(wB, m_rB) - vA - Utilities.Cross(wA, m_rA); Vec2 impulse1 = -Utilities.Mul22(m_mass, Cdot1); m_impulse.X += impulse1.X; m_impulse.Y += impulse1.Y; Vec2 P = impulse1; vA -= mA * P; wA -= iA * Utilities.Cross(m_rA, P); vB += mB * P; wB += iB * Utilities.Cross(m_rB, P); } else { Vec2 Cdot1 = vB + Utilities.Cross(wB, m_rB) - vA - Utilities.Cross(wA, m_rA); float Cdot2 = wB - wA; Vec3 Cdot = new Vec3(Cdot1.X, Cdot1.Y, Cdot2); Vec3 impulse = -Utilities.Mul(m_mass, Cdot); m_impulse += impulse; Vec2 P = new Vec2(impulse.X, impulse.Y); vA -= mA * P; wA -= iA * (Utilities.Cross(m_rA, P) + impulse.Z); vB += mB * P; wB += iB * (Utilities.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; }