Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/DistanceConstraint.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/DistanceConstraint.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(DistanceConstraintSettings)20{21JPH_ADD_BASE_CLASS(DistanceConstraintSettings, TwoBodyConstraintSettings)2223JPH_ADD_ENUM_ATTRIBUTE(DistanceConstraintSettings, mSpace)24JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint1)25JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint2)26JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMinDistance)27JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMaxDistance)28JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode")29JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library30JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping")31}3233void DistanceConstraintSettings::SaveBinaryState(StreamOut &inStream) const34{35ConstraintSettings::SaveBinaryState(inStream);3637inStream.Write(mSpace);38inStream.Write(mPoint1);39inStream.Write(mPoint2);40inStream.Write(mMinDistance);41inStream.Write(mMaxDistance);42mLimitsSpringSettings.SaveBinaryState(inStream);43}4445void DistanceConstraintSettings::RestoreBinaryState(StreamIn &inStream)46{47ConstraintSettings::RestoreBinaryState(inStream);4849inStream.Read(mSpace);50inStream.Read(mPoint1);51inStream.Read(mPoint2);52inStream.Read(mMinDistance);53inStream.Read(mMaxDistance);54mLimitsSpringSettings.RestoreBinaryState(inStream);55}5657TwoBodyConstraint *DistanceConstraintSettings::Create(Body &inBody1, Body &inBody2) const58{59return new DistanceConstraint(inBody1, inBody2, *this);60}6162DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings) :63TwoBodyConstraint(inBody1, inBody2, inSettings),64mMinDistance(inSettings.mMinDistance),65mMaxDistance(inSettings.mMaxDistance)66{67if (inSettings.mSpace == EConstraintSpace::WorldSpace)68{69// If all properties were specified in world space, take them to local space now70mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1);71mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2);72mWorldSpacePosition1 = inSettings.mPoint1;73mWorldSpacePosition2 = inSettings.mPoint2;74}75else76{77// If properties were specified in local space, we need to calculate world space positions78mLocalSpacePosition1 = Vec3(inSettings.mPoint1);79mLocalSpacePosition2 = Vec3(inSettings.mPoint2);80mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mPoint1;81mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mPoint2;82}8384// Store distance we want to keep between the world space points85float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Length();86float min_distance, max_distance;87if (mMinDistance < 0.0f && mMaxDistance < 0.0f)88{89min_distance = max_distance = distance;90}91else92{93min_distance = mMinDistance < 0.0f? min(distance, mMaxDistance) : mMinDistance;94max_distance = mMaxDistance < 0.0f? max(distance, mMinDistance) : mMaxDistance;95}96SetDistance(min_distance, max_distance);9798// Most likely gravity is going to tear us apart (this is only used when the distance between the points = 0)99mWorldSpaceNormal = Vec3::sAxisY();100101// Store spring settings102SetLimitsSpringSettings(inSettings.mLimitsSpringSettings);103}104105void DistanceConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)106{107if (mBody1->GetID() == inBodyID)108mLocalSpacePosition1 -= inDeltaCOM;109else if (mBody2->GetID() == inBodyID)110mLocalSpacePosition2 -= inDeltaCOM;111}112113void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime)114{115// Update world space positions (the bodies may have moved)116mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;117mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;118119// Calculate world space normal120Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1);121float delta_len = delta.Length();122if (delta_len > 0.0f)123mWorldSpaceNormal = delta / delta_len;124125// Calculate points relative to body126// r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1127Vec3 r1_plus_u = Vec3(mWorldSpacePosition2 - mBody1->GetCenterOfMassPosition());128Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition());129130if (mMinDistance == mMaxDistance)131{132mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings);133134// Single distance, allow constraint forces in both directions135mMinLambda = -FLT_MAX;136mMaxLambda = FLT_MAX;137}138else if (delta_len <= mMinDistance)139{140mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings);141142// Allow constraint forces to make distance bigger only143mMinLambda = 0;144mMaxLambda = FLT_MAX;145}146else if (delta_len >= mMaxDistance)147{148mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMaxDistance, mLimitsSpringSettings);149150// Allow constraint forces to make distance smaller only151mMinLambda = -FLT_MAX;152mMaxLambda = 0;153}154else155mAxisConstraint.Deactivate();156}157158void DistanceConstraint::SetupVelocityConstraint(float inDeltaTime)159{160CalculateConstraintProperties(inDeltaTime);161}162163void DistanceConstraint::ResetWarmStart()164{165mAxisConstraint.Deactivate();166}167168void DistanceConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)169{170mAxisConstraint.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal, inWarmStartImpulseRatio);171}172173bool DistanceConstraint::SolveVelocityConstraint(float inDeltaTime)174{175if (mAxisConstraint.IsActive())176return mAxisConstraint.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal, mMinLambda, mMaxLambda);177else178return false;179}180181bool DistanceConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)182{183if (mLimitsSpringSettings.mFrequency <= 0.0f) // When the spring is active, we don't need to solve the position constraint184{185float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Dot(mWorldSpaceNormal);186187// Calculate position error188float position_error = 0.0f;189if (distance < mMinDistance)190position_error = distance - mMinDistance;191else if (distance > mMaxDistance)192position_error = distance - mMaxDistance;193194if (position_error != 0.0f)195{196// Update constraint properties (bodies may have moved)197CalculateConstraintProperties(inDeltaTime);198199return mAxisConstraint.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal, position_error, inBaumgarte);200}201}202203return false;204}205206#ifdef JPH_DEBUG_RENDERER207void DistanceConstraint::DrawConstraint(DebugRenderer *inRenderer) const208{209// Draw constraint210Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1);211float len = delta.Length();212if (len < mMinDistance)213{214RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMinDistance / len : Vec3(0, len, 0));215inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);216inRenderer->DrawLine(mWorldSpacePosition2, real_end_pos, Color::sYellow);217}218else if (len > mMaxDistance)219{220RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMaxDistance / len : Vec3(0, len, 0));221inRenderer->DrawLine(mWorldSpacePosition1, real_end_pos, Color::sGreen);222inRenderer->DrawLine(real_end_pos, mWorldSpacePosition2, Color::sRed);223}224else225inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);226227// Draw constraint end points228inRenderer->DrawMarker(mWorldSpacePosition1, Color::sWhite, 0.1f);229inRenderer->DrawMarker(mWorldSpacePosition2, Color::sWhite, 0.1f);230231// Draw current length232inRenderer->DrawText3D(0.5_r * (mWorldSpacePosition1 + mWorldSpacePosition2), StringFormat("%.2f", (double)len));233}234#endif // JPH_DEBUG_RENDERER235236void DistanceConstraint::SaveState(StateRecorder &inStream) const237{238TwoBodyConstraint::SaveState(inStream);239240mAxisConstraint.SaveState(inStream);241inStream.Write(mWorldSpaceNormal); // When distance = 0, the normal is used from last frame so we need to store it242}243244void DistanceConstraint::RestoreState(StateRecorder &inStream)245{246TwoBodyConstraint::RestoreState(inStream);247248mAxisConstraint.RestoreState(inStream);249inStream.Read(mWorldSpaceNormal);250}251252Ref<ConstraintSettings> DistanceConstraint::GetConstraintSettings() const253{254DistanceConstraintSettings *settings = new DistanceConstraintSettings;255ToConstraintSettings(*settings);256settings->mSpace = EConstraintSpace::LocalToBodyCOM;257settings->mPoint1 = RVec3(mLocalSpacePosition1);258settings->mPoint2 = RVec3(mLocalSpacePosition2);259settings->mMinDistance = mMinDistance;260settings->mMaxDistance = mMaxDistance;261settings->mLimitsSpringSettings = mLimitsSpringSettings;262return settings;263}264265JPH_NAMESPACE_END266267268