SliderConstraint, a TypedConstraint implementing a Bullet physics slider joint. It computes transforms, tests and encodes linear and angular limits, prepares solver constraint rows, and exposes configuration and motor/limit parameters.
/* * C# / XNA port of Bullet (c) 2011 Mark Neale <[email protected]> * * Bullet Continuous Collision Detection and Physics Library * Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.com/ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ // Adapted from BulletXNA 73b2e1b9: namespace isolation, no XNA rendering, one type per file. #nullable disable #pragma warning disable CS1591, CS0168, CS0219, CS0414, CS0660, CS0661, CS0659, CS0693, CS8981, CS0108, CS0114 using System; using System.Diagnostics; using HumanoidRetargeter.Core.Formats.Mmd.Bullet.LinearMath; namespace HumanoidRetargeter.Core.Formats.Mmd.Bullet.BulletDynamics; internal class SliderConstraint : TypedConstraint { public const float SLIDER_CONSTRAINT_DEF_SOFTNESS = 1.0f; public const float SLIDER_CONSTRAINT_DEF_DAMPING = 1.0f; public const float SLIDER_CONSTRAINT_DEF_RESTITUTION = 0.7f; public const float SLIDER_CONSTRAINT_DEF_CFM = 0.0f; private const bool m_useSolveConstraintObsolete = false; private static bool USE_OFFSET_FOR_CONSTANT_FRAME = true; protected bool m_useOffsetForConstraintFrame; protected IndexedMatrix m_frameInA = IndexedMatrix.Identity; protected IndexedMatrix m_frameInB = IndexedMatrix.Identity; // use frameA fo define limits, if true protected bool m_useLinearReferenceFrameA; // linear limits protected float m_lowerLinLimit; protected float m_upperLinLimit; // angular limits protected float m_lowerAngLimit; protected float m_upperAngLimit; // softness, restitution and damping for different cases // DirLin - moving inside linear limits // LimLin - hitting linear limit // DirAng - moving inside angular limits // LimAng - hitting angular limit // OrthoLin, OrthoAng - against raint axis protected float m_softnessDirLin; protected float m_restitutionDirLin; protected float m_dampingDirLin; protected float m_cfmDirLin; protected float m_softnessDirAng; protected float m_restitutionDirAng; protected float m_dampingDirAng; protected float m_cfmDirAng; protected float m_softnessLimLin; protected float m_restitutionLimLin; protected float m_dampingLimLin; protected float m_cfmLimLin; protected float m_softnessLimAng; protected float m_restitutionLimAng; protected float m_dampingLimAng; protected float m_cfmLimAng; protected float m_softnessOrthoLin; protected float m_restitutionOrthoLin; protected float m_dampingOrthoLin; protected float m_cfmOrthoLin; protected float m_softnessOrthoAng; protected float m_restitutionOrthoAng; protected float m_dampingOrthoAng; protected float m_cfmOrthoAng; // for interlal use protected bool m_solveLinLim; protected bool m_solveAngLim; protected int m_flags; protected JacobianEntry[] m_jacLin = new JacobianEntry[3]; protected float[] m_jacLinDiagABInv = new float[3]; protected JacobianEntry[] m_jacAng = new JacobianEntry[3]; protected float m_timeStep; protected IndexedMatrix m_calculatedTransformA = IndexedMatrix.Identity; protected IndexedMatrix m_calculatedTransformB = IndexedMatrix.Identity; protected IndexedVector3 m_sliderAxis; protected IndexedVector3 m_realPivotAInW; protected IndexedVector3 m_realPivotBInW; protected IndexedVector3 m_projPivotInW; protected IndexedVector3 m_delta; protected IndexedVector3 m_depth; protected IndexedVector3 m_relPosA; protected IndexedVector3 m_relPosB; protected float m_linPos; protected float m_angPos; protected float m_angDepth; protected float m_kAngle; protected bool m_poweredLinMotor; protected float m_targetLinMotorVelocity; protected float m_maxLinMotorForce; protected float m_accumulatedLinMotorImpulse; protected bool m_poweredAngMotor; protected float m_targetAngMotorVelocity; protected float m_maxAngMotorForce; protected float m_accumulatedAngMotorImpulse; //------------------------ // constructors public SliderConstraint(RigidBody rbA, RigidBody rbB, ref IndexedMatrix frameInA, ref IndexedMatrix frameInB, bool useLinearReferenceFrameA) : base(TypedConstraintType.SLIDER_CONSTRAINT_TYPE, rbA, rbB) { m_frameInA = frameInA; m_frameInB = frameInB; m_useLinearReferenceFrameA = useLinearReferenceFrameA; InitParams(); } public SliderConstraint(RigidBody rbB, ref IndexedMatrix frameInB, bool useLinearReferenceFrameA) : base(TypedConstraintType.SLIDER_CONSTRAINT_TYPE, GetFixedBody(), rbB) { m_frameInB = frameInB; m_frameInA = rbB.GetCenterOfMassTransform() * m_frameInB; InitParams(); } protected void InitParams() { m_lowerLinLimit = 1.0f; m_upperLinLimit = -1.0f; m_lowerAngLimit = 0f; m_upperAngLimit = 0f; m_softnessDirLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; m_restitutionDirLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; m_dampingDirLin = 0f; m_cfmDirLin = SLIDER_CONSTRAINT_DEF_CFM; m_softnessDirAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; m_restitutionDirAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; m_dampingDirAng = 0f; m_cfmDirAng = SLIDER_CONSTRAINT_DEF_CFM; m_softnessOrthoLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; m_restitutionOrthoLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; m_dampingOrthoLin = SLIDER_CONSTRAINT_DEF_DAMPING; m_cfmOrthoLin = SLIDER_CONSTRAINT_DEF_CFM; m_softnessOrthoAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; m_restitutionOrthoAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; m_dampingOrthoAng = SLIDER_CONSTRAINT_DEF_DAMPING; m_cfmOrthoAng = SLIDER_CONSTRAINT_DEF_CFM; m_softnessLimLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; m_restitutionLimLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; m_dampingLimLin = SLIDER_CONSTRAINT_DEF_DAMPING; m_cfmLimLin = SLIDER_CONSTRAINT_DEF_CFM; m_softnessLimAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; m_restitutionLimAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; m_dampingLimAng = SLIDER_CONSTRAINT_DEF_DAMPING; m_cfmLimAng = SLIDER_CONSTRAINT_DEF_CFM; m_poweredLinMotor = false; m_targetLinMotorVelocity = 0f; m_maxLinMotorForce = 0f; m_accumulatedLinMotorImpulse = 0f; m_poweredAngMotor = false; m_targetAngMotorVelocity = 0f; m_maxAngMotorForce = 0f; m_accumulatedAngMotorImpulse = 0f; m_useOffsetForConstraintFrame = USE_OFFSET_FOR_CONSTANT_FRAME; m_flags = 0; CalculateTransforms(m_rbA.GetCenterOfMassTransform(), m_rbB.GetCenterOfMassTransform()); } public override void GetInfo1(ConstraintInfo1 info) { info.m_numConstraintRows = 4; // Fixed 2 linear + 2 angular info.nub = 2; //prepare constraint CalculateTransforms(m_rbA.GetCenterOfMassTransform(), m_rbB.GetCenterOfMassTransform()); TestAngLimits(); TestLinLimits(); if (GetSolveLinLimit() || GetPoweredLinMotor()) { info.m_numConstraintRows++; // limit 3rd linear as well info.nub--; } //testAngLimits(); if (GetSolveAngLimit() || GetPoweredAngMotor()) { info.m_numConstraintRows++; // limit 3rd angular as well info.nub--; } } public void GetInfo1NonVirtual(ConstraintInfo1 info) { info.m_numConstraintRows = 6; // Fixed 2 linear + 2 angular + 1 limit (even if not used) info.nub = 0; } public override void GetInfo2(ConstraintInfo2 info) { GetInfo2NonVirtual(info, m_rbA.GetCenterOfMassTransform(), m_rbB.GetCenterOfMassTransform(), m_rbA.GetLinearVelocity(), m_rbB.GetLinearVelocity(), m_rbA.GetInvMass(), m_rbB.GetInvMass()); } public void GetInfo2NonVirtual(ConstraintInfo2 info, IndexedMatrix transA, IndexedMatrix transB, IndexedVector3 linVelA, IndexedVector3 linVelB, float rbAinvMass, float rbBinvMass) { IndexedMatrix trA = GetCalculatedTransformA(); IndexedMatrix trB = GetCalculatedTransformB(); { } int i, s = 1; float signFact = m_useLinearReferenceFrameA ? 1.0f : -1.0f; // difference between frames in WCS IndexedVector3 ofs = trB._origin - trA._origin; // now get weight factors depending on masses float miA = rbAinvMass; float miB = rbBinvMass; bool hasStaticBody = (miA < MathUtil.SIMD_EPSILON) || (miB < MathUtil.SIMD_EPSILON); float miS = miA + miB; float factA, factB; if (miS > 0.0f) { factA = miB / miS; } else { factA = 0.5f; } factB = 1.0f - factA; IndexedVector3 ax1 = IndexedVector3.Zero, p, q; IndexedVector3 ax1A = trA._basis.GetColumn(0); IndexedVector3 ax1B = trB._basis.GetColumn(0); if (m_useOffsetForConstraintFrame) { // get the desired direction of slider axis // as weighted sum of X-orthos of frameA and frameB in WCS ax1 = ax1A * factA + ax1B * factB; ax1.Normalize(); // construct two orthos to slider axis TransformUtil.PlaneSpace1(ref ax1, out p, out q); } else { // old way - use frameA ax1 = trA._basis.GetColumn(0); // get 2 orthos to slider axis (Y, Z) p = trA._basis.GetColumn(1); q = trA._basis.GetColumn(2); } // make rotations around these orthos equal // the slider axis should be the only unconstrained // rotational axis, the angular velocity of the two bodies perpendicular to // the slider axis should be equal. thus the constraint equations are // p*w1 - p*w2 = 0 // q*w1 - q*w2 = 0 // where p and q are unit vectors normal to the slider axis, and w1 and w2 // are the angular velocity vectors of the two bodies. info.m_solverConstraints[0].m_relpos1CrossNormal = p; info.m_solverConstraints[s].m_relpos1CrossNormal = q; info.m_solverConstraints[0].m_relpos2CrossNormal = -p; info.m_solverConstraints[s].m_relpos2CrossNormal = -q; // compute the right hand side of the constraint equation. set relative // body velocities along p and q to bring the slider back into alignment. // if ax1A,ax1B are the unit length slider axes as computed from bodyA and // bodyB, we need to rotate both bodies along the axis u = (ax1 x ax2). // if "theta" is the angle between ax1 and ax2, we need an angular velocity // along u to cover angle erp*theta in one step : // |angular_velocity| = angle/time = erp*theta / stepsize // = (erp*fps) * theta // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2| // = (erp*fps) * theta * (ax1 x ax2) / sin(theta) // ...as ax1 and ax2 are unit length. if theta is smallish, // theta ~= sin(theta), so // angular_velocity = (erp*fps) * (ax1 x ax2) // ax1 x ax2 is in the plane space of ax1, so we project the angular // velocity to p and q to find the right hand side. // float k = info.fps * info.erp * getSoftnessOrthoAng(); float currERP = ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_ERP_ORTANG) != 0) ? m_softnessOrthoAng : m_softnessOrthoAng * info.erp; float k = info.fps * currERP; IndexedVector3 u = IndexedVector3.Cross(ax1A, ax1B); info.m_solverConstraints[0].m_rhs = k * IndexedVector3.Dot(u, p); info.m_solverConstraints[s].m_rhs = k * IndexedVector3.Dot(u, q); if ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_CFM_ORTANG) != 0) { info.m_solverConstraints[0].m_cfm = m_cfmOrthoAng; info.m_solverConstraints[s].m_cfm = m_cfmOrthoAng; } int nrow = 1; // last filled row int srow = nrow; float limit_err; int limit; bool powered; // next two rows. // we want: velA + wA x relA == velB + wB x relB ... but this would // result in three equations, so we project along two orthos to the slider axis IndexedMatrix bodyA_trans = transA; IndexedMatrix bodyB_trans = transB; nrow++; int s2 = nrow * s; nrow++; int s3 = nrow * s; IndexedVector3 tmpA = IndexedVector3.Zero, tmpB = IndexedVector3.Zero, relA = IndexedVector3.Zero, relB = IndexedVector3.Zero, c = IndexedVector3.Zero; if (m_useOffsetForConstraintFrame) { // get vector from bodyB to frameB in WCS relB = trB._origin - bodyB_trans._origin; // get its projection to slider axis IndexedVector3 projB = ax1 * IndexedVector3.Dot(relB, ax1); // get vector directed from bodyB to slider axis (and orthogonal to it) IndexedVector3 orthoB = relB - projB; // same for bodyA relA = trA._origin - bodyA_trans._origin; IndexedVector3 projA = ax1 * IndexedVector3.Dot(relA, ax1); IndexedVector3 orthoA = relA - projA; // get desired offset between frames A and B along slider axis float sliderOffs = m_linPos - m_depth.X; // desired vector from projection of center of bodyA to projection of center of bodyB to slider axis IndexedVector3 totalDist = projA + ax1 * sliderOffs - projB; // get offset vectors relA and relB relA = orthoA + totalDist * factA; relB = orthoB - totalDist * factB; // now choose average ortho to slider axis p = orthoB * factA + orthoA * factB; float len2 = p.LengthSquared(); if (len2 > MathUtil.SIMD_EPSILON) { p.Normalize(); } else { p = trA._basis.GetColumn(1); } // make one more ortho q = IndexedVector3.Cross(ax1, p); // fill two rows tmpA = IndexedVector3.Cross(relA, p); tmpB = IndexedVector3.Cross(relB, p); info.m_solverConstraints[s2].m_relpos1CrossNormal = tmpA; info.m_solverConstraints[s2].m_relpos2CrossNormal = -tmpB; tmpA = IndexedVector3.Cross(relA, q); tmpB = IndexedVector3.Cross(relB, q); if (hasStaticBody && GetSolveAngLimit()) { // to make constraint between static and dynamic objects more rigid // remove wA (or wB) from equation if angular limit is hit tmpB *= factB; tmpA *= factA; } info.m_solverConstraints[s3].m_relpos1CrossNormal = tmpA; info.m_solverConstraints[s3].m_relpos2CrossNormal = -tmpB; info.m_solverConstraints[s2].m_contactNormal = p; info.m_solverConstraints[s3].m_contactNormal = q; } else { // old way - maybe incorrect if bodies are not on the slider axis // see discussion "Bug in slider constraint" http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=4024&start=0 IndexedVector3 tmp = IndexedVector3.Cross(c, p); info.m_solverConstraints[s2].m_relpos1CrossNormal = factA * tmp; info.m_solverConstraints[s2].m_relpos2CrossNormal = factB * tmp; tmp = IndexedVector3.Cross(c, q); info.m_solverConstraints[s3].m_relpos1CrossNormal = factA * tmp; info.m_solverConstraints[s3].m_relpos2CrossNormal = factB * tmp; info.m_solverConstraints[s2].m_contactNormal = p; info.m_solverConstraints[s3].m_contactNormal = q; } // compute two elements of right hand side // k = info.fps * info.erp * getSoftnessOrthoLin(); currERP = ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_ERP_ORTLIN) != 0) ? m_softnessOrthoLin : m_softnessOrthoLin * info.erp; k = info.fps * currERP; float rhs = k * IndexedVector3.Dot(p, ofs); info.m_solverConstraints[s2].m_rhs = rhs; rhs = k * IndexedVector3.Dot(q, ofs); info.m_solverConstraints[s3].m_rhs = rhs; if ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_CFM_ORTLIN) != 0) { info.m_solverConstraints[s2].m_cfm = m_cfmOrthoLin; info.m_solverConstraints[s3].m_cfm = m_cfmOrthoLin; } // check linear limits limit_err = 0.0f; limit = 0; if (GetSolveLinLimit()) { limit_err = GetLinDepth() * signFact; limit = (limit_err > 0f) ? 2 : 1; } powered = false; if (GetPoweredLinMotor()) { powered = true; } // if the slider has joint limits or motor, add in the extra row if (limit != 0 || powered) { nrow++; srow = nrow; info.m_solverConstraints[srow].m_contactNormal = ax1; // linear torque decoupling step: // // we have to be careful that the linear constraint forces (+/- ax1) applied to the two bodies // do not create a torque couple. in other words, the points that the // constraint force is applied at must lie along the same ax1 axis. // a torque couple will result in limited slider-jointed free // bodies from gaining angular momentum. if (m_useOffsetForConstraintFrame) { // this is needed only when bodyA and bodyB are both dynamic. if (!hasStaticBody) { tmpA = IndexedVector3.Cross(relA, ax1); tmpB = IndexedVector3.Cross(relB, ax1); info.m_solverConstraints[srow].m_relpos1CrossNormal = tmpA; info.m_solverConstraints[srow].m_relpos2CrossNormal = -tmpB; } } else { // The old way. May be incorrect if bodies are not on the slider axis IndexedVector3 ltd = IndexedVector3.Cross(c, ax1); // Linear Torque Decoupling vector (a torque) info.m_solverConstraints[nrow].m_relpos1CrossNormal = factA * ltd; info.m_solverConstraints[nrow].m_relpos2CrossNormal = factB * ltd; } // right-hand part float lostop = GetLowerLinLimit(); float histop = GetUpperLinLimit(); if (limit != 0 && (MathUtil.CompareFloat(lostop, histop))) { // the joint motor is ineffective powered = false; } info.m_solverConstraints[nrow].m_rhs = 0f; info.m_solverConstraints[nrow].m_lowerLimit = 0f; info.m_solverConstraints[nrow].m_upperLimit = 0f; currERP = ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_ERP_LIMLIN) != 0) ? m_softnessLimLin : info.erp; if (powered) { if ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_CFM_DIRLIN) != 0) { info.m_solverConstraints[nrow].m_cfm = m_cfmDirLin; } float tag_vel = GetTargetLinMotorVelocity(); float mot_fact = GetMotorFactor(m_linPos, m_lowerLinLimit, m_upperLinLimit, tag_vel, info.fps * currERP); info.m_solverConstraints[nrow].m_rhs -= signFact * mot_fact * GetTargetLinMotorVelocity(); info.m_solverConstraints[nrow].m_lowerLimit += -GetMaxLinMotorForce() * info.fps; info.m_solverConstraints[nrow].m_upperLimit += GetMaxLinMotorForce() * info.fps; } if (limit != 0) { k = info.fps * currERP; info.m_solverConstraints[nrow].m_rhs += k * limit_err; if ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_CFM_LIMLIN) != 0) { info.m_solverConstraints[nrow].m_cfm = m_cfmLimLin; } if (MathUtil.CompareFloat(lostop, histop)) { // limited low and high simultaneously info.m_solverConstraints[nrow].m_lowerLimit = -MathUtil.SIMD_INFINITY; info.m_solverConstraints[nrow].m_upperLimit = MathUtil.SIMD_INFINITY; } else if (limit == 1) { // low limit info.m_solverConstraints[nrow].m_lowerLimit = -MathUtil.SIMD_INFINITY; info.m_solverConstraints[nrow].m_upperLimit = 0f; } else { // high limit info.m_solverConstraints[nrow].m_lowerLimit = 0f; info.m_solverConstraints[nrow].m_upperLimit = MathUtil.SIMD_INFINITY; } // bounce (we'll use slider parameter abs(1.0 - m_dampingLimLin) for that) float bounce = Math.Abs(1.0f - GetDampingLimLin()); if (bounce > 0.0f) { float vel = IndexedVector3.Dot(linVelA, ax1); vel -= IndexedVector3.Dot(linVelB, ax1); vel *= signFact; // only apply bounce if the velocity is incoming, and if the // resulting c[] exceeds what we already have. if (limit == 1) { // low limit if (vel < 0) { float newc = -bounce * vel; if (newc > info.m_solverConstraints[nrow].m_rhs) { info.m_solverConstraints[nrow].m_rhs = newc; } } } else { // high limit - all those computations are reversed if (vel > 0) { float newc = -bounce * vel; if (newc < info.m_solverConstraints[nrow].m_rhs) { info.m_solverConstraints[nrow].m_rhs = newc; } } } } info.m_solverConstraints[nrow].m_rhs *= GetSoftnessLimLin(); } // if(limit) } // if linear limit // check angular limits limit_err = 0.0f; limit = 0; if (GetSolveAngLimit()) { limit_err = GetAngDepth(); limit = (limit_err > 0.0f) ? 1 : 2; } // if the slider has joint limits, add in the extra row powered = false; if (GetPoweredAngMotor()) { powered = true; } if (limit != 0 || powered) { nrow++; srow = nrow; info.m_solverConstraints[srow].m_relpos1CrossNormal = ax1; info.m_solverConstraints[srow].m_relpos2CrossNormal = -ax1; float lostop = GetLowerAngLimit(); float histop = GetUpperAngLimit(); if (limit != 0 && (MathUtil.CompareFloat(lostop, histop))) { // the joint motor is ineffective powered = false; } currERP = ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_ERP_LIMANG) != 0) ? m_softnessLimAng : info.erp; if (powered) { if ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_CFM_DIRANG) != 0) { info.m_solverConstraints[nrow].m_cfm = m_cfmDirAng; } float mot_fact = GetMotorFactor(m_angPos, m_lowerAngLimit, m_upperAngLimit, GetTargetAngMotorVelocity(), info.fps * currERP); info.m_solverConstraints[nrow].m_rhs = mot_fact * GetTargetAngMotorVelocity(); info.m_solverConstraints[nrow].m_lowerLimit = -GetMaxAngMotorForce() * info.fps; info.m_solverConstraints[nrow].m_upperLimit = GetMaxAngMotorForce() * info.fps; } if (limit != 0) { k = info.fps * currERP; info.m_solverConstraints[nrow].m_rhs += k * limit_err; if ((m_flags & (int)SliderFlags.BT_SLIDER_FLAGS_CFM_LIMANG) != 0) { info.m_solverConstraints[nrow].m_cfm = m_cfmLimAng; } if (MathUtil.CompareFloat(lostop, histop)) { // limited low and high simultaneously info.m_solverConstraints[nrow].m_lowerLimit = -MathUtil.SIMD_INFINITY; info.m_solverConstraints[nrow].m_upperLimit = MathUtil.SIMD_INFINITY; } else if (limit == 1) { // low limit info.m_solverConstraints[nrow].m_lowerLimit = 0; info.m_solverConstraints[nrow].m_upperLimit = MathUtil.SIMD_INFINITY; } else { // high limit info.m_solverConstraints[nrow].m_lowerLimit = -MathUtil.SIMD_INFINITY; info.m_solverConstraints[nrow].m_upperLimit = 0; } // bounce (we'll use slider parameter abs(1.0 - m_dampingLimAng) for that) float bounce = Math.Abs(1.0f - GetDampingLimAng()); if (bounce > 0.0f) { float vel = IndexedVector3.Dot(m_rbA.GetAngularVelocity(), ax1); vel -= IndexedVector3.Dot(m_rbB.GetAngularVelocity(), ax1); // only apply bounce if the velocity is incoming, and if the // resulting c[] exceeds what we already have. if (limit == 1) { // low limit if (vel < 0) { float newc = -bounce * vel; if (newc > info.m_solverConstraints[nrow].m_rhs) { info.m_solverConstraints[nrow].m_rhs = newc; } } } else { // high limit - all those computations are reversed if (vel > 0) { float newc = -bounce * vel; if (newc < info.m_solverConstraints[nrow].m_rhs) { info.m_solverConstraints[nrow].m_rhs = newc; } } } } info.m_solverConstraints[nrow].m_rhs *= GetSoftnessLimAng(); } // if(limit) } // if angular limit or powered } // access public IndexedMatrix GetCalculatedTransformA() { return m_calculatedTransformA; } public IndexedMatrix GetCalculatedTransformB() { return m_calculatedTransformB; } public IndexedMatrix GetFrameOffsetA() { return m_frameInA; } public IndexedMatrix GetFrameOffsetB() { return m_frameInB; } public float GetLowerLinLimit() { return m_lowerLinLimit; } public void SetLowerLinLimit(float lowerLimit) { m_lowerLinLimit = lowerLimit; } public float GetUpperLinLimit() { return m_upperLinLimit; } public void SetUpperLinLimit(float upperLimit) { m_upperLinLimit = upperLimit; } public float GetLowerAngLimit() { return m_lowerAngLimit; } public void SetLowerAngLimit(float lowerLimit) { m_lowerAngLimit = MathUtil.NormalizeAngle(lowerLimit); } public float GetUpperAngLimit() { return m_upperAngLimit; } public void SetUpperAngLimit(float upperLimit) { m_upperAngLimit = MathUtil.NormalizeAngle(upperLimit); } public bool GetUseLinearReferenceFrameA() { return m_useLinearReferenceFrameA; } public float GetSoftnessDirLin() { return m_softnessDirLin; } public float GetRestitutionDirLin() { return m_restitutionDirLin; } public float GetDampingDirLin() { return m_dampingDirLin; } public float GetSoftnessDirAng() { return m_softnessDirAng; } public float GetRestitutionDirAng() { return m_restitutionDirAng; } public float GetDampingDirAng() { return m_dampingDirAng; } public float GetSoftnessLimLin() { return m_softnessLimLin; } public float GetRestitutionLimLin() { return m_restitutionLimLin; } public float GetDampingLimLin() { return m_dampingLimLin; } public float GetSoftnessLimAng() { return m_softnessLimAng; } public float GetRestitutionLimAng() { return m_restitutionLimAng; } public float GetDampingLimAng() { return m_dampingLimAng; } public float GetSoftnessOrthoLin() { return m_softnessOrthoLin; } public float GetRestitutionOrthoLin() { return m_restitutionOrthoLin; } public float GetDampingOrthoLin() { return m_dampingOrthoLin; } public float GetSoftnessOrthoAng() { return m_softnessOrthoAng; } public float GetRestitutionOrthoAng() { return m_restitutionOrthoAng; } public float GetDampingOrthoAng() { return m_dampingOrthoAng; } public void SetSoftnessDirLin(float softnessDirLin) { m_softnessDirLin = softnessDirLin; } public void SetRestitutionDirLin(float restitutionDirLin) { m_restitutionDirLin = restitutionDirLin; } public void SetDampingDirLin(float dampingDirLin) { m_dampingDirLin = dampingDirLin; } public void SetSoftnessDirAng(float softnessDirAng) { m_softnessDirAng = softnessDirAng; } public void SetRestitutionDirAng(float restitutionDirAng) { m_restitutionDirAng = restitutionDirAng; } public void SetDampingDirAng(float dampingDirAng) { m_dampingDirAng = dampingDirAng; } public void SetSoftnessLimLin(float softnessLimLin) { m_softnessLimLin = softnessLimLin; } public void SetRestitutionLimLin(float restitutionLimLin) { m_restitutionLimLin = restitutionLimLin; } public void SetDampingLimLin(float dampingLimLin) { m_dampingLimLin = dampingLimLin; } public void SetSoftnessLimAng(float softnessLimAng) { m_softnessLimAng = softnessLimAng; } public void SetRestitutionLimAng(float restitutionLimAng) { m_restitutionLimAng = restitutionLimAng; } public void SetDampingLimAng(float dampingLimAng) { m_dampingLimAng = dampingLimAng; } public void SetSoftnessOrthoLin(float softnessOrthoLin) { m_softnessOrthoLin = softnessOrthoLin; } public void SetRestitutionOrthoLin(float restitutionOrthoLin) { m_restitutionOrthoLin = restitutionOrthoLin; } public void SetDampingOrthoLin(float dampingOrthoLin) { m_dampingOrthoLin = dampingOrthoLin; } public void SetSoftnessOrthoAng(float softnessOrthoAng) { m_softnessOrthoAng = softnessOrthoAng; } public void SetRestitutionOrthoAng(float restitutionOrthoAng) { m_restitutionOrthoAng = restitutionOrthoAng; } public void SetDampingOrthoAng(float dampingOrthoAng) { m_dampingOrthoAng = dampingOrthoAng; } public void SetPoweredLinMotor(bool onOff) { m_poweredLinMotor = onOff; } public bool GetPoweredLinMotor() { return m_poweredLinMotor; } public void SetTargetLinMotorVelocity(float targetLinMotorVelocity) { m_targetLinMotorVelocity = targetLinMotorVelocity; } public float GetTargetLinMotorVelocity() { return m_targetLinMotorVelocity; } public void SetMaxLinMotorForce(float maxLinMotorForce) { m_maxLinMotorForce = maxLinMotorForce; } public float GetMaxLinMotorForce() { return m_maxLinMotorForce; } public void SetPoweredAngMotor(bool onOff) { m_poweredAngMotor = onOff; } public bool GetPoweredAngMotor() { return m_poweredAngMotor; } public void SetTargetAngMotorVelocity(float targetAngMotorVelocity) { m_targetAngMotorVelocity = targetAngMotorVelocity; } public float GetTargetAngMotorVelocity() { return m_targetAngMotorVelocity; } public void SetMaxAngMotorForce(float maxAngMotorForce) { m_maxAngMotorForce = maxAngMotorForce; } public float GetMaxAngMotorForce() { return m_maxAngMotorForce; } public float GetLinearPos() { return m_linPos; } public float GetAngularPos() { return m_angPos; } // access for ODE solver public bool GetSolveLinLimit() { return m_solveLinLim; } public float GetLinDepth() { return m_depth.X; } public bool GetSolveAngLimit() { return m_solveAngLim; } public float GetAngDepth() { return m_angDepth; } public void SetFrames(ref IndexedMatrix frameA, ref IndexedMatrix frameB) { m_frameInA = frameA; m_frameInB = frameB; CalculateTransforms(m_rbA.GetCenterOfMassTransform(), m_rbB.GetCenterOfMassTransform()); } // shared code used by ODE solver public void CalculateTransforms(IndexedMatrix transA, IndexedMatrix transB) { CalculateTransforms(ref transA, ref transB); } public void CalculateTransforms(ref IndexedMatrix transA, ref IndexedMatrix transB) { if (m_useLinearReferenceFrameA || (!m_useSolveConstraintObsolete)) { m_calculatedTransformA = transA * m_frameInA; m_calculatedTransformB = transB * m_frameInB; } else { m_calculatedTransformA = transB * m_frameInB; m_calculatedTransformB = transA * m_frameInA; } m_realPivotAInW = m_calculatedTransformA._origin; m_realPivotBInW = m_calculatedTransformB._origin; m_sliderAxis = m_calculatedTransformA._basis.GetColumn(0); // along X if (m_useLinearReferenceFrameA || m_useSolveConstraintObsolete) { m_delta = m_realPivotBInW - m_realPivotAInW; } else { m_delta = m_realPivotAInW - m_realPivotBInW; } m_projPivotInW = m_realPivotAInW + IndexedVector3.Dot(m_sliderAxis, m_delta) * m_sliderAxis; IndexedVector3 normalWorld; //linear part for (int i = 0; i < 3; i++) { normalWorld = m_calculatedTransformA._basis.GetColumn(i); m_depth[i] = m_delta.Dot(ref normalWorld); } } public void TestLinLimits() { m_solveLinLim = false; m_linPos = m_depth.X; if (m_lowerLinLimit <= m_upperLinLimit) { if (m_depth.X > m_upperLinLimit) { m_depth.X -= m_upperLinLimit; m_solveLinLim = true; } else if (m_depth.X < m_lowerLinLimit) { m_depth.X -= m_lowerLinLimit; m_solveLinLim = true; } else { m_depth.X = 0f; } } else { m_depth.X = 0f; } } public void TestLinLimits2(ConstraintInfo2 info) { } public void TestAngLimits() { m_angDepth = 0f; m_solveAngLim = false; if (m_lowerAngLimit <= m_upperAngLimit) { IndexedVector3 axisA0 = m_calculatedTransformA._basis.GetColumn(1); IndexedVector3 axisA1 = m_calculatedTransformA._basis.GetColumn(2); IndexedVector3 axisB0 = m_calculatedTransformB._basis.GetColumn(1); float rot = (float)Math.Atan2(IndexedVector3.Dot(axisB0, axisA1), IndexedVector3.Dot(axisB0, axisA0)); rot = AdjustAngleToLimits(rot, m_lowerAngLimit, m_upperAngLimit); m_angPos = rot; if (rot < m_lowerAngLimit) { m_angDepth = rot - m_lowerAngLimit; m_solveAngLim = true; } else if (rot > m_upperAngLimit) { m_angDepth = rot - m_upperAngLimit; m_solveAngLim = true; } } } // access for PE Solver public IndexedVector3 GetAncorInA() { IndexedVector3 ancorInA = m_realPivotAInW + (m_lowerLinLimit + m_upperLinLimit) * 0.5f * m_sliderAxis; ancorInA = m_rbA.GetCenterOfMassTransform().Inverse() * ancorInA; return ancorInA; } public IndexedVector3 GetAncorInB() { return m_frameInB._origin; } // access for UseFrameOffset public bool GetUseFrameOffset() { return m_useOffsetForConstraintFrame; } public void SetUseFrameOffset(bool frameOffsetOnOff) { m_useOffsetForConstraintFrame = frameOffsetOnOff; } }