2013-05-08 21:33:04 +00:00
|
|
|
/********************************************************************************
|
2015-02-15 20:56:45 +00:00
|
|
|
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
|
2016-04-11 18:15:20 +00:00
|
|
|
* Copyright (c) 2010-2016 Daniel Chappuis *
|
2013-05-08 21:33:04 +00:00
|
|
|
*********************************************************************************
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
|
|
// Libraries
|
|
|
|
#include "SliderJoint.h"
|
2018-04-20 05:13:39 +00:00
|
|
|
#include "engine/ConstraintSolver.h"
|
2013-05-08 21:33:04 +00:00
|
|
|
|
|
|
|
using namespace reactphysics3d;
|
|
|
|
|
2013-05-29 20:45:02 +00:00
|
|
|
// Static variables definition
|
2016-08-21 18:37:58 +00:00
|
|
|
const decimal SliderJoint::BETA = decimal(0.2);
|
2013-05-29 20:45:02 +00:00
|
|
|
|
2013-05-08 21:33:04 +00:00
|
|
|
// Constructor
|
2018-03-14 06:33:28 +00:00
|
|
|
SliderJoint::SliderJoint(uint id, const SliderJointInfo& jointInfo)
|
|
|
|
: Joint(id, jointInfo), mImpulseTranslation(0, 0), mImpulseRotation(0, 0, 0),
|
2013-05-29 20:45:02 +00:00
|
|
|
mImpulseLowerLimit(0), mImpulseUpperLimit(0), mImpulseMotor(0),
|
|
|
|
mIsLimitEnabled(jointInfo.isLimitEnabled), mIsMotorEnabled(jointInfo.isMotorEnabled),
|
2013-06-26 20:28:31 +00:00
|
|
|
mLowerLimit(jointInfo.minTranslationLimit),
|
|
|
|
mUpperLimit(jointInfo.maxTranslationLimit), mIsLowerLimitViolated(false),
|
|
|
|
mIsUpperLimitViolated(false), mMotorSpeed(jointInfo.motorSpeed),
|
|
|
|
mMaxMotorForce(jointInfo.maxMotorForce){
|
2013-05-22 21:38:30 +00:00
|
|
|
|
2018-03-28 20:55:02 +00:00
|
|
|
assert(mUpperLimit >= decimal(0.0));
|
|
|
|
assert(mLowerLimit <= decimal(0.0));
|
|
|
|
assert(mMaxMotorForce >= decimal(0.0));
|
2013-05-08 21:33:04 +00:00
|
|
|
|
|
|
|
// Compute the local-space anchor point for each body
|
2013-05-22 21:38:30 +00:00
|
|
|
const Transform& transform1 = mBody1->getTransform();
|
|
|
|
const Transform& transform2 = mBody2->getTransform();
|
|
|
|
mLocalAnchorPointBody1 = transform1.getInverse() * jointInfo.anchorPointWorldSpace;
|
|
|
|
mLocalAnchorPointBody2 = transform2.getInverse() * jointInfo.anchorPointWorldSpace;
|
|
|
|
|
2017-06-30 13:46:15 +00:00
|
|
|
// Store inverse of initial rotation from body 1 to body 2 in body 1 space:
|
|
|
|
//
|
|
|
|
// q20 = q10 r0
|
|
|
|
// <=> r0 = q10^-1 q20
|
|
|
|
// <=> r0^-1 = q20^-1 q10
|
|
|
|
//
|
|
|
|
// where:
|
|
|
|
//
|
|
|
|
// q20 = initial orientation of body 2
|
|
|
|
// q10 = initial orientation of body 1
|
|
|
|
// r0 = initial rotation rotation from body 1 to body 2
|
|
|
|
mInitOrientationDifferenceInv = transform2.getOrientation().getInverse() * transform1.getOrientation();
|
2013-05-21 21:03:14 +00:00
|
|
|
|
|
|
|
// Compute the slider axis in local-space of body 1
|
|
|
|
mSliderAxisBody1 = mBody1->getTransform().getOrientation().getInverse() *
|
|
|
|
jointInfo.sliderAxisWorldSpace;
|
|
|
|
mSliderAxisBody1.normalize();
|
2013-05-08 21:33:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize before solving the constraint
|
|
|
|
void SliderJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverData) {
|
|
|
|
|
2013-05-29 20:45:02 +00:00
|
|
|
// Initialize the bodies index in the veloc ity array
|
2017-12-14 14:09:56 +00:00
|
|
|
mIndexBody1 = mBody1->mArrayIndex;
|
|
|
|
mIndexBody2 = mBody2->mArrayIndex;
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-22 21:38:30 +00:00
|
|
|
// Get the bodies positions and orientations
|
2014-05-15 04:39:39 +00:00
|
|
|
const Vector3& x1 = mBody1->mCenterOfMassWorld;
|
2014-04-11 21:50:00 +00:00
|
|
|
const Vector3& x2 = mBody2->mCenterOfMassWorld;
|
2013-05-08 21:33:04 +00:00
|
|
|
const Quaternion& orientationBody1 = mBody1->getTransform().getOrientation();
|
|
|
|
const Quaternion& orientationBody2 = mBody2->getTransform().getOrientation();
|
|
|
|
|
|
|
|
// Get the inertia tensor of bodies
|
2013-06-26 20:28:31 +00:00
|
|
|
mI1 = mBody1->getInertiaTensorInverseWorld();
|
|
|
|
mI2 = mBody2->getInertiaTensorInverseWorld();
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-21 21:03:14 +00:00
|
|
|
// Vector from body center to the anchor point
|
|
|
|
mR1 = orientationBody1 * mLocalAnchorPointBody1;
|
|
|
|
mR2 = orientationBody2 * mLocalAnchorPointBody2;
|
|
|
|
|
2013-06-09 14:31:01 +00:00
|
|
|
// Compute the vector u (difference between anchor points)
|
2013-05-21 21:03:14 +00:00
|
|
|
const Vector3 u = x2 + mR2 - x1 - mR1;
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-23 20:52:08 +00:00
|
|
|
// Compute the two orthogonal vectors to the slider axis in world-space
|
2013-05-22 21:38:30 +00:00
|
|
|
mSliderAxisWorld = orientationBody1 * mSliderAxisBody1;
|
|
|
|
mSliderAxisWorld.normalize();
|
|
|
|
mN1 = mSliderAxisWorld.getOneUnitOrthogonalVector();
|
|
|
|
mN2 = mSliderAxisWorld.cross(mN1);
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-22 21:38:30 +00:00
|
|
|
// Check if the limit constraints are violated or not
|
2013-05-23 20:52:08 +00:00
|
|
|
decimal uDotSliderAxis = u.dot(mSliderAxisWorld);
|
|
|
|
decimal lowerLimitError = uDotSliderAxis - mLowerLimit;
|
|
|
|
decimal upperLimitError = mUpperLimit - uDotSliderAxis;
|
2013-05-29 20:45:02 +00:00
|
|
|
bool oldIsLowerLimitViolated = mIsLowerLimitViolated;
|
|
|
|
mIsLowerLimitViolated = lowerLimitError <= 0;
|
|
|
|
if (mIsLowerLimitViolated != oldIsLowerLimitViolated) {
|
|
|
|
mImpulseLowerLimit = 0.0;
|
|
|
|
}
|
|
|
|
bool oldIsUpperLimitViolated = mIsUpperLimitViolated;
|
|
|
|
mIsUpperLimitViolated = upperLimitError <= 0;
|
|
|
|
if (mIsUpperLimitViolated != oldIsUpperLimitViolated) {
|
|
|
|
mImpulseUpperLimit = 0.0;
|
|
|
|
}
|
2013-05-22 21:38:30 +00:00
|
|
|
|
|
|
|
// Compute the cross products used in the Jacobians
|
2013-05-21 21:03:14 +00:00
|
|
|
mR2CrossN1 = mR2.cross(mN1);
|
|
|
|
mR2CrossN2 = mR2.cross(mN2);
|
2013-05-22 21:38:30 +00:00
|
|
|
mR2CrossSliderAxis = mR2.cross(mSliderAxisWorld);
|
2013-05-21 21:03:14 +00:00
|
|
|
const Vector3 r1PlusU = mR1 + u;
|
|
|
|
mR1PlusUCrossN1 = (r1PlusU).cross(mN1);
|
|
|
|
mR1PlusUCrossN2 = (r1PlusU).cross(mN2);
|
2013-05-22 21:38:30 +00:00
|
|
|
mR1PlusUCrossSliderAxis = (r1PlusU).cross(mSliderAxisWorld);
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-12 10:43:07 +00:00
|
|
|
// Compute the inverse of the mass matrix K=JM^-1J^t for the 2 translation
|
|
|
|
// constraints (2x2 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
decimal sumInverseMass = mBody1->mMassInverse + mBody2->mMassInverse;
|
|
|
|
Vector3 I1R1PlusUCrossN1 = mI1 * mR1PlusUCrossN1;
|
|
|
|
Vector3 I1R1PlusUCrossN2 = mI1 * mR1PlusUCrossN2;
|
|
|
|
Vector3 I2R2CrossN1 = mI2 * mR2CrossN1;
|
|
|
|
Vector3 I2R2CrossN2 = mI2 * mR2CrossN2;
|
2013-05-22 21:38:30 +00:00
|
|
|
const decimal el11 = sumInverseMass + mR1PlusUCrossN1.dot(I1R1PlusUCrossN1) +
|
2013-05-21 21:03:14 +00:00
|
|
|
mR2CrossN1.dot(I2R2CrossN1);
|
2013-05-22 21:38:30 +00:00
|
|
|
const decimal el12 = mR1PlusUCrossN1.dot(I1R1PlusUCrossN2) +
|
2013-05-21 21:03:14 +00:00
|
|
|
mR2CrossN1.dot(I2R2CrossN2);
|
2013-05-22 21:38:30 +00:00
|
|
|
const decimal el21 = mR1PlusUCrossN2.dot(I1R1PlusUCrossN1) +
|
2013-05-21 21:03:14 +00:00
|
|
|
mR2CrossN2.dot(I2R2CrossN1);
|
2013-05-22 21:38:30 +00:00
|
|
|
const decimal el22 = sumInverseMass + mR1PlusUCrossN2.dot(I1R1PlusUCrossN2) +
|
2013-05-21 21:03:14 +00:00
|
|
|
mR2CrossN2.dot(I2R2CrossN2);
|
2013-05-12 10:43:07 +00:00
|
|
|
Matrix2x2 matrixKTranslation(el11, el12, el21, el22);
|
2013-05-21 21:03:14 +00:00
|
|
|
mInverseMassMatrixTranslationConstraint.setToZero();
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mBody1->getType() == BodyType::DYNAMIC || mBody2->getType() == BodyType::DYNAMIC) {
|
2013-05-21 21:03:14 +00:00
|
|
|
mInverseMassMatrixTranslationConstraint = matrixKTranslation.getInverse();
|
|
|
|
}
|
2013-05-12 10:43:07 +00:00
|
|
|
|
2013-05-22 21:38:30 +00:00
|
|
|
// Compute the bias "b" of the translation constraint
|
|
|
|
mBTranslation.setToZero();
|
2013-05-29 20:45:02 +00:00
|
|
|
decimal biasFactor = (BETA / constraintSolverData.timeStep);
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mPositionCorrectionTechnique == JointsPositionCorrectionTechnique::BAUMGARTE_JOINTS) {
|
2013-05-22 21:38:30 +00:00
|
|
|
mBTranslation.x = u.dot(mN1);
|
|
|
|
mBTranslation.y = u.dot(mN2);
|
|
|
|
mBTranslation *= biasFactor;
|
|
|
|
}
|
|
|
|
|
2013-05-12 10:43:07 +00:00
|
|
|
// Compute the inverse of the mass matrix K=JM^-1J^t for the 3 rotation
|
|
|
|
// contraints (3x3 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
mInverseMassMatrixRotationConstraint = mI1 + mI2;
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mBody1->getType() == BodyType::DYNAMIC || mBody2->getType() == BodyType::DYNAMIC) {
|
2013-05-21 21:03:14 +00:00
|
|
|
mInverseMassMatrixRotationConstraint = mInverseMassMatrixRotationConstraint.getInverse();
|
|
|
|
}
|
2013-05-22 21:38:30 +00:00
|
|
|
|
|
|
|
// Compute the bias "b" of the rotation constraint
|
|
|
|
mBRotation.setToZero();
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mPositionCorrectionTechnique == JointsPositionCorrectionTechnique::BAUMGARTE_JOINTS) {
|
2017-06-30 13:46:15 +00:00
|
|
|
const Quaternion qError = orientationBody2 * mInitOrientationDifferenceInv * orientationBody1.getInverse();
|
2013-05-22 21:38:30 +00:00
|
|
|
mBRotation = biasFactor * decimal(2.0) * qError.getVectorV();
|
|
|
|
}
|
|
|
|
|
2013-09-30 21:34:47 +00:00
|
|
|
// If the limits are enabled
|
2013-05-29 20:45:02 +00:00
|
|
|
if (mIsLimitEnabled && (mIsLowerLimitViolated || mIsUpperLimitViolated)) {
|
2013-05-22 21:38:30 +00:00
|
|
|
|
2013-06-09 14:31:01 +00:00
|
|
|
// Compute the inverse of the mass matrix K=JM^-1J^t for the limits (1x1 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
mInverseMassMatrixLimit = mBody1->mMassInverse + mBody2->mMassInverse +
|
|
|
|
mR1PlusUCrossSliderAxis.dot(mI1 * mR1PlusUCrossSliderAxis) +
|
|
|
|
mR2CrossSliderAxis.dot(mI2 * mR2CrossSliderAxis);
|
2013-05-29 20:45:02 +00:00
|
|
|
mInverseMassMatrixLimit = (mInverseMassMatrixLimit > 0.0) ?
|
|
|
|
decimal(1.0) / mInverseMassMatrixLimit : decimal(0.0);
|
|
|
|
|
|
|
|
// Compute the bias "b" of the lower limit constraint
|
|
|
|
mBLowerLimit = 0.0;
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mPositionCorrectionTechnique == JointsPositionCorrectionTechnique::BAUMGARTE_JOINTS) {
|
2013-05-29 20:45:02 +00:00
|
|
|
mBLowerLimit = biasFactor * lowerLimitError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the bias "b" of the upper limit constraint
|
|
|
|
mBUpperLimit = 0.0;
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mPositionCorrectionTechnique == JointsPositionCorrectionTechnique::BAUMGARTE_JOINTS) {
|
2013-05-29 20:45:02 +00:00
|
|
|
mBUpperLimit = biasFactor * upperLimitError;
|
|
|
|
}
|
2013-05-22 21:38:30 +00:00
|
|
|
}
|
2013-05-23 20:52:08 +00:00
|
|
|
|
2013-09-30 21:34:47 +00:00
|
|
|
// If the motor is enabled
|
|
|
|
if (mIsMotorEnabled) {
|
|
|
|
|
|
|
|
// Compute the inverse of mass matrix K=JM^-1J^t for the motor (1x1 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
mInverseMassMatrixMotor = mBody1->mMassInverse + mBody2->mMassInverse;
|
2013-09-30 21:34:47 +00:00
|
|
|
mInverseMassMatrixMotor = (mInverseMassMatrixMotor > 0.0) ?
|
|
|
|
decimal(1.0) / mInverseMassMatrixMotor : decimal(0.0);
|
2013-05-29 20:45:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If warm-starting is not enabled
|
|
|
|
if (!constraintSolverData.isWarmStartingActive) {
|
|
|
|
|
|
|
|
// Reset all the accumulated impulses
|
|
|
|
mImpulseTranslation.setToZero();
|
|
|
|
mImpulseRotation.setToZero();
|
|
|
|
mImpulseLowerLimit = 0.0;
|
|
|
|
mImpulseUpperLimit = 0.0;
|
|
|
|
mImpulseMotor = 0.0;
|
2013-05-23 20:52:08 +00:00
|
|
|
}
|
2013-05-12 10:43:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Warm start the constraint (apply the previous impulse at the beginning of the step)
|
|
|
|
void SliderJoint::warmstart(const ConstraintSolverData& constraintSolverData) {
|
|
|
|
|
|
|
|
// Get the velocities
|
|
|
|
Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1];
|
|
|
|
Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2];
|
|
|
|
Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1];
|
|
|
|
Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2];
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-12 10:43:07 +00:00
|
|
|
// Get the inverse mass and inverse inertia tensors of the bodies
|
2013-11-21 22:24:11 +00:00
|
|
|
const decimal inverseMassBody1 = mBody1->mMassInverse;
|
|
|
|
const decimal inverseMassBody2 = mBody2->mMassInverse;
|
2013-05-12 10:43:07 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower and upper limits constraints of body 1
|
2013-05-29 20:45:02 +00:00
|
|
|
decimal impulseLimits = mImpulseUpperLimit - mImpulseLowerLimit;
|
|
|
|
Vector3 linearImpulseLimits = impulseLimits * mSliderAxisWorld;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the motor constraint of body 1
|
2013-05-29 20:45:02 +00:00
|
|
|
Vector3 impulseMotor = mImpulseMotor * mSliderAxisWorld;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 2 translation constraints of body 1
|
|
|
|
Vector3 linearImpulseBody1 = -mN1 * mImpulseTranslation.x - mN2 * mImpulseTranslation.y;
|
|
|
|
Vector3 angularImpulseBody1 = -mR1PlusUCrossN1 * mImpulseTranslation.x -
|
|
|
|
mR1PlusUCrossN2 * mImpulseTranslation.y;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 3 rotation constraints of body 1
|
|
|
|
angularImpulseBody1 += -mImpulseRotation;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower and upper limits constraints of body 1
|
|
|
|
linearImpulseBody1 += linearImpulseLimits;
|
|
|
|
angularImpulseBody1 += impulseLimits * mR1PlusUCrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the motor constraint of body 1
|
|
|
|
linearImpulseBody1 += impulseMotor;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
v1 += inverseMassBody1 * linearImpulseBody1;
|
|
|
|
w1 += mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 2 translation constraints of body 2
|
|
|
|
Vector3 linearImpulseBody2 = mN1 * mImpulseTranslation.x + mN2 * mImpulseTranslation.y;
|
|
|
|
Vector3 angularImpulseBody2 = mR2CrossN1 * mImpulseTranslation.x +
|
|
|
|
mR2CrossN2 * mImpulseTranslation.y;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 3 rotation constraints of body 2
|
|
|
|
angularImpulseBody2 += mImpulseRotation;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower and upper limits constraints of body 2
|
|
|
|
linearImpulseBody2 += -linearImpulseLimits;
|
|
|
|
angularImpulseBody2 += -impulseLimits * mR2CrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the motor constraint of body 2
|
|
|
|
linearImpulseBody2 += -impulseMotor;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
v2 += inverseMassBody2 * linearImpulseBody2;
|
|
|
|
w2 += mI2 * angularImpulseBody2;
|
2013-05-08 21:33:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Solve the velocity constraint
|
|
|
|
void SliderJoint::solveVelocityConstraint(const ConstraintSolverData& constraintSolverData) {
|
|
|
|
|
|
|
|
// Get the velocities
|
|
|
|
Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1];
|
|
|
|
Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2];
|
|
|
|
Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1];
|
|
|
|
Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2];
|
|
|
|
|
|
|
|
// Get the inverse mass and inverse inertia tensors of the bodies
|
2013-11-21 22:24:11 +00:00
|
|
|
decimal inverseMassBody1 = mBody1->mMassInverse;
|
|
|
|
decimal inverseMassBody2 = mBody2->mMassInverse;
|
2013-05-08 21:33:04 +00:00
|
|
|
|
2013-05-21 21:03:14 +00:00
|
|
|
// --------------- Translation Constraints --------------- //
|
|
|
|
|
2013-05-12 10:43:07 +00:00
|
|
|
// Compute J*v for the 2 translation constraints
|
2013-05-21 21:03:14 +00:00
|
|
|
const decimal el1 = -mN1.dot(v1) - w1.dot(mR1PlusUCrossN1) +
|
|
|
|
mN1.dot(v2) + w2.dot(mR2CrossN1);
|
|
|
|
const decimal el2 = -mN2.dot(v1) - w1.dot(mR1PlusUCrossN2) +
|
|
|
|
mN2.dot(v2) + w2.dot(mR2CrossN2);
|
2013-05-12 10:43:07 +00:00
|
|
|
const Vector2 JvTranslation(el1, el2);
|
|
|
|
|
2013-05-21 21:03:14 +00:00
|
|
|
// Compute the Lagrange multiplier lambda for the 2 translation constraints
|
2013-05-22 21:38:30 +00:00
|
|
|
Vector2 deltaLambda = mInverseMassMatrixTranslationConstraint * (-JvTranslation -mBTranslation);
|
2013-05-21 21:03:14 +00:00
|
|
|
mImpulseTranslation += deltaLambda;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 2 translation constraints of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = -mN1 * deltaLambda.x - mN2 * deltaLambda.y;
|
|
|
|
Vector3 angularImpulseBody1 = -mR1PlusUCrossN1 * deltaLambda.x -
|
|
|
|
mR1PlusUCrossN2 * deltaLambda.y;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
v1 += inverseMassBody1 * linearImpulseBody1;
|
|
|
|
w1 += mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 2 translation constraints of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = mN1 * deltaLambda.x + mN2 * deltaLambda.y;
|
|
|
|
Vector3 angularImpulseBody2 = mR2CrossN1 * deltaLambda.x + mR2CrossN2 * deltaLambda.y;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
v2 += inverseMassBody2 * linearImpulseBody2;
|
|
|
|
w2 += mI2 * angularImpulseBody2;
|
2013-05-21 21:03:14 +00:00
|
|
|
|
|
|
|
// --------------- Rotation Constraints --------------- //
|
|
|
|
|
|
|
|
// Compute J*v for the 3 rotation constraints
|
|
|
|
const Vector3 JvRotation = w2 - w1;
|
|
|
|
|
2013-05-12 10:43:07 +00:00
|
|
|
// Compute the Lagrange multiplier lambda for the 3 rotation constraints
|
2013-05-22 21:38:30 +00:00
|
|
|
Vector3 deltaLambda2 = mInverseMassMatrixRotationConstraint * (-JvRotation - mBRotation);
|
2013-05-12 10:43:07 +00:00
|
|
|
mImpulseRotation += deltaLambda2;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 3 rotation constraints of body 1
|
|
|
|
angularImpulseBody1 = -deltaLambda2;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body to body 1
|
|
|
|
w1 += mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 3 rotation constraints of body 2
|
|
|
|
angularImpulseBody2 = deltaLambda2;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
w2 += mI2 * angularImpulseBody2;
|
2013-05-22 21:38:30 +00:00
|
|
|
|
|
|
|
// --------------- Limits Constraints --------------- //
|
|
|
|
|
2013-05-29 20:45:02 +00:00
|
|
|
if (mIsLimitEnabled) {
|
2013-05-22 21:38:30 +00:00
|
|
|
|
|
|
|
// If the lower limit is violated
|
|
|
|
if (mIsLowerLimitViolated) {
|
|
|
|
|
|
|
|
// Compute J*v for the lower limit constraint
|
|
|
|
const decimal JvLowerLimit = mSliderAxisWorld.dot(v2) + mR2CrossSliderAxis.dot(w2) -
|
|
|
|
mSliderAxisWorld.dot(v1) - mR1PlusUCrossSliderAxis.dot(w1);
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the lower limit constraint
|
2013-05-23 20:52:08 +00:00
|
|
|
decimal deltaLambdaLower = mInverseMassMatrixLimit * (-JvLowerLimit -mBLowerLimit);
|
2013-05-22 21:38:30 +00:00
|
|
|
decimal lambdaTemp = mImpulseLowerLimit;
|
|
|
|
mImpulseLowerLimit = std::max(mImpulseLowerLimit + deltaLambdaLower, decimal(0.0));
|
|
|
|
deltaLambdaLower = mImpulseLowerLimit - lambdaTemp;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower limit constraint of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = -deltaLambdaLower * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody1 = -deltaLambdaLower * mR1PlusUCrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
v1 += inverseMassBody1 * linearImpulseBody1;
|
|
|
|
w1 += mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower limit constraint of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = deltaLambdaLower * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody2 = deltaLambdaLower * mR2CrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
v2 += inverseMassBody2 * linearImpulseBody2;
|
|
|
|
w2 += mI2 * angularImpulseBody2;
|
2013-05-23 20:52:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the upper limit is violated
|
|
|
|
if (mIsUpperLimitViolated) {
|
|
|
|
|
|
|
|
// Compute J*v for the upper limit constraint
|
|
|
|
const decimal JvUpperLimit = mSliderAxisWorld.dot(v1) + mR1PlusUCrossSliderAxis.dot(w1)
|
|
|
|
- mSliderAxisWorld.dot(v2) - mR2CrossSliderAxis.dot(w2);
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the upper limit constraint
|
|
|
|
decimal deltaLambdaUpper = mInverseMassMatrixLimit * (-JvUpperLimit -mBUpperLimit);
|
|
|
|
decimal lambdaTemp = mImpulseUpperLimit;
|
|
|
|
mImpulseUpperLimit = std::max(mImpulseUpperLimit + deltaLambdaUpper, decimal(0.0));
|
|
|
|
deltaLambdaUpper = mImpulseUpperLimit - lambdaTemp;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the upper limit constraint of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = deltaLambdaUpper * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody1 = deltaLambdaUpper * mR1PlusUCrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
v1 += inverseMassBody1 * linearImpulseBody1;
|
|
|
|
w1 += mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the upper limit constraint of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = -deltaLambdaUpper * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody2 = -deltaLambdaUpper * mR2CrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
v2 += inverseMassBody2 * linearImpulseBody2;
|
|
|
|
w2 += mI2 * angularImpulseBody2;
|
2013-05-22 21:38:30 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-29 20:45:02 +00:00
|
|
|
|
|
|
|
// --------------- Motor --------------- //
|
|
|
|
|
|
|
|
if (mIsMotorEnabled) {
|
|
|
|
|
|
|
|
// Compute J*v for the motor
|
|
|
|
const decimal JvMotor = mSliderAxisWorld.dot(v1) - mSliderAxisWorld.dot(v2);
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the motor
|
|
|
|
const decimal maxMotorImpulse = mMaxMotorForce * constraintSolverData.timeStep;
|
2013-06-09 14:31:01 +00:00
|
|
|
decimal deltaLambdaMotor = mInverseMassMatrixMotor * (-JvMotor - mMotorSpeed);
|
2013-05-29 20:45:02 +00:00
|
|
|
decimal lambdaTemp = mImpulseMotor;
|
|
|
|
mImpulseMotor = clamp(mImpulseMotor + deltaLambdaMotor, -maxMotorImpulse, maxMotorImpulse);
|
|
|
|
deltaLambdaMotor = mImpulseMotor - lambdaTemp;
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the motor of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = deltaLambdaMotor * mSliderAxisWorld;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
v1 += inverseMassBody1 * linearImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the motor of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = -deltaLambdaMotor * mSliderAxisWorld;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
v2 += inverseMassBody2 * linearImpulseBody2;
|
2013-05-29 20:45:02 +00:00
|
|
|
}
|
2013-05-08 21:33:04 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 20:28:31 +00:00
|
|
|
// Solve the position constraint (for position error correction)
|
2013-05-08 21:33:04 +00:00
|
|
|
void SliderJoint::solvePositionConstraint(const ConstraintSolverData& constraintSolverData) {
|
|
|
|
|
2013-06-26 20:28:31 +00:00
|
|
|
// If the error position correction technique is not the non-linear-gauss-seidel, we do
|
|
|
|
// do not execute this method
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mPositionCorrectionTechnique != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) return;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
|
|
|
// Get the bodies positions and orientations
|
|
|
|
Vector3& x1 = constraintSolverData.positions[mIndexBody1];
|
|
|
|
Vector3& x2 = constraintSolverData.positions[mIndexBody2];
|
|
|
|
Quaternion& q1 = constraintSolverData.orientations[mIndexBody1];
|
|
|
|
Quaternion& q2 = constraintSolverData.orientations[mIndexBody2];
|
|
|
|
|
|
|
|
// Get the inverse mass and inverse inertia tensors of the bodies
|
2013-11-21 22:24:11 +00:00
|
|
|
decimal inverseMassBody1 = mBody1->mMassInverse;
|
|
|
|
decimal inverseMassBody2 = mBody2->mMassInverse;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
|
|
|
// Recompute the inertia tensor of bodies
|
|
|
|
mI1 = mBody1->getInertiaTensorInverseWorld();
|
|
|
|
mI2 = mBody2->getInertiaTensorInverseWorld();
|
|
|
|
|
|
|
|
// Vector from body center to the anchor point
|
|
|
|
mR1 = q1 * mLocalAnchorPointBody1;
|
|
|
|
mR2 = q2 * mLocalAnchorPointBody2;
|
|
|
|
|
|
|
|
// Compute the vector u (difference between anchor points)
|
|
|
|
const Vector3 u = x2 + mR2 - x1 - mR1;
|
|
|
|
|
|
|
|
// Compute the two orthogonal vectors to the slider axis in world-space
|
|
|
|
mSliderAxisWorld = q1 * mSliderAxisBody1;
|
|
|
|
mSliderAxisWorld.normalize();
|
|
|
|
mN1 = mSliderAxisWorld.getOneUnitOrthogonalVector();
|
|
|
|
mN2 = mSliderAxisWorld.cross(mN1);
|
|
|
|
|
|
|
|
// Check if the limit constraints are violated or not
|
|
|
|
decimal uDotSliderAxis = u.dot(mSliderAxisWorld);
|
|
|
|
decimal lowerLimitError = uDotSliderAxis - mLowerLimit;
|
|
|
|
decimal upperLimitError = mUpperLimit - uDotSliderAxis;
|
|
|
|
mIsLowerLimitViolated = lowerLimitError <= 0;
|
|
|
|
mIsUpperLimitViolated = upperLimitError <= 0;
|
|
|
|
|
|
|
|
// Compute the cross products used in the Jacobians
|
|
|
|
mR2CrossN1 = mR2.cross(mN1);
|
|
|
|
mR2CrossN2 = mR2.cross(mN2);
|
|
|
|
mR2CrossSliderAxis = mR2.cross(mSliderAxisWorld);
|
|
|
|
const Vector3 r1PlusU = mR1 + u;
|
|
|
|
mR1PlusUCrossN1 = (r1PlusU).cross(mN1);
|
|
|
|
mR1PlusUCrossN2 = (r1PlusU).cross(mN2);
|
|
|
|
mR1PlusUCrossSliderAxis = (r1PlusU).cross(mSliderAxisWorld);
|
|
|
|
|
|
|
|
// --------------- Translation Constraints --------------- //
|
|
|
|
|
|
|
|
// Recompute the inverse of the mass matrix K=JM^-1J^t for the 2 translation
|
|
|
|
// constraints (2x2 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
decimal sumInverseMass = mBody1->mMassInverse + mBody2->mMassInverse;
|
|
|
|
Vector3 I1R1PlusUCrossN1 = mI1 * mR1PlusUCrossN1;
|
|
|
|
Vector3 I1R1PlusUCrossN2 = mI1 * mR1PlusUCrossN2;
|
|
|
|
Vector3 I2R2CrossN1 = mI2 * mR2CrossN1;
|
|
|
|
Vector3 I2R2CrossN2 = mI2 * mR2CrossN2;
|
2013-06-26 20:28:31 +00:00
|
|
|
const decimal el11 = sumInverseMass + mR1PlusUCrossN1.dot(I1R1PlusUCrossN1) +
|
|
|
|
mR2CrossN1.dot(I2R2CrossN1);
|
|
|
|
const decimal el12 = mR1PlusUCrossN1.dot(I1R1PlusUCrossN2) +
|
|
|
|
mR2CrossN1.dot(I2R2CrossN2);
|
|
|
|
const decimal el21 = mR1PlusUCrossN2.dot(I1R1PlusUCrossN1) +
|
|
|
|
mR2CrossN2.dot(I2R2CrossN1);
|
|
|
|
const decimal el22 = sumInverseMass + mR1PlusUCrossN2.dot(I1R1PlusUCrossN2) +
|
|
|
|
mR2CrossN2.dot(I2R2CrossN2);
|
|
|
|
Matrix2x2 matrixKTranslation(el11, el12, el21, el22);
|
|
|
|
mInverseMassMatrixTranslationConstraint.setToZero();
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mBody1->getType() == BodyType::DYNAMIC || mBody2->getType() == BodyType::DYNAMIC) {
|
2013-06-26 20:28:31 +00:00
|
|
|
mInverseMassMatrixTranslationConstraint = matrixKTranslation.getInverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the position error for the 2 translation constraints
|
|
|
|
const Vector2 translationError(u.dot(mN1), u.dot(mN2));
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the 2 translation constraints
|
|
|
|
Vector2 lambdaTranslation = mInverseMassMatrixTranslationConstraint * (-translationError);
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 2 translation constraints of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = -mN1 * lambdaTranslation.x - mN2 * lambdaTranslation.y;
|
|
|
|
Vector3 angularImpulseBody1 = -mR1PlusUCrossN1 * lambdaTranslation.x -
|
|
|
|
mR1PlusUCrossN2 * lambdaTranslation.y;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
const Vector3 v1 = inverseMassBody1 * linearImpulseBody1;
|
|
|
|
Vector3 w1 = mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 1
|
|
|
|
x1 += v1;
|
|
|
|
q1 += Quaternion(0, w1) * q1 * decimal(0.5);
|
|
|
|
q1.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 2 translation constraints of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = mN1 * lambdaTranslation.x + mN2 * lambdaTranslation.y;
|
|
|
|
Vector3 angularImpulseBody2 = mR2CrossN1 * lambdaTranslation.x +
|
|
|
|
mR2CrossN2 * lambdaTranslation.y;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
const Vector3 v2 = inverseMassBody2 * linearImpulseBody2;
|
|
|
|
Vector3 w2 = mI2 * angularImpulseBody2;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 2
|
|
|
|
x2 += v2;
|
|
|
|
q2 += Quaternion(0, w2) * q2 * decimal(0.5);
|
|
|
|
q2.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
|
|
|
|
// --------------- Rotation Constraints --------------- //
|
|
|
|
|
|
|
|
// Compute the inverse of the mass matrix K=JM^-1J^t for the 3 rotation
|
|
|
|
// contraints (3x3 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
mInverseMassMatrixRotationConstraint = mI1 + mI2;
|
2016-07-08 05:25:37 +00:00
|
|
|
if (mBody1->getType() == BodyType::DYNAMIC || mBody2->getType() == BodyType::DYNAMIC) {
|
2013-06-26 20:28:31 +00:00
|
|
|
mInverseMassMatrixRotationConstraint = mInverseMassMatrixRotationConstraint.getInverse();
|
|
|
|
}
|
|
|
|
|
2017-06-30 13:46:15 +00:00
|
|
|
// Calculate difference in rotation
|
|
|
|
//
|
|
|
|
// The rotation should be:
|
|
|
|
//
|
|
|
|
// q2 = q1 r0
|
|
|
|
//
|
|
|
|
// But because of drift the actual rotation is:
|
|
|
|
//
|
|
|
|
// q2 = qError q1 r0
|
|
|
|
// <=> qError = q2 r0^-1 q1^-1
|
|
|
|
//
|
|
|
|
// Where:
|
|
|
|
// q1 = current rotation of body 1
|
|
|
|
// q2 = current rotation of body 2
|
|
|
|
// qError = error that needs to be reduced to zero
|
|
|
|
Quaternion qError = q2 * mInitOrientationDifferenceInv * q1.getInverse();
|
|
|
|
|
|
|
|
// A quaternion can be seen as:
|
|
|
|
//
|
|
|
|
// q = [sin(theta / 2) * v, cos(theta/2)]
|
|
|
|
//
|
|
|
|
// Where:
|
|
|
|
// v = rotation vector
|
|
|
|
// theta = rotation angle
|
|
|
|
//
|
|
|
|
// If we assume theta is small (error is small) then sin(x) = x so an approximation of the error angles is:
|
2013-06-26 20:28:31 +00:00
|
|
|
const Vector3 errorRotation = decimal(2.0) * qError.getVectorV();
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the 3 rotation constraints
|
|
|
|
Vector3 lambdaRotation = mInverseMassMatrixRotationConstraint * (-errorRotation);
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 3 rotation constraints of body 1
|
|
|
|
angularImpulseBody1 = -lambdaRotation;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
w1 = mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 1
|
|
|
|
q1 += Quaternion(0, w1) * q1 * decimal(0.5);
|
|
|
|
q1.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the 3 rotation constraints of body 2
|
|
|
|
angularImpulseBody2 = lambdaRotation;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
w2 = mI2 * angularImpulseBody2;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 2
|
|
|
|
q2 += Quaternion(0, w2) * q2 * decimal(0.5);
|
|
|
|
q2.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
|
|
|
|
// --------------- Limits Constraints --------------- //
|
|
|
|
|
|
|
|
if (mIsLimitEnabled) {
|
|
|
|
|
|
|
|
if (mIsLowerLimitViolated || mIsUpperLimitViolated) {
|
|
|
|
|
|
|
|
// Compute the inverse of the mass matrix K=JM^-1J^t for the limits (1x1 matrix)
|
2013-11-21 22:24:11 +00:00
|
|
|
mInverseMassMatrixLimit = mBody1->mMassInverse + mBody2->mMassInverse +
|
|
|
|
mR1PlusUCrossSliderAxis.dot(mI1 * mR1PlusUCrossSliderAxis) +
|
|
|
|
mR2CrossSliderAxis.dot(mI2 * mR2CrossSliderAxis);
|
2013-06-26 20:28:31 +00:00
|
|
|
mInverseMassMatrixLimit = (mInverseMassMatrixLimit > 0.0) ?
|
|
|
|
decimal(1.0) / mInverseMassMatrixLimit : decimal(0.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the lower limit is violated
|
|
|
|
if (mIsLowerLimitViolated) {
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the lower limit constraint
|
|
|
|
decimal lambdaLowerLimit = mInverseMassMatrixLimit * (-lowerLimitError);
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower limit constraint of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = -lambdaLowerLimit * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody1 = -lambdaLowerLimit * mR1PlusUCrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
const Vector3 v1 = inverseMassBody1 * linearImpulseBody1;
|
|
|
|
const Vector3 w1 = mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 1
|
|
|
|
x1 += v1;
|
|
|
|
q1 += Quaternion(0, w1) * q1 * decimal(0.5);
|
|
|
|
q1.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the lower limit constraint of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = lambdaLowerLimit * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody2 = lambdaLowerLimit * mR2CrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
const Vector3 v2 = inverseMassBody2 * linearImpulseBody2;
|
|
|
|
const Vector3 w2 = mI2 * angularImpulseBody2;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 2
|
|
|
|
x2 += v2;
|
|
|
|
q2 += Quaternion(0, w2) * q2 * decimal(0.5);
|
|
|
|
q2.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the upper limit is violated
|
|
|
|
if (mIsUpperLimitViolated) {
|
|
|
|
|
|
|
|
// Compute the Lagrange multiplier lambda for the upper limit constraint
|
|
|
|
decimal lambdaUpperLimit = mInverseMassMatrixLimit * (-upperLimitError);
|
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the upper limit constraint of body 1
|
|
|
|
const Vector3 linearImpulseBody1 = lambdaUpperLimit * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody1 = lambdaUpperLimit * mR1PlusUCrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 1
|
|
|
|
const Vector3 v1 = inverseMassBody1 * linearImpulseBody1;
|
|
|
|
const Vector3 w1 = mI1 * angularImpulseBody1;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 1
|
|
|
|
x1 += v1;
|
|
|
|
q1 += Quaternion(0, w1) * q1 * decimal(0.5);
|
|
|
|
q1.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Compute the impulse P=J^T * lambda for the upper limit constraint of body 2
|
|
|
|
const Vector3 linearImpulseBody2 = -lambdaUpperLimit * mSliderAxisWorld;
|
|
|
|
const Vector3 angularImpulseBody2 = -lambdaUpperLimit * mR2CrossSliderAxis;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Apply the impulse to the body 2
|
|
|
|
const Vector3 v2 = inverseMassBody2 * linearImpulseBody2;
|
|
|
|
const Vector3 w2 = mI2 * angularImpulseBody2;
|
2013-06-26 20:28:31 +00:00
|
|
|
|
2013-11-21 22:24:11 +00:00
|
|
|
// Update the body position/orientation of body 2
|
|
|
|
x2 += v2;
|
|
|
|
q2 += Quaternion(0, w2) * q2 * decimal(0.5);
|
|
|
|
q2.normalize();
|
2013-06-26 20:28:31 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-08 21:33:04 +00:00
|
|
|
}
|
2013-05-29 20:45:02 +00:00
|
|
|
|
|
|
|
// Enable/Disable the limits of the joint
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @param isLimitEnabled True if you want to enable the joint limits and false
|
|
|
|
* otherwise
|
|
|
|
*/
|
2013-05-29 20:45:02 +00:00
|
|
|
void SliderJoint::enableLimit(bool isLimitEnabled) {
|
|
|
|
|
|
|
|
if (isLimitEnabled != mIsLimitEnabled) {
|
|
|
|
|
|
|
|
mIsLimitEnabled = isLimitEnabled;
|
|
|
|
|
|
|
|
// Reset the limits
|
|
|
|
resetLimits();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable/Disable the motor of the joint
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @param isMotorEnabled True if you want to enable the joint motor and false
|
|
|
|
* otherwise
|
|
|
|
*/
|
2013-05-29 20:45:02 +00:00
|
|
|
void SliderJoint::enableMotor(bool isMotorEnabled) {
|
|
|
|
|
|
|
|
mIsMotorEnabled = isMotorEnabled;
|
|
|
|
mImpulseMotor = 0.0;
|
|
|
|
|
2013-09-03 17:30:43 +00:00
|
|
|
// Wake up the two bodies of the joint
|
|
|
|
mBody1->setIsSleeping(false);
|
|
|
|
mBody2->setIsSleeping(false);
|
2013-05-29 20:45:02 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 20:28:31 +00:00
|
|
|
// Return the current translation value of the joint
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @return The current translation distance of the joint (in meters)
|
|
|
|
*/
|
2013-06-26 20:28:31 +00:00
|
|
|
decimal SliderJoint::getTranslation() const {
|
|
|
|
|
2015-02-12 21:31:26 +00:00
|
|
|
// TODO : Check if we need to compare rigid body position or center of mass here
|
|
|
|
|
2013-06-26 20:28:31 +00:00
|
|
|
// Get the bodies positions and orientations
|
|
|
|
const Vector3& x1 = mBody1->getTransform().getPosition();
|
|
|
|
const Vector3& x2 = mBody2->getTransform().getPosition();
|
|
|
|
const Quaternion& q1 = mBody1->getTransform().getOrientation();
|
|
|
|
const Quaternion& q2 = mBody2->getTransform().getOrientation();
|
|
|
|
|
|
|
|
// Compute the two anchor points in world-space coordinates
|
|
|
|
const Vector3 anchorBody1 = x1 + q1 * mLocalAnchorPointBody1;
|
|
|
|
const Vector3 anchorBody2 = x2 + q2 * mLocalAnchorPointBody2;
|
|
|
|
|
|
|
|
// Compute the vector u (difference between anchor points)
|
|
|
|
const Vector3 u = anchorBody2 - anchorBody1;
|
|
|
|
|
|
|
|
// Compute the slider axis in world-space
|
|
|
|
Vector3 sliderAxisWorld = q1 * mSliderAxisBody1;
|
|
|
|
sliderAxisWorld.normalize();
|
|
|
|
|
|
|
|
// Compute and return the translation value
|
|
|
|
return u.dot(sliderAxisWorld);
|
|
|
|
}
|
|
|
|
|
2013-06-09 14:31:01 +00:00
|
|
|
// Set the minimum translation limit
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @param lowerLimit The minimum translation limit of the joint (in meters)
|
|
|
|
*/
|
2013-06-09 14:31:01 +00:00
|
|
|
void SliderJoint::setMinTranslationLimit(decimal lowerLimit) {
|
2013-05-29 20:45:02 +00:00
|
|
|
|
|
|
|
assert(lowerLimit <= mUpperLimit);
|
|
|
|
|
|
|
|
if (lowerLimit != mLowerLimit) {
|
|
|
|
|
|
|
|
mLowerLimit = lowerLimit;
|
|
|
|
|
|
|
|
// Reset the limits
|
|
|
|
resetLimits();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-09 14:31:01 +00:00
|
|
|
// Set the maximum translation limit
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @param lowerLimit The maximum translation limit of the joint (in meters)
|
|
|
|
*/
|
2013-06-09 14:31:01 +00:00
|
|
|
void SliderJoint::setMaxTranslationLimit(decimal upperLimit) {
|
2013-05-29 20:45:02 +00:00
|
|
|
|
|
|
|
assert(mLowerLimit <= upperLimit);
|
|
|
|
|
|
|
|
if (upperLimit != mUpperLimit) {
|
|
|
|
|
|
|
|
mUpperLimit = upperLimit;
|
|
|
|
|
|
|
|
// Reset the limits
|
|
|
|
resetLimits();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the limits
|
|
|
|
void SliderJoint::resetLimits() {
|
|
|
|
|
|
|
|
// Reset the accumulated impulses for the limits
|
|
|
|
mImpulseLowerLimit = 0.0;
|
|
|
|
mImpulseUpperLimit = 0.0;
|
|
|
|
|
2013-09-03 17:30:43 +00:00
|
|
|
// Wake up the two bodies of the joint
|
|
|
|
mBody1->setIsSleeping(false);
|
|
|
|
mBody2->setIsSleeping(false);
|
2013-05-29 20:45:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the motor speed
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @param motorSpeed The speed of the joint motor (in meters per second)
|
|
|
|
*/
|
2013-05-29 20:45:02 +00:00
|
|
|
void SliderJoint::setMotorSpeed(decimal motorSpeed) {
|
|
|
|
|
|
|
|
if (motorSpeed != mMotorSpeed) {
|
|
|
|
|
|
|
|
mMotorSpeed = motorSpeed;
|
|
|
|
|
2013-09-03 17:30:43 +00:00
|
|
|
// Wake up the two bodies of the joint
|
|
|
|
mBody1->setIsSleeping(false);
|
|
|
|
mBody2->setIsSleeping(false);
|
2013-05-29 20:45:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the maximum motor force
|
2015-02-12 21:31:26 +00:00
|
|
|
/**
|
|
|
|
* @param maxMotorForce The maximum force of the joint motor (in Newton x meters)
|
|
|
|
*/
|
2013-05-29 20:45:02 +00:00
|
|
|
void SliderJoint::setMaxMotorForce(decimal maxMotorForce) {
|
|
|
|
|
|
|
|
if (maxMotorForce != mMaxMotorForce) {
|
|
|
|
|
|
|
|
assert(mMaxMotorForce >= 0.0);
|
|
|
|
mMaxMotorForce = maxMotorForce;
|
|
|
|
|
2013-09-03 17:30:43 +00:00
|
|
|
// Wake up the two bodies of the joint
|
|
|
|
mBody1->setIsSleeping(false);
|
|
|
|
mBody2->setIsSleeping(false);
|
2013-05-29 20:45:02 +00:00
|
|
|
}
|
|
|
|
}
|