Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/PulleyConstraint.cpp
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2022 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Constraints/PulleyConstraint.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(PulleyConstraintSettings)20{21JPH_ADD_BASE_CLASS(PulleyConstraintSettings, TwoBodyConstraintSettings)2223JPH_ADD_ENUM_ATTRIBUTE(PulleyConstraintSettings, mSpace)24JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint1)25JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint1)26JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint2)27JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint2)28JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mRatio)29JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMinLength)30JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMaxLength)31}3233void PulleyConstraintSettings::SaveBinaryState(StreamOut &inStream) const34{35ConstraintSettings::SaveBinaryState(inStream);3637inStream.Write(mSpace);38inStream.Write(mBodyPoint1);39inStream.Write(mFixedPoint1);40inStream.Write(mBodyPoint2);41inStream.Write(mFixedPoint2);42inStream.Write(mRatio);43inStream.Write(mMinLength);44inStream.Write(mMaxLength);45}4647void PulleyConstraintSettings::RestoreBinaryState(StreamIn &inStream)48{49ConstraintSettings::RestoreBinaryState(inStream);5051inStream.Read(mSpace);52inStream.Read(mBodyPoint1);53inStream.Read(mFixedPoint1);54inStream.Read(mBodyPoint2);55inStream.Read(mFixedPoint2);56inStream.Read(mRatio);57inStream.Read(mMinLength);58inStream.Read(mMaxLength);59}6061TwoBodyConstraint *PulleyConstraintSettings::Create(Body &inBody1, Body &inBody2) const62{63return new PulleyConstraint(inBody1, inBody2, *this);64}6566PulleyConstraint::PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings) :67TwoBodyConstraint(inBody1, inBody2, inSettings),68mFixedPosition1(inSettings.mFixedPoint1),69mFixedPosition2(inSettings.mFixedPoint2),70mRatio(inSettings.mRatio),71mMinLength(inSettings.mMinLength),72mMaxLength(inSettings.mMaxLength)73{74if (inSettings.mSpace == EConstraintSpace::WorldSpace)75{76// If all properties were specified in world space, take them to local space now77mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint1);78mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint2);79mWorldSpacePosition1 = inSettings.mBodyPoint1;80mWorldSpacePosition2 = inSettings.mBodyPoint2;81}82else83{84// If properties were specified in local space, we need to calculate world space positions85mLocalSpacePosition1 = Vec3(inSettings.mBodyPoint1);86mLocalSpacePosition2 = Vec3(inSettings.mBodyPoint2);87mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mBodyPoint1;88mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mBodyPoint2;89}9091// Calculate min/max length if it was not provided92float current_length = GetCurrentLength();93if (mMinLength < 0.0f)94mMinLength = current_length;95if (mMaxLength < 0.0f)96mMaxLength = current_length;9798// Initialize the normals to a likely valid axis in case the fixed points overlap with the attachment points (most likely the fixed points are above both bodies)99mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY();100}101102void PulleyConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)103{104if (mBody1->GetID() == inBodyID)105mLocalSpacePosition1 -= inDeltaCOM;106else if (mBody2->GetID() == inBodyID)107mLocalSpacePosition2 -= inDeltaCOM;108}109110float PulleyConstraint::CalculatePositionsNormalsAndLength()111{112// Update world space positions (the bodies may have moved)113mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;114mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;115116// Calculate world space normals117Vec3 delta1 = Vec3(mWorldSpacePosition1 - mFixedPosition1);118float delta1_len = delta1.Length();119if (delta1_len > 0.0f)120mWorldSpaceNormal1 = delta1 / delta1_len;121122Vec3 delta2 = Vec3(mWorldSpacePosition2 - mFixedPosition2);123float delta2_len = delta2.Length();124if (delta2_len > 0.0f)125mWorldSpaceNormal2 = delta2 / delta2_len;126127// Calculate length128return delta1_len + mRatio * delta2_len;129}130131void PulleyConstraint::CalculateConstraintProperties()132{133// Calculate attachment points relative to COM134Vec3 r1 = Vec3(mWorldSpacePosition1 - mBody1->GetCenterOfMassPosition());135Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition());136137mIndependentAxisConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, r1, mWorldSpaceNormal1, r2, mWorldSpaceNormal2, mRatio);138}139140void PulleyConstraint::SetupVelocityConstraint(float inDeltaTime)141{142// Determine if the constraint is active143float current_length = CalculatePositionsNormalsAndLength();144bool min_length_violation = current_length <= mMinLength;145bool max_length_violation = current_length >= mMaxLength;146if (min_length_violation || max_length_violation)147{148// Determine max lambda based on if the length is too big or small149mMinLambda = max_length_violation? -FLT_MAX : 0.0f;150mMaxLambda = min_length_violation? FLT_MAX : 0.0f;151152CalculateConstraintProperties();153}154else155mIndependentAxisConstraintPart.Deactivate();156}157158void PulleyConstraint::ResetWarmStart()159{160mIndependentAxisConstraintPart.Deactivate();161}162163void PulleyConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)164{165mIndependentAxisConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, inWarmStartImpulseRatio);166}167168bool PulleyConstraint::SolveVelocityConstraint(float inDeltaTime)169{170if (mIndependentAxisConstraintPart.IsActive())171return mIndependentAxisConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, mMinLambda, mMaxLambda);172else173return false;174}175176bool PulleyConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)177{178// Calculate new length (bodies may have changed)179float current_length = CalculatePositionsNormalsAndLength();180181float position_error = 0.0f;182if (current_length < mMinLength)183position_error = current_length - mMinLength;184else if (current_length > mMaxLength)185position_error = current_length - mMaxLength;186187if (position_error != 0.0f)188{189// Update constraint properties (bodies may have moved)190CalculateConstraintProperties();191192return mIndependentAxisConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, position_error, inBaumgarte);193}194195return false;196}197198#ifdef JPH_DEBUG_RENDERER199void PulleyConstraint::DrawConstraint(DebugRenderer *inRenderer) const200{201// Color according to length vs min/max length202float current_length = GetCurrentLength();203Color color = Color::sGreen;204if (current_length < mMinLength)205color = Color::sYellow;206else if (current_length > mMaxLength)207color = Color::sRed;208209// Draw constraint210inRenderer->DrawLine(mWorldSpacePosition1, mFixedPosition1, color);211inRenderer->DrawLine(mFixedPosition1, mFixedPosition2, color);212inRenderer->DrawLine(mFixedPosition2, mWorldSpacePosition2, color);213214// Draw current length215inRenderer->DrawText3D(0.5_r * (mFixedPosition1 + mFixedPosition2), StringFormat("%.2f", (double)current_length));216}217#endif // JPH_DEBUG_RENDERER218219void PulleyConstraint::SaveState(StateRecorder &inStream) const220{221TwoBodyConstraint::SaveState(inStream);222223mIndependentAxisConstraintPart.SaveState(inStream);224inStream.Write(mWorldSpaceNormal1); // When distance to fixed point = 0, the normal is used from last frame so we need to store it225inStream.Write(mWorldSpaceNormal2);226}227228void PulleyConstraint::RestoreState(StateRecorder &inStream)229{230TwoBodyConstraint::RestoreState(inStream);231232mIndependentAxisConstraintPart.RestoreState(inStream);233inStream.Read(mWorldSpaceNormal1);234inStream.Read(mWorldSpaceNormal2);235}236237Ref<ConstraintSettings> PulleyConstraint::GetConstraintSettings() const238{239PulleyConstraintSettings *settings = new PulleyConstraintSettings;240ToConstraintSettings(*settings);241settings->mSpace = EConstraintSpace::LocalToBodyCOM;242settings->mBodyPoint1 = RVec3(mLocalSpacePosition1);243settings->mFixedPoint1 = mFixedPosition1;244settings->mBodyPoint2 = RVec3(mLocalSpacePosition2);245settings->mFixedPoint2 = mFixedPosition2;246settings->mRatio = mRatio;247settings->mMinLength = mMinLength;248settings->mMaxLength = mMaxLength;249return settings;250}251252JPH_NAMESPACE_END253254255