Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/DistanceConstraint.cpp
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Constraints/DistanceConstraint.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(DistanceConstraintSettings)
21
{
22
JPH_ADD_BASE_CLASS(DistanceConstraintSettings, TwoBodyConstraintSettings)
23
24
JPH_ADD_ENUM_ATTRIBUTE(DistanceConstraintSettings, mSpace)
25
JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint1)
26
JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint2)
27
JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMinDistance)
28
JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMaxDistance)
29
JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode")
30
JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library
31
JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping")
32
}
33
34
void DistanceConstraintSettings::SaveBinaryState(StreamOut &inStream) const
35
{
36
ConstraintSettings::SaveBinaryState(inStream);
37
38
inStream.Write(mSpace);
39
inStream.Write(mPoint1);
40
inStream.Write(mPoint2);
41
inStream.Write(mMinDistance);
42
inStream.Write(mMaxDistance);
43
mLimitsSpringSettings.SaveBinaryState(inStream);
44
}
45
46
void DistanceConstraintSettings::RestoreBinaryState(StreamIn &inStream)
47
{
48
ConstraintSettings::RestoreBinaryState(inStream);
49
50
inStream.Read(mSpace);
51
inStream.Read(mPoint1);
52
inStream.Read(mPoint2);
53
inStream.Read(mMinDistance);
54
inStream.Read(mMaxDistance);
55
mLimitsSpringSettings.RestoreBinaryState(inStream);
56
}
57
58
TwoBodyConstraint *DistanceConstraintSettings::Create(Body &inBody1, Body &inBody2) const
59
{
60
return new DistanceConstraint(inBody1, inBody2, *this);
61
}
62
63
DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings) :
64
TwoBodyConstraint(inBody1, inBody2, inSettings),
65
mMinDistance(inSettings.mMinDistance),
66
mMaxDistance(inSettings.mMaxDistance)
67
{
68
if (inSettings.mSpace == EConstraintSpace::WorldSpace)
69
{
70
// If all properties were specified in world space, take them to local space now
71
mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1);
72
mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2);
73
mWorldSpacePosition1 = inSettings.mPoint1;
74
mWorldSpacePosition2 = inSettings.mPoint2;
75
}
76
else
77
{
78
// If properties were specified in local space, we need to calculate world space positions
79
mLocalSpacePosition1 = Vec3(inSettings.mPoint1);
80
mLocalSpacePosition2 = Vec3(inSettings.mPoint2);
81
mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mPoint1;
82
mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mPoint2;
83
}
84
85
// Store distance we want to keep between the world space points
86
float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Length();
87
float min_distance, max_distance;
88
if (mMinDistance < 0.0f && mMaxDistance < 0.0f)
89
{
90
min_distance = max_distance = distance;
91
}
92
else
93
{
94
min_distance = mMinDistance < 0.0f? min(distance, mMaxDistance) : mMinDistance;
95
max_distance = mMaxDistance < 0.0f? max(distance, mMinDistance) : mMaxDistance;
96
}
97
SetDistance(min_distance, max_distance);
98
99
// Most likely gravity is going to tear us apart (this is only used when the distance between the points = 0)
100
mWorldSpaceNormal = Vec3::sAxisY();
101
102
// Store spring settings
103
SetLimitsSpringSettings(inSettings.mLimitsSpringSettings);
104
}
105
106
void DistanceConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
107
{
108
if (mBody1->GetID() == inBodyID)
109
mLocalSpacePosition1 -= inDeltaCOM;
110
else if (mBody2->GetID() == inBodyID)
111
mLocalSpacePosition2 -= inDeltaCOM;
112
}
113
114
void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime)
115
{
116
// Update world space positions (the bodies may have moved)
117
mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
118
mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;
119
120
// Calculate world space normal
121
Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1);
122
float delta_len = delta.Length();
123
if (delta_len > 0.0f)
124
mWorldSpaceNormal = delta / delta_len;
125
126
// Calculate points relative to body
127
// r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1
128
Vec3 r1_plus_u = Vec3(mWorldSpacePosition2 - mBody1->GetCenterOfMassPosition());
129
Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition());
130
131
if (mMinDistance == mMaxDistance)
132
{
133
mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings);
134
135
// Single distance, allow constraint forces in both directions
136
mMinLambda = -FLT_MAX;
137
mMaxLambda = FLT_MAX;
138
}
139
else if (delta_len <= mMinDistance)
140
{
141
mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings);
142
143
// Allow constraint forces to make distance bigger only
144
mMinLambda = 0;
145
mMaxLambda = FLT_MAX;
146
}
147
else if (delta_len >= mMaxDistance)
148
{
149
mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMaxDistance, mLimitsSpringSettings);
150
151
// Allow constraint forces to make distance smaller only
152
mMinLambda = -FLT_MAX;
153
mMaxLambda = 0;
154
}
155
else
156
mAxisConstraint.Deactivate();
157
}
158
159
void DistanceConstraint::SetupVelocityConstraint(float inDeltaTime)
160
{
161
CalculateConstraintProperties(inDeltaTime);
162
}
163
164
void DistanceConstraint::ResetWarmStart()
165
{
166
mAxisConstraint.Deactivate();
167
}
168
169
void DistanceConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
170
{
171
mAxisConstraint.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal, inWarmStartImpulseRatio);
172
}
173
174
bool DistanceConstraint::SolveVelocityConstraint(float inDeltaTime)
175
{
176
if (mAxisConstraint.IsActive())
177
return mAxisConstraint.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal, mMinLambda, mMaxLambda);
178
else
179
return false;
180
}
181
182
bool DistanceConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
183
{
184
if (mLimitsSpringSettings.mFrequency <= 0.0f) // When the spring is active, we don't need to solve the position constraint
185
{
186
float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Dot(mWorldSpaceNormal);
187
188
// Calculate position error
189
float position_error = 0.0f;
190
if (distance < mMinDistance)
191
position_error = distance - mMinDistance;
192
else if (distance > mMaxDistance)
193
position_error = distance - mMaxDistance;
194
195
if (position_error != 0.0f)
196
{
197
// Update constraint properties (bodies may have moved)
198
CalculateConstraintProperties(inDeltaTime);
199
200
return mAxisConstraint.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal, position_error, inBaumgarte);
201
}
202
}
203
204
return false;
205
}
206
207
#ifdef JPH_DEBUG_RENDERER
208
void DistanceConstraint::DrawConstraint(DebugRenderer *inRenderer) const
209
{
210
// Draw constraint
211
Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1);
212
float len = delta.Length();
213
if (len < mMinDistance)
214
{
215
RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMinDistance / len : Vec3(0, len, 0));
216
inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);
217
inRenderer->DrawLine(mWorldSpacePosition2, real_end_pos, Color::sYellow);
218
}
219
else if (len > mMaxDistance)
220
{
221
RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMaxDistance / len : Vec3(0, len, 0));
222
inRenderer->DrawLine(mWorldSpacePosition1, real_end_pos, Color::sGreen);
223
inRenderer->DrawLine(real_end_pos, mWorldSpacePosition2, Color::sRed);
224
}
225
else
226
inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);
227
228
// Draw constraint end points
229
inRenderer->DrawMarker(mWorldSpacePosition1, Color::sWhite, 0.1f);
230
inRenderer->DrawMarker(mWorldSpacePosition2, Color::sWhite, 0.1f);
231
232
// Draw current length
233
inRenderer->DrawText3D(0.5_r * (mWorldSpacePosition1 + mWorldSpacePosition2), StringFormat("%.2f", (double)len));
234
}
235
#endif // JPH_DEBUG_RENDERER
236
237
void DistanceConstraint::SaveState(StateRecorder &inStream) const
238
{
239
TwoBodyConstraint::SaveState(inStream);
240
241
mAxisConstraint.SaveState(inStream);
242
inStream.Write(mWorldSpaceNormal); // When distance = 0, the normal is used from last frame so we need to store it
243
}
244
245
void DistanceConstraint::RestoreState(StateRecorder &inStream)
246
{
247
TwoBodyConstraint::RestoreState(inStream);
248
249
mAxisConstraint.RestoreState(inStream);
250
inStream.Read(mWorldSpaceNormal);
251
}
252
253
Ref<ConstraintSettings> DistanceConstraint::GetConstraintSettings() const
254
{
255
DistanceConstraintSettings *settings = new DistanceConstraintSettings;
256
ToConstraintSettings(*settings);
257
settings->mSpace = EConstraintSpace::LocalToBodyCOM;
258
settings->mPoint1 = RVec3(mLocalSpacePosition1);
259
settings->mPoint2 = RVec3(mLocalSpacePosition2);
260
settings->mMinDistance = mMinDistance;
261
settings->mMaxDistance = mMaxDistance;
262
settings->mLimitsSpringSettings = mLimitsSpringSettings;
263
return settings;
264
}
265
266
JPH_NAMESPACE_END
267
268