Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/PulleyConstraint.cpp
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Constraints/PulleyConstraint.h>
8
#include <Jolt/Physics/Body/Body.h>
9
#include <Jolt/ObjectStream/TypeDeclarations.h>
10
#include <Jolt/Core/StreamIn.h>
11
#include <Jolt/Core/StreamOut.h>
12
#ifdef JPH_DEBUG_RENDERER
13
#include <Jolt/Renderer/DebugRenderer.h>
14
#endif // JPH_DEBUG_RENDERER
15
16
JPH_NAMESPACE_BEGIN
17
18
using namespace literals;
19
20
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PulleyConstraintSettings)
21
{
22
JPH_ADD_BASE_CLASS(PulleyConstraintSettings, TwoBodyConstraintSettings)
23
24
JPH_ADD_ENUM_ATTRIBUTE(PulleyConstraintSettings, mSpace)
25
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint1)
26
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint1)
27
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint2)
28
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint2)
29
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mRatio)
30
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMinLength)
31
JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMaxLength)
32
}
33
34
void PulleyConstraintSettings::SaveBinaryState(StreamOut &inStream) const
35
{
36
ConstraintSettings::SaveBinaryState(inStream);
37
38
inStream.Write(mSpace);
39
inStream.Write(mBodyPoint1);
40
inStream.Write(mFixedPoint1);
41
inStream.Write(mBodyPoint2);
42
inStream.Write(mFixedPoint2);
43
inStream.Write(mRatio);
44
inStream.Write(mMinLength);
45
inStream.Write(mMaxLength);
46
}
47
48
void PulleyConstraintSettings::RestoreBinaryState(StreamIn &inStream)
49
{
50
ConstraintSettings::RestoreBinaryState(inStream);
51
52
inStream.Read(mSpace);
53
inStream.Read(mBodyPoint1);
54
inStream.Read(mFixedPoint1);
55
inStream.Read(mBodyPoint2);
56
inStream.Read(mFixedPoint2);
57
inStream.Read(mRatio);
58
inStream.Read(mMinLength);
59
inStream.Read(mMaxLength);
60
}
61
62
TwoBodyConstraint *PulleyConstraintSettings::Create(Body &inBody1, Body &inBody2) const
63
{
64
return new PulleyConstraint(inBody1, inBody2, *this);
65
}
66
67
PulleyConstraint::PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings) :
68
TwoBodyConstraint(inBody1, inBody2, inSettings),
69
mFixedPosition1(inSettings.mFixedPoint1),
70
mFixedPosition2(inSettings.mFixedPoint2),
71
mRatio(inSettings.mRatio),
72
mMinLength(inSettings.mMinLength),
73
mMaxLength(inSettings.mMaxLength)
74
{
75
if (inSettings.mSpace == EConstraintSpace::WorldSpace)
76
{
77
// If all properties were specified in world space, take them to local space now
78
mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint1);
79
mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint2);
80
mWorldSpacePosition1 = inSettings.mBodyPoint1;
81
mWorldSpacePosition2 = inSettings.mBodyPoint2;
82
}
83
else
84
{
85
// If properties were specified in local space, we need to calculate world space positions
86
mLocalSpacePosition1 = Vec3(inSettings.mBodyPoint1);
87
mLocalSpacePosition2 = Vec3(inSettings.mBodyPoint2);
88
mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mBodyPoint1;
89
mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mBodyPoint2;
90
}
91
92
// Calculate min/max length if it was not provided
93
float current_length = GetCurrentLength();
94
if (mMinLength < 0.0f)
95
mMinLength = current_length;
96
if (mMaxLength < 0.0f)
97
mMaxLength = current_length;
98
99
// 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)
100
mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY();
101
}
102
103
void PulleyConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
104
{
105
if (mBody1->GetID() == inBodyID)
106
mLocalSpacePosition1 -= inDeltaCOM;
107
else if (mBody2->GetID() == inBodyID)
108
mLocalSpacePosition2 -= inDeltaCOM;
109
}
110
111
float PulleyConstraint::CalculatePositionsNormalsAndLength()
112
{
113
// Update world space positions (the bodies may have moved)
114
mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
115
mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;
116
117
// Calculate world space normals
118
Vec3 delta1 = Vec3(mWorldSpacePosition1 - mFixedPosition1);
119
float delta1_len = delta1.Length();
120
if (delta1_len > 0.0f)
121
mWorldSpaceNormal1 = delta1 / delta1_len;
122
123
Vec3 delta2 = Vec3(mWorldSpacePosition2 - mFixedPosition2);
124
float delta2_len = delta2.Length();
125
if (delta2_len > 0.0f)
126
mWorldSpaceNormal2 = delta2 / delta2_len;
127
128
// Calculate length
129
return delta1_len + mRatio * delta2_len;
130
}
131
132
void PulleyConstraint::CalculateConstraintProperties()
133
{
134
// Calculate attachment points relative to COM
135
Vec3 r1 = Vec3(mWorldSpacePosition1 - mBody1->GetCenterOfMassPosition());
136
Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition());
137
138
mIndependentAxisConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, r1, mWorldSpaceNormal1, r2, mWorldSpaceNormal2, mRatio);
139
}
140
141
void PulleyConstraint::SetupVelocityConstraint(float inDeltaTime)
142
{
143
// Determine if the constraint is active
144
float current_length = CalculatePositionsNormalsAndLength();
145
bool min_length_violation = current_length <= mMinLength;
146
bool max_length_violation = current_length >= mMaxLength;
147
if (min_length_violation || max_length_violation)
148
{
149
// Determine max lambda based on if the length is too big or small
150
mMinLambda = max_length_violation? -FLT_MAX : 0.0f;
151
mMaxLambda = min_length_violation? FLT_MAX : 0.0f;
152
153
CalculateConstraintProperties();
154
}
155
else
156
mIndependentAxisConstraintPart.Deactivate();
157
}
158
159
void PulleyConstraint::ResetWarmStart()
160
{
161
mIndependentAxisConstraintPart.Deactivate();
162
}
163
164
void PulleyConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
165
{
166
mIndependentAxisConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, inWarmStartImpulseRatio);
167
}
168
169
bool PulleyConstraint::SolveVelocityConstraint(float inDeltaTime)
170
{
171
if (mIndependentAxisConstraintPart.IsActive())
172
return mIndependentAxisConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, mMinLambda, mMaxLambda);
173
else
174
return false;
175
}
176
177
bool PulleyConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
178
{
179
// Calculate new length (bodies may have changed)
180
float current_length = CalculatePositionsNormalsAndLength();
181
182
float position_error = 0.0f;
183
if (current_length < mMinLength)
184
position_error = current_length - mMinLength;
185
else if (current_length > mMaxLength)
186
position_error = current_length - mMaxLength;
187
188
if (position_error != 0.0f)
189
{
190
// Update constraint properties (bodies may have moved)
191
CalculateConstraintProperties();
192
193
return mIndependentAxisConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, position_error, inBaumgarte);
194
}
195
196
return false;
197
}
198
199
#ifdef JPH_DEBUG_RENDERER
200
void PulleyConstraint::DrawConstraint(DebugRenderer *inRenderer) const
201
{
202
// Color according to length vs min/max length
203
float current_length = GetCurrentLength();
204
Color color = Color::sGreen;
205
if (current_length < mMinLength)
206
color = Color::sYellow;
207
else if (current_length > mMaxLength)
208
color = Color::sRed;
209
210
// Draw constraint
211
inRenderer->DrawLine(mWorldSpacePosition1, mFixedPosition1, color);
212
inRenderer->DrawLine(mFixedPosition1, mFixedPosition2, color);
213
inRenderer->DrawLine(mFixedPosition2, mWorldSpacePosition2, color);
214
215
// Draw current length
216
inRenderer->DrawText3D(0.5_r * (mFixedPosition1 + mFixedPosition2), StringFormat("%.2f", (double)current_length));
217
}
218
#endif // JPH_DEBUG_RENDERER
219
220
void PulleyConstraint::SaveState(StateRecorder &inStream) const
221
{
222
TwoBodyConstraint::SaveState(inStream);
223
224
mIndependentAxisConstraintPart.SaveState(inStream);
225
inStream.Write(mWorldSpaceNormal1); // When distance to fixed point = 0, the normal is used from last frame so we need to store it
226
inStream.Write(mWorldSpaceNormal2);
227
}
228
229
void PulleyConstraint::RestoreState(StateRecorder &inStream)
230
{
231
TwoBodyConstraint::RestoreState(inStream);
232
233
mIndependentAxisConstraintPart.RestoreState(inStream);
234
inStream.Read(mWorldSpaceNormal1);
235
inStream.Read(mWorldSpaceNormal2);
236
}
237
238
Ref<ConstraintSettings> PulleyConstraint::GetConstraintSettings() const
239
{
240
PulleyConstraintSettings *settings = new PulleyConstraintSettings;
241
ToConstraintSettings(*settings);
242
settings->mSpace = EConstraintSpace::LocalToBodyCOM;
243
settings->mBodyPoint1 = RVec3(mLocalSpacePosition1);
244
settings->mFixedPoint1 = mFixedPosition1;
245
settings->mBodyPoint2 = RVec3(mLocalSpacePosition2);
246
settings->mFixedPoint2 = mFixedPosition2;
247
settings->mRatio = mRatio;
248
settings->mMinLength = mMinLength;
249
settings->mMaxLength = mMaxLength;
250
return settings;
251
}
252
253
JPH_NAMESPACE_END
254
255