Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/SliderConstraint.cpp
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Constraints/SliderConstraint.h>7#include <Jolt/Physics/Body/Body.h>8#include <Jolt/ObjectStream/TypeDeclarations.h>9#include <Jolt/Core/StreamIn.h>10#include <Jolt/Core/StreamOut.h>11#ifdef JPH_DEBUG_RENDERER12#include <Jolt/Renderer/DebugRenderer.h>13#endif // JPH_DEBUG_RENDERER1415JPH_NAMESPACE_BEGIN1617using namespace literals;1819JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SliderConstraintSettings)20{21JPH_ADD_BASE_CLASS(SliderConstraintSettings, TwoBodyConstraintSettings)2223JPH_ADD_ENUM_ATTRIBUTE(SliderConstraintSettings, mSpace)24JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mAutoDetectPoint)25JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mPoint1)26JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mSliderAxis1)27JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mNormalAxis1)28JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mPoint2)29JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mSliderAxis2)30JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mNormalAxis2)31JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mLimitsMin)32JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mLimitsMax)33JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode")34JPH_ADD_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library35JPH_ADD_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping")36JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mMaxFrictionForce)37JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mMotorSettings)38}3940void SliderConstraintSettings::SetSliderAxis(Vec3Arg inSliderAxis)41{42JPH_ASSERT(mSpace == EConstraintSpace::WorldSpace);4344mSliderAxis1 = mSliderAxis2 = inSliderAxis;45mNormalAxis1 = mNormalAxis2 = inSliderAxis.GetNormalizedPerpendicular();46}4748void SliderConstraintSettings::SaveBinaryState(StreamOut &inStream) const49{50ConstraintSettings::SaveBinaryState(inStream);5152inStream.Write(mSpace);53inStream.Write(mAutoDetectPoint);54inStream.Write(mPoint1);55inStream.Write(mSliderAxis1);56inStream.Write(mNormalAxis1);57inStream.Write(mPoint2);58inStream.Write(mSliderAxis2);59inStream.Write(mNormalAxis2);60inStream.Write(mLimitsMin);61inStream.Write(mLimitsMax);62inStream.Write(mMaxFrictionForce);63mLimitsSpringSettings.SaveBinaryState(inStream);64mMotorSettings.SaveBinaryState(inStream);65}6667void SliderConstraintSettings::RestoreBinaryState(StreamIn &inStream)68{69ConstraintSettings::RestoreBinaryState(inStream);7071inStream.Read(mSpace);72inStream.Read(mAutoDetectPoint);73inStream.Read(mPoint1);74inStream.Read(mSliderAxis1);75inStream.Read(mNormalAxis1);76inStream.Read(mPoint2);77inStream.Read(mSliderAxis2);78inStream.Read(mNormalAxis2);79inStream.Read(mLimitsMin);80inStream.Read(mLimitsMax);81inStream.Read(mMaxFrictionForce);82mLimitsSpringSettings.RestoreBinaryState(inStream);83mMotorSettings.RestoreBinaryState(inStream);84}8586TwoBodyConstraint *SliderConstraintSettings::Create(Body &inBody1, Body &inBody2) const87{88return new SliderConstraint(inBody1, inBody2, *this);89}9091SliderConstraint::SliderConstraint(Body &inBody1, Body &inBody2, const SliderConstraintSettings &inSettings) :92TwoBodyConstraint(inBody1, inBody2, inSettings),93mMaxFrictionForce(inSettings.mMaxFrictionForce),94mMotorSettings(inSettings.mMotorSettings)95{96// Store inverse of initial rotation from body 1 to body 2 in body 1 space97mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXY(inSettings.mSliderAxis1, inSettings.mNormalAxis1, inSettings.mSliderAxis2, inSettings.mNormalAxis2);9899if (inSettings.mSpace == EConstraintSpace::WorldSpace)100{101RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform();102RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform();103104if (inSettings.mAutoDetectPoint)105{106// Determine anchor point: If any of the bodies can never be dynamic use the other body as anchor point107RVec3 anchor;108if (!inBody1.CanBeKinematicOrDynamic())109anchor = inBody2.GetCenterOfMassPosition();110else if (!inBody2.CanBeKinematicOrDynamic())111anchor = inBody1.GetCenterOfMassPosition();112else113{114// Otherwise use weighted anchor point towards the lightest body115Real inv_m1 = Real(inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked());116Real inv_m2 = Real(inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked());117Real total_inv_mass = inv_m1 + inv_m2;118if (total_inv_mass != 0.0_r)119anchor = (inv_m1 * inBody1.GetCenterOfMassPosition() + inv_m2 * inBody2.GetCenterOfMassPosition()) / total_inv_mass;120else121anchor = inBody1.GetCenterOfMassPosition();122}123124// Store local positions125mLocalSpacePosition1 = Vec3(inv_transform1 * anchor);126mLocalSpacePosition2 = Vec3(inv_transform2 * anchor);127}128else129{130// Store local positions131mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1);132mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2);133}134135// If all properties were specified in world space, take them to local space now136mLocalSpaceSliderAxis1 = inv_transform1.Multiply3x3(inSettings.mSliderAxis1).Normalized();137mLocalSpaceNormal1 = inv_transform1.Multiply3x3(inSettings.mNormalAxis1).Normalized();138139// Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2140// => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10141mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation();142}143else144{145// Store local positions146mLocalSpacePosition1 = Vec3(inSettings.mPoint1);147mLocalSpacePosition2 = Vec3(inSettings.mPoint2);148149// Store local space axis150mLocalSpaceSliderAxis1 = inSettings.mSliderAxis1;151mLocalSpaceNormal1 = inSettings.mNormalAxis1;152}153154// Calculate 2nd local space normal155mLocalSpaceNormal2 = mLocalSpaceSliderAxis1.Cross(mLocalSpaceNormal1);156157// Store limits158JPH_ASSERT(inSettings.mLimitsMin != inSettings.mLimitsMax || inSettings.mLimitsSpringSettings.mFrequency > 0.0f, "Better use a fixed constraint");159SetLimits(inSettings.mLimitsMin, inSettings.mLimitsMax);160161// Store spring settings162SetLimitsSpringSettings(inSettings.mLimitsSpringSettings);163}164165void SliderConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)166{167if (mBody1->GetID() == inBodyID)168mLocalSpacePosition1 -= inDeltaCOM;169else if (mBody2->GetID() == inBodyID)170mLocalSpacePosition2 -= inDeltaCOM;171}172173float SliderConstraint::GetCurrentPosition() const174{175// See: CalculateR1R2U and CalculateSlidingAxisAndPosition176Vec3 r1 = mBody1->GetRotation() * mLocalSpacePosition1;177Vec3 r2 = mBody2->GetRotation() * mLocalSpacePosition2;178Vec3 u = Vec3(mBody2->GetCenterOfMassPosition() - mBody1->GetCenterOfMassPosition()) + r2 - r1;179return u.Dot(mBody1->GetRotation() * mLocalSpaceSliderAxis1);180}181182void SliderConstraint::SetLimits(float inLimitsMin, float inLimitsMax)183{184JPH_ASSERT(inLimitsMin <= 0.0f);185JPH_ASSERT(inLimitsMax >= 0.0f);186mLimitsMin = inLimitsMin;187mLimitsMax = inLimitsMax;188mHasLimits = mLimitsMin != -FLT_MAX || mLimitsMax != FLT_MAX;189}190191void SliderConstraint::CalculateR1R2U(Mat44Arg inRotation1, Mat44Arg inRotation2)192{193// Calculate points relative to body194mR1 = inRotation1 * mLocalSpacePosition1;195mR2 = inRotation2 * mLocalSpacePosition2;196197// Calculate X2 + R2 - X1 - R1198mU = Vec3(mBody2->GetCenterOfMassPosition() - mBody1->GetCenterOfMassPosition()) + mR2 - mR1;199}200201void SliderConstraint::CalculatePositionConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2)202{203// Calculate world space normals204mN1 = inRotation1 * mLocalSpaceNormal1;205mN2 = inRotation1 * mLocalSpaceNormal2;206207mPositionConstraintPart.CalculateConstraintProperties(*mBody1, inRotation1, mR1 + mU, *mBody2, inRotation2, mR2, mN1, mN2);208}209210void SliderConstraint::CalculateSlidingAxisAndPosition(Mat44Arg inRotation1)211{212if (mHasLimits || mMotorState != EMotorState::Off || mMaxFrictionForce > 0.0f)213{214// Calculate world space slider axis215mWorldSpaceSliderAxis = inRotation1 * mLocalSpaceSliderAxis1;216217// Calculate slide distance along axis218mD = mU.Dot(mWorldSpaceSliderAxis);219}220}221222void SliderConstraint::CalculatePositionLimitsConstraintProperties(float inDeltaTime)223{224// Check if distance is within limits225bool below_min = mD <= mLimitsMin;226if (mHasLimits && (below_min || mD >= mLimitsMax))227mPositionLimitsConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, 0.0f, mD - (below_min? mLimitsMin : mLimitsMax), mLimitsSpringSettings);228else229mPositionLimitsConstraintPart.Deactivate();230}231232void SliderConstraint::CalculateMotorConstraintProperties(float inDeltaTime)233{234switch (mMotorState)235{236case EMotorState::Off:237if (mMaxFrictionForce > 0.0f)238mMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis);239else240mMotorConstraintPart.Deactivate();241break;242243case EMotorState::Velocity:244mMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, -mTargetVelocity);245break;246247case EMotorState::Position:248if (mMotorSettings.mSpringSettings.HasStiffness())249mMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, 0.0f, mD - mTargetPosition, mMotorSettings.mSpringSettings);250else251mMotorConstraintPart.Deactivate();252break;253}254}255256void SliderConstraint::SetupVelocityConstraint(float inDeltaTime)257{258// Calculate constraint properties that are constant while bodies don't move259Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());260Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());261CalculateR1R2U(rotation1, rotation2);262CalculatePositionConstraintProperties(rotation1, rotation2);263mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, *mBody2, rotation2);264CalculateSlidingAxisAndPosition(rotation1);265CalculatePositionLimitsConstraintProperties(inDeltaTime);266CalculateMotorConstraintProperties(inDeltaTime);267}268269void SliderConstraint::ResetWarmStart()270{271mMotorConstraintPart.Deactivate();272mPositionConstraintPart.Deactivate();273mRotationConstraintPart.Deactivate();274mPositionLimitsConstraintPart.Deactivate();275}276277void SliderConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)278{279// Warm starting: Apply previous frame impulse280mMotorConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceSliderAxis, inWarmStartImpulseRatio);281mPositionConstraintPart.WarmStart(*mBody1, *mBody2, mN1, mN2, inWarmStartImpulseRatio);282mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);283mPositionLimitsConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceSliderAxis, inWarmStartImpulseRatio);284}285286bool SliderConstraint::SolveVelocityConstraint(float inDeltaTime)287{288// Solve motor289bool motor = false;290if (mMotorConstraintPart.IsActive())291{292switch (mMotorState)293{294case EMotorState::Off:295{296float max_lambda = mMaxFrictionForce * inDeltaTime;297motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, -max_lambda, max_lambda);298break;299}300301case EMotorState::Velocity:302case EMotorState::Position:303motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, inDeltaTime * mMotorSettings.mMinForceLimit, inDeltaTime * mMotorSettings.mMaxForceLimit);304break;305}306}307308// Solve position constraint along 2 axis309bool pos = mPositionConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mN1, mN2);310311// Solve rotation constraint312bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);313314// Solve limits along slider axis315bool limit = false;316if (mPositionLimitsConstraintPart.IsActive())317{318float min_lambda, max_lambda;319if (mLimitsMin == mLimitsMax)320{321min_lambda = -FLT_MAX;322max_lambda = FLT_MAX;323}324else if (mD <= mLimitsMin)325{326min_lambda = 0.0f;327max_lambda = FLT_MAX;328}329else330{331min_lambda = -FLT_MAX;332max_lambda = 0.0f;333}334limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, min_lambda, max_lambda);335}336337return motor || pos || rot || limit;338}339340bool SliderConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)341{342// Motor operates on velocities only, don't call SolvePositionConstraint343344// Solve position constraint along 2 axis345Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());346Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());347CalculateR1R2U(rotation1, rotation2);348CalculatePositionConstraintProperties(rotation1, rotation2);349bool pos = mPositionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mU, mN1, mN2, inBaumgarte);350351// Solve rotation constraint352mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation()));353bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte);354355// Solve limits along slider axis356bool limit = false;357if (mHasLimits && mLimitsSpringSettings.mFrequency <= 0.0f)358{359rotation1 = Mat44::sRotation(mBody1->GetRotation());360rotation2 = Mat44::sRotation(mBody2->GetRotation());361CalculateR1R2U(rotation1, rotation2);362CalculateSlidingAxisAndPosition(rotation1);363CalculatePositionLimitsConstraintProperties(inDeltaTime);364if (mPositionLimitsConstraintPart.IsActive())365{366if (mD <= mLimitsMin)367limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, mD - mLimitsMin, inBaumgarte);368else369{370JPH_ASSERT(mD >= mLimitsMax);371limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, mD - mLimitsMax, inBaumgarte);372}373}374}375376return pos || rot || limit;377}378379#ifdef JPH_DEBUG_RENDERER380void SliderConstraint::DrawConstraint(DebugRenderer *inRenderer) const381{382RMat44 transform1 = mBody1->GetCenterOfMassTransform();383RMat44 transform2 = mBody2->GetCenterOfMassTransform();384385// Transform the local positions into world space386Vec3 slider_axis = transform1.Multiply3x3(mLocalSpaceSliderAxis1);387RVec3 position1 = transform1 * mLocalSpacePosition1;388RVec3 position2 = transform2 * mLocalSpacePosition2;389390// Draw constraint391inRenderer->DrawMarker(position1, Color::sRed, 0.1f);392inRenderer->DrawMarker(position2, Color::sGreen, 0.1f);393inRenderer->DrawLine(position1, position2, Color::sGreen);394395// Draw motor396switch (mMotorState)397{398case EMotorState::Position:399inRenderer->DrawMarker(position1 + mTargetPosition * slider_axis, Color::sYellow, 1.0f);400break;401402case EMotorState::Velocity:403{404Vec3 cur_vel = (mBody2->GetLinearVelocity() - mBody1->GetLinearVelocity()).Dot(slider_axis) * slider_axis;405inRenderer->DrawLine(position2, position2 + cur_vel, Color::sBlue);406inRenderer->DrawArrow(position2 + cur_vel, position2 + mTargetVelocity * slider_axis, Color::sRed, 0.1f);407break;408}409410case EMotorState::Off:411break;412}413}414415void SliderConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const416{417if (mHasLimits)418{419RMat44 transform1 = mBody1->GetCenterOfMassTransform();420RMat44 transform2 = mBody2->GetCenterOfMassTransform();421422// Transform the local positions into world space423Vec3 slider_axis = transform1.Multiply3x3(mLocalSpaceSliderAxis1);424RVec3 position1 = transform1 * mLocalSpacePosition1;425RVec3 position2 = transform2 * mLocalSpacePosition2;426427// Calculate the limits in world space428RVec3 limits_min = position1 + mLimitsMin * slider_axis;429RVec3 limits_max = position1 + mLimitsMax * slider_axis;430431inRenderer->DrawLine(limits_min, position1, Color::sWhite);432inRenderer->DrawLine(position2, limits_max, Color::sWhite);433434inRenderer->DrawMarker(limits_min, Color::sWhite, 0.1f);435inRenderer->DrawMarker(limits_max, Color::sWhite, 0.1f);436}437}438#endif // JPH_DEBUG_RENDERER439440void SliderConstraint::SaveState(StateRecorder &inStream) const441{442TwoBodyConstraint::SaveState(inStream);443444mMotorConstraintPart.SaveState(inStream);445mPositionConstraintPart.SaveState(inStream);446mRotationConstraintPart.SaveState(inStream);447mPositionLimitsConstraintPart.SaveState(inStream);448449inStream.Write(mMotorState);450inStream.Write(mTargetVelocity);451inStream.Write(mTargetPosition);452}453454void SliderConstraint::RestoreState(StateRecorder &inStream)455{456TwoBodyConstraint::RestoreState(inStream);457458mMotorConstraintPart.RestoreState(inStream);459mPositionConstraintPart.RestoreState(inStream);460mRotationConstraintPart.RestoreState(inStream);461mPositionLimitsConstraintPart.RestoreState(inStream);462463inStream.Read(mMotorState);464inStream.Read(mTargetVelocity);465inStream.Read(mTargetPosition);466}467468Ref<ConstraintSettings> SliderConstraint::GetConstraintSettings() const469{470SliderConstraintSettings *settings = new SliderConstraintSettings;471ToConstraintSettings(*settings);472settings->mSpace = EConstraintSpace::LocalToBodyCOM;473settings->mPoint1 = RVec3(mLocalSpacePosition1);474settings->mSliderAxis1 = mLocalSpaceSliderAxis1;475settings->mNormalAxis1 = mLocalSpaceNormal1;476settings->mPoint2 = RVec3(mLocalSpacePosition2);477Mat44 inv_initial_rotation = Mat44::sRotation(mInvInitialOrientation);478settings->mSliderAxis2 = inv_initial_rotation.Multiply3x3(mLocalSpaceSliderAxis1);479settings->mNormalAxis2 = inv_initial_rotation.Multiply3x3(mLocalSpaceNormal1);480settings->mLimitsMin = mLimitsMin;481settings->mLimitsMax = mLimitsMax;482settings->mLimitsSpringSettings = mLimitsSpringSettings;483settings->mMaxFrictionForce = mMaxFrictionForce;484settings->mMotorSettings = mMotorSettings;485return settings;486}487488Mat44 SliderConstraint::GetConstraintToBody1Matrix() const489{490return Mat44(Vec4(mLocalSpaceSliderAxis1, 0), Vec4(mLocalSpaceNormal1, 0), Vec4(mLocalSpaceNormal2, 0), Vec4(mLocalSpacePosition1, 1));491}492493Mat44 SliderConstraint::GetConstraintToBody2Matrix() const494{495Mat44 mat = Mat44::sRotation(mInvInitialOrientation).Multiply3x3(Mat44(Vec4(mLocalSpaceSliderAxis1, 0), Vec4(mLocalSpaceNormal1, 0), Vec4(mLocalSpaceNormal2, 0), Vec4(0, 0, 0, 1)));496mat.SetTranslation(mLocalSpacePosition2);497return mat;498}499500JPH_NAMESPACE_END501502503