Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
#include <Jolt/Physics/Body/Body.h>
8
#include <Jolt/Physics/Constraints/ConstraintPart/SpringPart.h>
9
#include <Jolt/Physics/Constraints/SpringSettings.h>
10
#include <Jolt/Physics/StateRecorder.h>
11
#include <Jolt/Physics/DeterminismLog.h>
12
13
JPH_NAMESPACE_BEGIN
14
15
/// Constraint that constrains motion along 1 axis
16
///
17
/// @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.1.1
18
/// (we're not using the approximation of eq 27 but instead add the U term as in eq 55)
19
///
20
/// Constraint equation (eq 25):
21
///
22
/// \f[C = (p_2 - p_1) \cdot n\f]
23
///
24
/// Jacobian (eq 28):
25
///
26
/// \f[J = \begin{bmatrix} -n^T & (-(r_1 + u) \times n)^T & n^T & (r_2 \times n)^T \end{bmatrix}\f]
27
///
28
/// Used terms (here and below, everything in world space):\n
29
/// n = constraint axis (normalized).\n
30
/// p1, p2 = constraint points.\n
31
/// r1 = p1 - x1.\n
32
/// r2 = p2 - x2.\n
33
/// u = x2 + r2 - x1 - r1 = p2 - p1.\n
34
/// x1, x2 = center of mass for the bodies.\n
35
/// v = [v1, w1, v2, w2].\n
36
/// v1, v2 = linear velocity of body 1 and 2.\n
37
/// w1, w2 = angular velocity of body 1 and 2.\n
38
/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n
39
/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n
40
/// b = velocity bias.\n
41
/// \f$\beta\f$ = baumgarte constant.
42
class AxisConstraintPart
43
{
44
/// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated
45
template <EMotionType Type1, EMotionType Type2>
46
JPH_INLINE bool ApplyVelocityStep(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inLambda) const
47
{
48
// Apply impulse if delta is not zero
49
if (inLambda != 0.0f)
50
{
51
// Calculate velocity change due to constraint
52
//
53
// Impulse:
54
// P = J^T lambda
55
//
56
// Euler velocity integration:
57
// v' = v + M^-1 P
58
if constexpr (Type1 == EMotionType::Dynamic)
59
{
60
ioMotionProperties1->SubLinearVelocityStep((inLambda * inInvMass1) * inWorldSpaceAxis);
61
ioMotionProperties1->SubAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
62
}
63
if constexpr (Type2 == EMotionType::Dynamic)
64
{
65
ioMotionProperties2->AddLinearVelocityStep((inLambda * inInvMass2) * inWorldSpaceAxis);
66
ioMotionProperties2->AddAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
67
}
68
return true;
69
}
70
71
return false;
72
}
73
74
/// Internal helper function to calculate the inverse effective mass
75
template <EMotionType Type1, EMotionType Type2>
76
JPH_INLINE float TemplatedCalculateInverseEffectiveMass(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
77
{
78
JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-5f));
79
80
// Calculate properties used below
81
Vec3 r1_plus_u_x_axis;
82
if constexpr (Type1 != EMotionType::Static)
83
{
84
r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis);
85
r1_plus_u_x_axis.StoreFloat3(&mR1PlusUxAxis);
86
}
87
else
88
{
89
#ifdef JPH_DEBUG
90
Vec3::sNaN().StoreFloat3(&mR1PlusUxAxis);
91
#endif
92
}
93
94
Vec3 r2_x_axis;
95
if constexpr (Type2 != EMotionType::Static)
96
{
97
r2_x_axis = inR2.Cross(inWorldSpaceAxis);
98
r2_x_axis.StoreFloat3(&mR2xAxis);
99
}
100
else
101
{
102
#ifdef JPH_DEBUG
103
Vec3::sNaN().StoreFloat3(&mR2xAxis);
104
#endif
105
}
106
107
// Calculate inverse effective mass: K = J M^-1 J^T
108
float inv_effective_mass;
109
110
if constexpr (Type1 == EMotionType::Dynamic)
111
{
112
Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis);
113
invi1_r1_plus_u_x_axis.StoreFloat3(&mInvI1_R1PlusUxAxis);
114
inv_effective_mass = inInvMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis);
115
}
116
else
117
{
118
(void)r1_plus_u_x_axis; // Fix compiler warning: Not using this (it's not calculated either)
119
JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI1_R1PlusUxAxis);)
120
inv_effective_mass = 0.0f;
121
}
122
123
if constexpr (Type2 == EMotionType::Dynamic)
124
{
125
Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis);
126
invi2_r2_x_axis.StoreFloat3(&mInvI2_R2xAxis);
127
inv_effective_mass += inInvMass2 + invi2_r2_x_axis.Dot(r2_x_axis);
128
}
129
else
130
{
131
(void)r2_x_axis; // Fix compiler warning: Not using this (it's not calculated either)
132
JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI2_R2xAxis);)
133
}
134
135
return inv_effective_mass;
136
}
137
138
/// Internal helper function to calculate the inverse effective mass
139
JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
140
{
141
// Dispatch to the correct templated form
142
switch (inBody1.GetMotionType())
143
{
144
case EMotionType::Dynamic:
145
{
146
const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
147
float inv_m1 = mp1->GetInverseMass();
148
Mat44 inv_i1 = inBody1.GetInverseInertia();
149
switch (inBody2.GetMotionType())
150
{
151
case EMotionType::Dynamic:
152
return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Dynamic>(inv_m1, inv_i1, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
153
154
case EMotionType::Kinematic:
155
return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Kinematic>(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
156
157
case EMotionType::Static:
158
return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Static>(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
159
160
default:
161
break;
162
}
163
break;
164
}
165
166
case EMotionType::Kinematic:
167
JPH_ASSERT(inBody2.IsDynamic());
168
return TemplatedCalculateInverseEffectiveMass<EMotionType::Kinematic, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
169
170
case EMotionType::Static:
171
JPH_ASSERT(inBody2.IsDynamic());
172
return TemplatedCalculateInverseEffectiveMass<EMotionType::Static, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
173
174
default:
175
break;
176
}
177
178
JPH_ASSERT(false);
179
return 0.0f;
180
}
181
182
/// Internal helper function to calculate the inverse effective mass, version that supports mass scaling
183
JPH_INLINE float CalculateInverseEffectiveMassWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
184
{
185
// Dispatch to the correct templated form
186
switch (inBody1.GetMotionType())
187
{
188
case EMotionType::Dynamic:
189
{
190
Mat44 inv_i1 = inInvInertiaScale1 * inBody1.GetInverseInertia();
191
switch (inBody2.GetMotionType())
192
{
193
case EMotionType::Dynamic:
194
return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Dynamic>(inInvMass1, inv_i1, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
195
196
case EMotionType::Kinematic:
197
return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Kinematic>(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
198
199
case EMotionType::Static:
200
return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Static>(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
201
202
default:
203
break;
204
}
205
break;
206
}
207
208
case EMotionType::Kinematic:
209
JPH_ASSERT(inBody2.IsDynamic());
210
return TemplatedCalculateInverseEffectiveMass<EMotionType::Kinematic, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
211
212
case EMotionType::Static:
213
JPH_ASSERT(inBody2.IsDynamic());
214
return TemplatedCalculateInverseEffectiveMass<EMotionType::Static, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
215
216
default:
217
break;
218
}
219
220
JPH_ASSERT(false);
221
return 0.0f;
222
}
223
224
public:
225
/// Templated form of CalculateConstraintProperties with the motion types baked in
226
template <EMotionType Type1, EMotionType Type2>
227
JPH_INLINE void TemplatedCalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
228
{
229
float inv_effective_mass = TemplatedCalculateInverseEffectiveMass<Type1, Type2>(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis);
230
231
if (inv_effective_mass == 0.0f)
232
Deactivate();
233
else
234
{
235
mEffectiveMass = 1.0f / inv_effective_mass;
236
mSpringPart.CalculateSpringPropertiesWithBias(inBias);
237
}
238
239
JPH_DET_LOG("TemplatedCalculateConstraintProperties: invM1: " << inInvMass1 << " invI1: " << inInvI1 << " r1PlusU: " << inR1PlusU << " invM2: " << inInvMass2 << " invI2: " << inInvI2 << " r2: " << inR2 << " bias: " << inBias << " r1PlusUxAxis: " << mR1PlusUxAxis << " r2xAxis: " << mR2xAxis << " invI1_R1PlusUxAxis: " << mInvI1_R1PlusUxAxis << " invI2_R2xAxis: " << mInvI2_R2xAxis << " effectiveMass: " << mEffectiveMass << " totalLambda: " << mTotalLambda);
240
}
241
242
/// Calculate properties used during the functions below
243
/// @param inBody1 The first body that this constraint is attached to
244
/// @param inBody2 The second body that this constraint is attached to
245
/// @param inR1PlusU See equations above (r1 + u)
246
/// @param inR2 See equations above (r2)
247
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
248
/// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
249
inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
250
{
251
float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
252
253
if (inv_effective_mass == 0.0f)
254
Deactivate();
255
else
256
{
257
mEffectiveMass = 1.0f / inv_effective_mass;
258
mSpringPart.CalculateSpringPropertiesWithBias(inBias);
259
}
260
}
261
262
/// Calculate properties used during the functions below, version that supports mass scaling
263
/// @param inBody1 The first body that this constraint is attached to
264
/// @param inBody2 The second body that this constraint is attached to
265
/// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic)
266
/// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic)
267
/// @param inInvInertiaScale1 Scale factor for the inverse inertia of body 1
268
/// @param inInvInertiaScale2 Scale factor for the inverse inertia of body 2
269
/// @param inR1PlusU See equations above (r1 + u)
270
/// @param inR2 See equations above (r2)
271
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
272
/// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
273
inline void CalculateConstraintPropertiesWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
274
{
275
float inv_effective_mass = CalculateInverseEffectiveMassWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, inR1PlusU, inBody2, inInvMass2, inInvInertiaScale2, inR2, inWorldSpaceAxis);
276
277
if (inv_effective_mass == 0.0f)
278
Deactivate();
279
else
280
{
281
mEffectiveMass = 1.0f / inv_effective_mass;
282
mSpringPart.CalculateSpringPropertiesWithBias(inBias);
283
}
284
}
285
286
/// Calculate properties used during the functions below
287
/// @param inDeltaTime Time step
288
/// @param inBody1 The first body that this constraint is attached to
289
/// @param inBody2 The second body that this constraint is attached to
290
/// @param inR1PlusU See equations above (r1 + u)
291
/// @param inR2 See equations above (r2)
292
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
293
/// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
294
/// @param inC Value of the constraint equation (C).
295
/// @param inFrequency Oscillation frequency (Hz).
296
/// @param inDamping Damping factor (0 = no damping, 1 = critical damping).
297
inline void CalculateConstraintPropertiesWithFrequencyAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inFrequency, float inDamping)
298
{
299
float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
300
301
if (inv_effective_mass == 0.0f)
302
Deactivate();
303
else
304
mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass);
305
}
306
307
/// Calculate properties used during the functions below
308
/// @param inDeltaTime Time step
309
/// @param inBody1 The first body that this constraint is attached to
310
/// @param inBody2 The second body that this constraint is attached to
311
/// @param inR1PlusU See equations above (r1 + u)
312
/// @param inR2 See equations above (r2)
313
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
314
/// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
315
/// @param inC Value of the constraint equation (C).
316
/// @param inStiffness Spring stiffness k.
317
/// @param inDamping Spring damping coefficient c.
318
inline void CalculateConstraintPropertiesWithStiffnessAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inStiffness, float inDamping)
319
{
320
float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
321
322
if (inv_effective_mass == 0.0f)
323
Deactivate();
324
else
325
mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass);
326
}
327
328
/// Selects one of the above functions based on the spring settings
329
inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings)
330
{
331
float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
332
333
if (inv_effective_mass == 0.0f)
334
Deactivate();
335
else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping)
336
mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass);
337
else
338
mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass);
339
}
340
341
/// Deactivate this constraint
342
inline void Deactivate()
343
{
344
mEffectiveMass = 0.0f;
345
mTotalLambda = 0.0f;
346
}
347
348
/// Check if constraint is active
349
inline bool IsActive() const
350
{
351
return mEffectiveMass != 0.0f;
352
}
353
354
/// Templated form of WarmStart with the motion types baked in
355
template <EMotionType Type1, EMotionType Type2>
356
inline void TemplatedWarmStart(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
357
{
358
mTotalLambda *= inWarmStartImpulseRatio;
359
360
ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, mTotalLambda);
361
}
362
363
/// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses
364
/// @param ioBody1 The first body that this constraint is attached to
365
/// @param ioBody2 The second body that this constraint is attached to
366
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
367
/// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame
368
inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
369
{
370
EMotionType motion_type1 = ioBody1.GetMotionType();
371
MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
372
373
EMotionType motion_type2 = ioBody2.GetMotionType();
374
MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
375
376
// Dispatch to the correct templated form
377
// Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies
378
if (motion_type1 == EMotionType::Dynamic)
379
{
380
if (motion_type2 == EMotionType::Dynamic)
381
TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio);
382
else
383
TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inWarmStartImpulseRatio);
384
}
385
else
386
{
387
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
388
TemplatedWarmStart<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio);
389
}
390
}
391
392
/// Templated form of SolveVelocityConstraint with the motion types baked in, part 1: get the total lambda
393
template <EMotionType Type1, EMotionType Type2>
394
JPH_INLINE float TemplatedSolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const
395
{
396
// Calculate jacobian multiplied by linear velocity
397
float jv;
398
if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
399
jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity());
400
else if constexpr (Type1 != EMotionType::Static)
401
jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity());
402
else if constexpr (Type2 != EMotionType::Static)
403
jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity());
404
else
405
JPH_ASSERT(false); // Static vs static is nonsensical!
406
407
// Calculate jacobian multiplied by angular velocity
408
if constexpr (Type1 != EMotionType::Static)
409
jv += Vec3::sLoadFloat3Unsafe(mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity());
410
if constexpr (Type2 != EMotionType::Static)
411
jv -= Vec3::sLoadFloat3Unsafe(mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity());
412
413
// Lagrange multiplier is:
414
//
415
// lambda = -K^-1 (J v + b)
416
float lambda = mEffectiveMass * (jv - mSpringPart.GetBias(mTotalLambda));
417
418
// Return the total accumulated lambda
419
return mTotalLambda + lambda;
420
}
421
422
/// Templated form of SolveVelocityConstraint with the motion types baked in, part 2: apply new lambda
423
template <EMotionType Type1, EMotionType Type2>
424
JPH_INLINE bool TemplatedSolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
425
{
426
float delta_lambda = inTotalLambda - mTotalLambda; // Calculate change in lambda
427
mTotalLambda = inTotalLambda; // Store accumulated impulse
428
429
return ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, delta_lambda);
430
}
431
432
/// Templated form of SolveVelocityConstraint with the motion types baked in
433
template <EMotionType Type1, EMotionType Type2>
434
inline bool TemplatedSolveVelocityConstraint(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
435
{
436
float total_lambda = TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis);
437
438
// Clamp impulse to specified range
439
total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
440
441
return TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, total_lambda);
442
}
443
444
/// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
445
/// @param ioBody1 The first body that this constraint is attached to
446
/// @param ioBody2 The second body that this constraint is attached to
447
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
448
/// @param inMinLambda Minimum value of constraint impulse to apply (N s)
449
/// @param inMaxLambda Maximum value of constraint impulse to apply (N s)
450
inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
451
{
452
EMotionType motion_type1 = ioBody1.GetMotionType();
453
MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
454
455
EMotionType motion_type2 = ioBody2.GetMotionType();
456
MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
457
458
// Dispatch to the correct templated form
459
switch (motion_type1)
460
{
461
case EMotionType::Dynamic:
462
switch (motion_type2)
463
{
464
case EMotionType::Dynamic:
465
return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda);
466
467
case EMotionType::Kinematic:
468
return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
469
470
case EMotionType::Static:
471
return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
472
473
default:
474
JPH_ASSERT(false);
475
break;
476
}
477
break;
478
479
case EMotionType::Kinematic:
480
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
481
return TemplatedSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda);
482
483
case EMotionType::Static:
484
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
485
return TemplatedSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda);
486
487
default:
488
JPH_ASSERT(false);
489
break;
490
}
491
492
return false;
493
}
494
495
/// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
496
/// @param ioBody1 The first body that this constraint is attached to
497
/// @param ioBody2 The second body that this constraint is attached to
498
/// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic)
499
/// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic)
500
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
501
/// @param inMinLambda Minimum value of constraint impulse to apply (N s)
502
/// @param inMaxLambda Maximum value of constraint impulse to apply (N s)
503
inline bool SolveVelocityConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
504
{
505
EMotionType motion_type1 = ioBody1.GetMotionType();
506
MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
507
508
EMotionType motion_type2 = ioBody2.GetMotionType();
509
MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
510
511
// Dispatch to the correct templated form
512
switch (motion_type1)
513
{
514
case EMotionType::Dynamic:
515
switch (motion_type2)
516
{
517
case EMotionType::Dynamic:
518
return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, inInvMass1, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
519
520
case EMotionType::Kinematic:
521
return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
522
523
case EMotionType::Static:
524
return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
525
526
default:
527
JPH_ASSERT(false);
528
break;
529
}
530
break;
531
532
case EMotionType::Kinematic:
533
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
534
return TemplatedSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
535
536
case EMotionType::Static:
537
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
538
return TemplatedSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
539
540
default:
541
JPH_ASSERT(false);
542
break;
543
}
544
545
return false;
546
}
547
548
/// Iteratively update the position constraint. Makes sure C(...) = 0.
549
/// @param ioBody1 The first body that this constraint is attached to
550
/// @param ioBody2 The second body that this constraint is attached to
551
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
552
/// @param inC Value of the constraint equation (C)
553
/// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
554
inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
555
{
556
// Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint
557
if (inC != 0.0f && !mSpringPart.IsActive())
558
{
559
// Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
560
//
561
// lambda = -K^-1 * beta / dt * C
562
//
563
// We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
564
float lambda = -mEffectiveMass * inBaumgarte * inC;
565
566
// Directly integrate velocity change for one time step
567
//
568
// Euler velocity integration:
569
// dv = M^-1 P
570
//
571
// Impulse:
572
// P = J^T lambda
573
//
574
// Euler position integration:
575
// x' = x + dv * dt
576
//
577
// Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
578
// Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
579
// stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
580
// integrate + a position integrate and then discard the velocity change.
581
if (ioBody1.IsDynamic())
582
{
583
ioBody1.SubPositionStep((lambda * ioBody1.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis);
584
ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
585
}
586
if (ioBody2.IsDynamic())
587
{
588
ioBody2.AddPositionStep((lambda * ioBody2.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis);
589
ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
590
}
591
return true;
592
}
593
594
return false;
595
}
596
597
/// Iteratively update the position constraint. Makes sure C(...) = 0.
598
/// @param ioBody1 The first body that this constraint is attached to
599
/// @param ioBody2 The second body that this constraint is attached to
600
/// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic)
601
/// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic)
602
/// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
603
/// @param inC Value of the constraint equation (C)
604
/// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
605
inline bool SolvePositionConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
606
{
607
// Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint
608
if (inC != 0.0f && !mSpringPart.IsActive())
609
{
610
// Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
611
//
612
// lambda = -K^-1 * beta / dt * C
613
//
614
// We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
615
float lambda = -mEffectiveMass * inBaumgarte * inC;
616
617
// Directly integrate velocity change for one time step
618
//
619
// Euler velocity integration:
620
// dv = M^-1 P
621
//
622
// Impulse:
623
// P = J^T lambda
624
//
625
// Euler position integration:
626
// x' = x + dv * dt
627
//
628
// Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
629
// Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
630
// stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
631
// integrate + a position integrate and then discard the velocity change.
632
if (ioBody1.IsDynamic())
633
{
634
ioBody1.SubPositionStep((lambda * inInvMass1) * inWorldSpaceAxis);
635
ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
636
}
637
if (ioBody2.IsDynamic())
638
{
639
ioBody2.AddPositionStep((lambda * inInvMass2) * inWorldSpaceAxis);
640
ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
641
}
642
return true;
643
}
644
645
return false;
646
}
647
648
/// Override total lagrange multiplier, can be used to set the initial value for warm starting
649
inline void SetTotalLambda(float inLambda)
650
{
651
mTotalLambda = inLambda;
652
}
653
654
/// Return lagrange multiplier
655
inline float GetTotalLambda() const
656
{
657
return mTotalLambda;
658
}
659
660
/// Save state of this constraint part
661
void SaveState(StateRecorder &inStream) const
662
{
663
inStream.Write(mTotalLambda);
664
}
665
666
/// Restore state of this constraint part
667
void RestoreState(StateRecorder &inStream)
668
{
669
inStream.Read(mTotalLambda);
670
}
671
672
private:
673
Float3 mR1PlusUxAxis;
674
Float3 mR2xAxis;
675
Float3 mInvI1_R1PlusUxAxis;
676
Float3 mInvI2_R2xAxis;
677
float mEffectiveMass = 0.0f;
678
SpringPart mSpringPart;
679
float mTotalLambda = 0.0f;
680
};
681
682
JPH_NAMESPACE_END
683
684