Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp
9912 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/SwingTwistConstraint.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
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SwingTwistConstraintSettings)
19
{
20
JPH_ADD_BASE_CLASS(SwingTwistConstraintSettings, TwoBodyConstraintSettings)
21
22
JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSpace)
23
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition1)
24
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis1)
25
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis1)
26
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition2)
27
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis2)
28
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis2)
29
JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSwingType)
30
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mNormalHalfConeAngle)
31
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneHalfConeAngle)
32
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMinAngle)
33
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMaxAngle)
34
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mMaxFrictionTorque)
35
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mSwingMotorSettings)
36
JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMotorSettings)
37
}
38
39
void SwingTwistConstraintSettings::SaveBinaryState(StreamOut &inStream) const
40
{
41
ConstraintSettings::SaveBinaryState(inStream);
42
43
inStream.Write(mSpace);
44
inStream.Write(mPosition1);
45
inStream.Write(mTwistAxis1);
46
inStream.Write(mPlaneAxis1);
47
inStream.Write(mPosition2);
48
inStream.Write(mTwistAxis2);
49
inStream.Write(mPlaneAxis2);
50
inStream.Write(mSwingType);
51
inStream.Write(mNormalHalfConeAngle);
52
inStream.Write(mPlaneHalfConeAngle);
53
inStream.Write(mTwistMinAngle);
54
inStream.Write(mTwistMaxAngle);
55
inStream.Write(mMaxFrictionTorque);
56
mSwingMotorSettings.SaveBinaryState(inStream);
57
mTwistMotorSettings.SaveBinaryState(inStream);
58
}
59
60
void SwingTwistConstraintSettings::RestoreBinaryState(StreamIn &inStream)
61
{
62
ConstraintSettings::RestoreBinaryState(inStream);
63
64
inStream.Read(mSpace);
65
inStream.Read(mPosition1);
66
inStream.Read(mTwistAxis1);
67
inStream.Read(mPlaneAxis1);
68
inStream.Read(mPosition2);
69
inStream.Read(mTwistAxis2);
70
inStream.Read(mPlaneAxis2);
71
inStream.Read(mSwingType);
72
inStream.Read(mNormalHalfConeAngle);
73
inStream.Read(mPlaneHalfConeAngle);
74
inStream.Read(mTwistMinAngle);
75
inStream.Read(mTwistMaxAngle);
76
inStream.Read(mMaxFrictionTorque);
77
mSwingMotorSettings.RestoreBinaryState(inStream);
78
mTwistMotorSettings.RestoreBinaryState(inStream);
79
}
80
81
TwoBodyConstraint *SwingTwistConstraintSettings::Create(Body &inBody1, Body &inBody2) const
82
{
83
return new SwingTwistConstraint(inBody1, inBody2, *this);
84
}
85
86
void SwingTwistConstraint::UpdateLimits()
87
{
88
// Pass limits on to swing twist constraint part
89
mSwingTwistConstraintPart.SetLimits(mTwistMinAngle, mTwistMaxAngle, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle);
90
}
91
92
SwingTwistConstraint::SwingTwistConstraint(Body &inBody1, Body &inBody2, const SwingTwistConstraintSettings &inSettings) :
93
TwoBodyConstraint(inBody1, inBody2, inSettings),
94
mNormalHalfConeAngle(inSettings.mNormalHalfConeAngle),
95
mPlaneHalfConeAngle(inSettings.mPlaneHalfConeAngle),
96
mTwistMinAngle(inSettings.mTwistMinAngle),
97
mTwistMaxAngle(inSettings.mTwistMaxAngle),
98
mMaxFrictionTorque(inSettings.mMaxFrictionTorque),
99
mSwingMotorSettings(inSettings.mSwingMotorSettings),
100
mTwistMotorSettings(inSettings.mTwistMotorSettings)
101
{
102
// Override swing type
103
mSwingTwistConstraintPart.SetSwingType(inSettings.mSwingType);
104
105
// Calculate rotation needed to go from constraint space to body1 local space
106
Vec3 normal_axis1 = inSettings.mPlaneAxis1.Cross(inSettings.mTwistAxis1);
107
Mat44 c_to_b1(Vec4(inSettings.mTwistAxis1, 0), Vec4(normal_axis1, 0), Vec4(inSettings.mPlaneAxis1, 0), Vec4(0, 0, 0, 1));
108
mConstraintToBody1 = c_to_b1.GetQuaternion();
109
110
// Calculate rotation needed to go from constraint space to body2 local space
111
Vec3 normal_axis2 = inSettings.mPlaneAxis2.Cross(inSettings.mTwistAxis2);
112
Mat44 c_to_b2(Vec4(inSettings.mTwistAxis2, 0), Vec4(normal_axis2, 0), Vec4(inSettings.mPlaneAxis2, 0), Vec4(0, 0, 0, 1));
113
mConstraintToBody2 = c_to_b2.GetQuaternion();
114
115
if (inSettings.mSpace == EConstraintSpace::WorldSpace)
116
{
117
// If all properties were specified in world space, take them to local space now
118
mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPosition1);
119
mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1;
120
121
mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPosition2);
122
mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2;
123
}
124
else
125
{
126
mLocalSpacePosition1 = Vec3(inSettings.mPosition1);
127
mLocalSpacePosition2 = Vec3(inSettings.mPosition2);
128
}
129
130
UpdateLimits();
131
}
132
133
void SwingTwistConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
134
{
135
if (mBody1->GetID() == inBodyID)
136
mLocalSpacePosition1 -= inDeltaCOM;
137
else if (mBody2->GetID() == inBodyID)
138
mLocalSpacePosition2 -= inDeltaCOM;
139
}
140
141
Quat SwingTwistConstraint::GetRotationInConstraintSpace() const
142
{
143
// Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform())
144
// Let c1, c2 be the transform that takes a vector from constraint space to local space of body1 and body2 (For body1 this is Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1))
145
// Let q be the rotation of the constraint in constraint space
146
// b2 takes a vector from the local space of body2 to world space
147
// To express this in terms of b1: b2 = b1 * c1 * q * c2^-1
148
// c2^-1 goes from local body 2 space to constraint space
149
// q rotates the constraint
150
// c1 goes from constraint space to body 1 local space
151
// b1 goes from body 1 local space to world space
152
// So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2
153
// Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations
154
Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1;
155
Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2;
156
return constraint_body1_to_world.Conjugated() * constraint_body2_to_world;
157
}
158
159
void SwingTwistConstraint::SetSwingMotorState(EMotorState inState)
160
{
161
JPH_ASSERT(inState == EMotorState::Off || mSwingMotorSettings.IsValid());
162
163
if (mSwingMotorState != inState)
164
{
165
mSwingMotorState = inState;
166
167
// Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes)
168
for (AngleConstraintPart &c : mMotorConstraintPart)
169
c.Deactivate();
170
}
171
}
172
173
void SwingTwistConstraint::SetTwistMotorState(EMotorState inState)
174
{
175
JPH_ASSERT(inState == EMotorState::Off || mTwistMotorSettings.IsValid());
176
177
if (mTwistMotorState != inState)
178
{
179
mTwistMotorState = inState;
180
181
// Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes)
182
mMotorConstraintPart[0].Deactivate();
183
}
184
}
185
186
void SwingTwistConstraint::SetTargetOrientationCS(QuatArg inOrientation)
187
{
188
Quat q_swing, q_twist;
189
inOrientation.GetSwingTwist(q_swing, q_twist);
190
191
uint clamped_axis;
192
mSwingTwistConstraintPart.ClampSwingTwist(q_swing, q_twist, clamped_axis);
193
194
if (clamped_axis != 0)
195
mTargetOrientation = q_swing * q_twist;
196
else
197
mTargetOrientation = inOrientation;
198
}
199
200
void SwingTwistConstraint::SetupVelocityConstraint(float inDeltaTime)
201
{
202
// Setup point constraint
203
Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());
204
Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());
205
mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2);
206
207
// GetRotationInConstraintSpace written out since we reuse the sub expressions
208
Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1;
209
Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2;
210
Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world;
211
212
// Calculate constraint properties for the swing twist limit
213
mSwingTwistConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, q, constraint_body1_to_world);
214
215
if (mSwingMotorState != EMotorState::Off || mTwistMotorState != EMotorState::Off || mMaxFrictionTorque > 0.0f)
216
{
217
// Calculate rotation motor axis
218
Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world);
219
for (int i = 0; i < 3; ++i)
220
mWorldSpaceMotorAxis[i] = ws_axis.GetColumn3(i);
221
222
Vec3 rotation_error;
223
if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position)
224
{
225
// Get target orientation along the shortest path from q
226
Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation;
227
228
// The definition of the constraint rotation q:
229
// R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1)
230
//
231
// R2' is the rotation of body 2 when reaching the target_orientation:
232
// R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2)
233
//
234
// The difference in body 2 space:
235
// R2' = R2 * diff_body2 (3)
236
//
237
// We want to specify the difference in the constraint space of body 2:
238
// diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4)
239
//
240
// Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5)
241
// Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6)
242
// Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7)
243
// Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^*
244
// <=> target_orientation = q * diff
245
// <=> diff = q^* * target_orientation
246
Quat diff = q.Conjugated() * target_orientation;
247
248
// Approximate error angles
249
// The imaginary part of a quaternion is rotation_axis * sin(angle / 2)
250
// If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i]
251
// We'll be making small time steps, so if the angle is not small at least the sign will be correct and we'll move in the right direction
252
rotation_error = -2.0f * diff.GetXYZ();
253
}
254
255
// Swing motor
256
switch (mSwingMotorState)
257
{
258
case EMotorState::Off:
259
if (mMaxFrictionTorque > 0.0f)
260
{
261
// Enable friction
262
for (int i = 1; i < 3; ++i)
263
mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f);
264
}
265
else
266
{
267
// Disable friction
268
for (AngleConstraintPart &c : mMotorConstraintPart)
269
c.Deactivate();
270
}
271
break;
272
273
case EMotorState::Velocity:
274
// Use motor to create angular velocity around desired axis
275
for (int i = 1; i < 3; ++i)
276
mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], -mTargetAngularVelocity[i]);
277
break;
278
279
case EMotorState::Position:
280
// Use motor to drive rotation error to zero
281
if (mSwingMotorSettings.mSpringSettings.HasStiffness())
282
{
283
for (int i = 1; i < 3; ++i)
284
mMotorConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f, rotation_error[i], mSwingMotorSettings.mSpringSettings);
285
}
286
else
287
{
288
for (int i = 1; i < 3; ++i)
289
mMotorConstraintPart[i].Deactivate();
290
}
291
break;
292
}
293
294
// Twist motor
295
switch (mTwistMotorState)
296
{
297
case EMotorState::Off:
298
if (mMaxFrictionTorque > 0.0f)
299
{
300
// Enable friction
301
mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f);
302
}
303
else
304
{
305
// Disable friction
306
mMotorConstraintPart[0].Deactivate();
307
}
308
break;
309
310
case EMotorState::Velocity:
311
// Use motor to create angular velocity around desired axis
312
mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], -mTargetAngularVelocity[0]);
313
break;
314
315
case EMotorState::Position:
316
// Use motor to drive rotation error to zero
317
if (mTwistMotorSettings.mSpringSettings.HasStiffness())
318
mMotorConstraintPart[0].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f, rotation_error[0], mTwistMotorSettings.mSpringSettings);
319
else
320
mMotorConstraintPart[0].Deactivate();
321
break;
322
}
323
}
324
else
325
{
326
// Disable rotation motor
327
for (AngleConstraintPart &c : mMotorConstraintPart)
328
c.Deactivate();
329
}
330
}
331
332
void SwingTwistConstraint::ResetWarmStart()
333
{
334
for (AngleConstraintPart &c : mMotorConstraintPart)
335
c.Deactivate();
336
mSwingTwistConstraintPart.Deactivate();
337
mPointConstraintPart.Deactivate();
338
}
339
340
void SwingTwistConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
341
{
342
// Warm starting: Apply previous frame impulse
343
for (AngleConstraintPart &c : mMotorConstraintPart)
344
c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
345
mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
346
mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
347
}
348
349
bool SwingTwistConstraint::SolveVelocityConstraint(float inDeltaTime)
350
{
351
bool impulse = false;
352
353
// Solve twist rotation motor
354
if (mMotorConstraintPart[0].IsActive())
355
{
356
// Twist limits
357
float min_twist_limit, max_twist_limit;
358
if (mTwistMotorState == EMotorState::Off)
359
{
360
max_twist_limit = inDeltaTime * mMaxFrictionTorque;
361
min_twist_limit = -max_twist_limit;
362
}
363
else
364
{
365
min_twist_limit = inDeltaTime * mTwistMotorSettings.mMinTorqueLimit;
366
max_twist_limit = inDeltaTime * mTwistMotorSettings.mMaxTorqueLimit;
367
}
368
369
impulse |= mMotorConstraintPart[0].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], min_twist_limit, max_twist_limit);
370
}
371
372
// Solve swing rotation motor
373
if (mMotorConstraintPart[1].IsActive())
374
{
375
// Swing parts should turn on / off together
376
JPH_ASSERT(mMotorConstraintPart[2].IsActive());
377
378
// Swing limits
379
float min_swing_limit, max_swing_limit;
380
if (mSwingMotorState == EMotorState::Off)
381
{
382
max_swing_limit = inDeltaTime * mMaxFrictionTorque;
383
min_swing_limit = -max_swing_limit;
384
}
385
else
386
{
387
min_swing_limit = inDeltaTime * mSwingMotorSettings.mMinTorqueLimit;
388
max_swing_limit = inDeltaTime * mSwingMotorSettings.mMaxTorqueLimit;
389
}
390
391
for (int i = 1; i < 3; ++i)
392
impulse |= mMotorConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], min_swing_limit, max_swing_limit);
393
}
394
else
395
{
396
// Swing parts should turn on / off together
397
JPH_ASSERT(!mMotorConstraintPart[2].IsActive());
398
}
399
400
// Solve rotation limits
401
impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
402
403
// Solve position constraint
404
impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
405
406
return impulse;
407
}
408
409
bool SwingTwistConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
410
{
411
bool impulse = false;
412
413
// Solve rotation violations
414
Quat q = GetRotationInConstraintSpace();
415
impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte);
416
417
// Solve position violations
418
mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);
419
impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);
420
421
return impulse;
422
}
423
424
#ifdef JPH_DEBUG_RENDERER
425
void SwingTwistConstraint::DrawConstraint(DebugRenderer *inRenderer) const
426
{
427
// Get constraint properties in world space
428
RMat44 transform1 = mBody1->GetCenterOfMassTransform();
429
RVec3 position1 = transform1 * mLocalSpacePosition1;
430
Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1;
431
Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2;
432
433
// Draw constraint orientation
434
inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize);
435
436
// Draw current swing and twist
437
Quat q = GetRotationInConstraintSpace();
438
Quat q_swing, q_twist;
439
q.GetSwingTwist(q_swing, q_twist);
440
inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite);
441
inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite);
442
443
if (mSwingMotorState == EMotorState::Velocity || mTwistMotorState == EMotorState::Velocity)
444
{
445
// Draw target angular velocity
446
inRenderer->DrawArrow(position1, position1 + rotation2 * mTargetAngularVelocity, Color::sRed, 0.1f);
447
}
448
if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position)
449
{
450
// Draw motor swing and twist
451
Quat swing, twist;
452
mTargetOrientation.GetSwingTwist(swing, twist);
453
inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * twist).RotateAxisY(), Color::sYellow);
454
inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * swing).RotateAxisX(), Color::sCyan);
455
}
456
}
457
458
void SwingTwistConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const
459
{
460
// Get matrix that transforms from constraint space to world space
461
RMat44 constraint_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1);
462
463
// Draw limits
464
if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Pyramid)
465
inRenderer->DrawSwingPyramidLimits(constraint_to_world, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off);
466
else
467
inRenderer->DrawSwingConeLimits(constraint_to_world, mPlaneHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off);
468
inRenderer->DrawPie(constraint_to_world.GetTranslation(), mDrawConstraintSize, constraint_to_world.GetAxisX(), constraint_to_world.GetAxisY(), mTwistMinAngle, mTwistMaxAngle, Color::sPurple, DebugRenderer::ECastShadow::Off);
469
}
470
#endif // JPH_DEBUG_RENDERER
471
472
void SwingTwistConstraint::SaveState(StateRecorder &inStream) const
473
{
474
TwoBodyConstraint::SaveState(inStream);
475
476
mPointConstraintPart.SaveState(inStream);
477
mSwingTwistConstraintPart.SaveState(inStream);
478
for (const AngleConstraintPart &c : mMotorConstraintPart)
479
c.SaveState(inStream);
480
481
inStream.Write(mSwingMotorState);
482
inStream.Write(mTwistMotorState);
483
inStream.Write(mTargetAngularVelocity);
484
inStream.Write(mTargetOrientation);
485
}
486
487
void SwingTwistConstraint::RestoreState(StateRecorder &inStream)
488
{
489
TwoBodyConstraint::RestoreState(inStream);
490
491
mPointConstraintPart.RestoreState(inStream);
492
mSwingTwistConstraintPart.RestoreState(inStream);
493
for (AngleConstraintPart &c : mMotorConstraintPart)
494
c.RestoreState(inStream);
495
496
inStream.Read(mSwingMotorState);
497
inStream.Read(mTwistMotorState);
498
inStream.Read(mTargetAngularVelocity);
499
inStream.Read(mTargetOrientation);
500
}
501
502
Ref<ConstraintSettings> SwingTwistConstraint::GetConstraintSettings() const
503
{
504
SwingTwistConstraintSettings *settings = new SwingTwistConstraintSettings;
505
ToConstraintSettings(*settings);
506
settings->mSpace = EConstraintSpace::LocalToBodyCOM;
507
settings->mPosition1 = RVec3(mLocalSpacePosition1);
508
settings->mTwistAxis1 = mConstraintToBody1.RotateAxisX();
509
settings->mPlaneAxis1 = mConstraintToBody1.RotateAxisZ();
510
settings->mPosition2 = RVec3(mLocalSpacePosition2);
511
settings->mTwistAxis2 = mConstraintToBody2.RotateAxisX();
512
settings->mPlaneAxis2 = mConstraintToBody2.RotateAxisZ();
513
settings->mSwingType = mSwingTwistConstraintPart.GetSwingType();
514
settings->mNormalHalfConeAngle = mNormalHalfConeAngle;
515
settings->mPlaneHalfConeAngle = mPlaneHalfConeAngle;
516
settings->mTwistMinAngle = mTwistMinAngle;
517
settings->mTwistMaxAngle = mTwistMaxAngle;
518
settings->mMaxFrictionTorque = mMaxFrictionTorque;
519
settings->mSwingMotorSettings = mSwingMotorSettings;
520
settings->mTwistMotorSettings = mTwistMotorSettings;
521
return settings;
522
}
523
524
JPH_NAMESPACE_END
525
526