Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
SwingTwistConstraintPart.h
Go to the documentation of this file.
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
10
12
14enum class ESwingType : uint8
15{
16 Cone,
17 Pyramid,
18};
19
33{
34public:
36 void SetSwingType(ESwingType inSwingType)
37 {
38 mSwingType = inSwingType;
39 }
40
43 {
44 return mSwingType;
45 }
46
48 void SetLimits(float inTwistMinAngle, float inTwistMaxAngle, float inSwingYMinAngle, float inSwingYMaxAngle, float inSwingZMinAngle, float inSwingZMaxAngle)
49 {
50 constexpr float cLockedAngle = DegreesToRadians(0.5f);
51 constexpr float cFreeAngle = DegreesToRadians(179.5f);
52
53 // Assume sane input
54 JPH_ASSERT(inTwistMinAngle <= inTwistMaxAngle);
55 JPH_ASSERT(inSwingYMinAngle <= inSwingYMaxAngle);
56 JPH_ASSERT(inSwingZMinAngle <= inSwingZMaxAngle);
57 JPH_ASSERT(inSwingYMinAngle >= -JPH_PI && inSwingYMaxAngle <= JPH_PI);
58 JPH_ASSERT(inSwingZMinAngle >= -JPH_PI && inSwingZMaxAngle <= JPH_PI);
59
60 // Calculate the sine and cosine of the half angles
61 Vec4 half_twist = 0.5f * Vec4(inTwistMinAngle, inTwistMaxAngle, 0, 0);
62 Vec4 twist_s, twist_c;
63 half_twist.SinCos(twist_s, twist_c);
64 Vec4 half_swing = 0.5f * Vec4(inSwingYMinAngle, inSwingYMaxAngle, inSwingZMinAngle, inSwingZMaxAngle);
65 Vec4 swing_s, swing_c;
66 half_swing.SinCos(swing_s, swing_c);
67
68 // Store half angles for pyramid limit
69 mSwingYHalfMinAngle = half_swing.GetX();
70 mSwingYHalfMaxAngle = half_swing.GetY();
71 mSwingZHalfMinAngle = half_swing.GetZ();
72 mSwingZHalfMaxAngle = half_swing.GetW();
73
74 // Store axis flags which are used at runtime to quickly decided which constraints to apply
75 mRotationFlags = 0;
76 if (inTwistMinAngle > -cLockedAngle && inTwistMaxAngle < cLockedAngle)
77 {
78 mRotationFlags |= TwistXLocked;
79 mSinTwistHalfMinAngle = 0.0f;
80 mSinTwistHalfMaxAngle = 0.0f;
81 mCosTwistHalfMinAngle = 1.0f;
82 mCosTwistHalfMaxAngle = 1.0f;
83 }
84 else if (inTwistMinAngle < -cFreeAngle && inTwistMaxAngle > cFreeAngle)
85 {
86 mRotationFlags |= TwistXFree;
87 mSinTwistHalfMinAngle = -1.0f;
88 mSinTwistHalfMaxAngle = 1.0f;
89 mCosTwistHalfMinAngle = 0.0f;
90 mCosTwistHalfMaxAngle = 0.0f;
91 }
92 else
93 {
94 mSinTwistHalfMinAngle = twist_s.GetX();
95 mSinTwistHalfMaxAngle = twist_s.GetY();
96 mCosTwistHalfMinAngle = twist_c.GetX();
97 mCosTwistHalfMaxAngle = twist_c.GetY();
98 }
99
100 if (inSwingYMinAngle > -cLockedAngle && inSwingYMaxAngle < cLockedAngle)
101 {
102 mRotationFlags |= SwingYLocked;
103 mSinSwingYHalfMinAngle = 0.0f;
104 mSinSwingYHalfMaxAngle = 0.0f;
105 mCosSwingYHalfMinAngle = 1.0f;
106 mCosSwingYHalfMaxAngle = 1.0f;
107 }
108 else if (inSwingYMinAngle < -cFreeAngle && inSwingYMaxAngle > cFreeAngle)
109 {
110 mRotationFlags |= SwingYFree;
111 mSinSwingYHalfMinAngle = -1.0f;
112 mSinSwingYHalfMaxAngle = 1.0f;
113 mCosSwingYHalfMinAngle = 0.0f;
114 mCosSwingYHalfMaxAngle = 0.0f;
115 }
116 else
117 {
118 mSinSwingYHalfMinAngle = swing_s.GetX();
119 mSinSwingYHalfMaxAngle = swing_s.GetY();
120 mCosSwingYHalfMinAngle = swing_c.GetX();
121 mCosSwingYHalfMaxAngle = swing_c.GetY();
122 JPH_ASSERT(mSinSwingYHalfMinAngle <= mSinSwingYHalfMaxAngle);
123 }
124
125 if (inSwingZMinAngle > -cLockedAngle && inSwingZMaxAngle < cLockedAngle)
126 {
127 mRotationFlags |= SwingZLocked;
128 mSinSwingZHalfMinAngle = 0.0f;
129 mSinSwingZHalfMaxAngle = 0.0f;
130 mCosSwingZHalfMinAngle = 1.0f;
131 mCosSwingZHalfMaxAngle = 1.0f;
132 }
133 else if (inSwingZMinAngle < -cFreeAngle && inSwingZMaxAngle > cFreeAngle)
134 {
135 mRotationFlags |= SwingZFree;
136 mSinSwingZHalfMinAngle = -1.0f;
137 mSinSwingZHalfMaxAngle = 1.0f;
138 mCosSwingZHalfMinAngle = 0.0f;
139 mCosSwingZHalfMaxAngle = 0.0f;
140 }
141 else
142 {
143 mSinSwingZHalfMinAngle = swing_s.GetZ();
144 mSinSwingZHalfMaxAngle = swing_s.GetW();
145 mCosSwingZHalfMinAngle = swing_c.GetZ();
146 mCosSwingZHalfMaxAngle = swing_c.GetW();
147 JPH_ASSERT(mSinSwingZHalfMinAngle <= mSinSwingZHalfMaxAngle);
148 }
149 }
150
152 static constexpr uint cClampedTwistMin = 1 << 0;
153 static constexpr uint cClampedTwistMax = 1 << 1;
154 static constexpr uint cClampedSwingYMin = 1 << 2;
155 static constexpr uint cClampedSwingYMax = 1 << 3;
156 static constexpr uint cClampedSwingZMin = 1 << 4;
157 static constexpr uint cClampedSwingZMax = 1 << 5;
158
160 static JPH_INLINE bool sDistanceToMinShorter(float inDeltaMin, float inDeltaMax)
161 {
162 // We're outside of the limits, get actual delta to min/max range
163 // Note that a swing/twist of -1 and 1 represent the same angle, so if the difference is bigger than 1, the shortest angle is the other way around (2 - difference)
164 // We should actually be working with angles rather than sin(angle / 2). When the difference is small the approximation is accurate, but
165 // when working with extreme values the calculation is off and e.g. when the limit is between 0 and 180 a value of approx -60 will clamp
166 // to 180 rather than 0 (you'd expect anything > -90 to go to 0).
167 inDeltaMin = abs(inDeltaMin);
168 if (inDeltaMin > 1.0f) inDeltaMin = 2.0f - inDeltaMin;
169 inDeltaMax = abs(inDeltaMax);
170 if (inDeltaMax > 1.0f) inDeltaMax = 2.0f - inDeltaMax;
171 return inDeltaMin < inDeltaMax;
172 }
173
175 inline void ClampSwingTwist(Quat &ioSwing, Quat &ioTwist, uint &outClampedAxis) const
176 {
177 // Start with not clamped
178 outClampedAxis = 0;
179
180 // Check that swing and twist quaternions don't contain rotations around the wrong axis
181 JPH_ASSERT(ioSwing.GetX() == 0.0f);
182 JPH_ASSERT(ioTwist.GetY() == 0.0f);
183 JPH_ASSERT(ioTwist.GetZ() == 0.0f);
184
185 // Ensure quaternions have w > 0
186 bool negate_swing = ioSwing.GetW() < 0.0f;
187 if (negate_swing)
188 ioSwing = -ioSwing;
189 bool negate_twist = ioTwist.GetW() < 0.0f;
190 if (negate_twist)
191 ioTwist = -ioTwist;
192
193 if (mRotationFlags & TwistXLocked)
194 {
195 // Twist axis is locked, clamp whenever twist is not identity
196 outClampedAxis |= ioTwist.GetX() != 0.0f? (cClampedTwistMin | cClampedTwistMax) : 0;
197 ioTwist = Quat::sIdentity();
198 }
199 else if ((mRotationFlags & TwistXFree) == 0)
200 {
201 // Twist axis has limit, clamp whenever out of range
202 float delta_min = mSinTwistHalfMinAngle - ioTwist.GetX();
203 float delta_max = ioTwist.GetX() - mSinTwistHalfMaxAngle;
204 if (delta_min > 0.0f || delta_max > 0.0f)
205 {
206 // Pick the twist that corresponds to the smallest delta
207 if (sDistanceToMinShorter(delta_min, delta_max))
208 {
209 ioTwist = Quat(mSinTwistHalfMinAngle, 0, 0, mCosTwistHalfMinAngle);
210 outClampedAxis |= cClampedTwistMin;
211 }
212 else
213 {
214 ioTwist = Quat(mSinTwistHalfMaxAngle, 0, 0, mCosTwistHalfMaxAngle);
215 outClampedAxis |= cClampedTwistMax;
216 }
217 }
218 }
219
220 // Clamp swing
221 if (mRotationFlags & SwingYLocked)
222 {
223 if (mRotationFlags & SwingZLocked)
224 {
225 // Both swing Y and Z are disabled, no degrees of freedom in swing
226 outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0;
227 outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0;
228 ioSwing = Quat::sIdentity();
229 }
230 else
231 {
232 // Swing Y angle disabled, only 1 degree of freedom in swing
233 outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0;
234 float delta_min = mSinSwingZHalfMinAngle - ioSwing.GetZ();
235 float delta_max = ioSwing.GetZ() - mSinSwingZHalfMaxAngle;
236 if (delta_min > 0.0f || delta_max > 0.0f)
237 {
238 // Pick the swing that corresponds to the smallest delta
239 if (sDistanceToMinShorter(delta_min, delta_max))
240 {
241 ioSwing = Quat(0, 0, mSinSwingZHalfMinAngle, mCosSwingZHalfMinAngle);
242 outClampedAxis |= cClampedSwingZMin;
243 }
244 else
245 {
246 ioSwing = Quat(0, 0, mSinSwingZHalfMaxAngle, mCosSwingZHalfMaxAngle);
247 outClampedAxis |= cClampedSwingZMax;
248 }
249 }
250 else if ((outClampedAxis & cClampedSwingYMin) != 0)
251 {
252 float z = ioSwing.GetZ();
253 ioSwing = Quat(0, 0, z, sqrt(1.0f - Square(z)));
254 }
255 }
256 }
257 else if (mRotationFlags & SwingZLocked)
258 {
259 // Swing Z angle disabled, only 1 degree of freedom in swing
260 outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0;
261 float delta_min = mSinSwingYHalfMinAngle - ioSwing.GetY();
262 float delta_max = ioSwing.GetY() - mSinSwingYHalfMaxAngle;
263 if (delta_min > 0.0f || delta_max > 0.0f)
264 {
265 // Pick the swing that corresponds to the smallest delta
266 if (sDistanceToMinShorter(delta_min, delta_max))
267 {
268 ioSwing = Quat(0, mSinSwingYHalfMinAngle, 0, mCosSwingYHalfMinAngle);
269 outClampedAxis |= cClampedSwingYMin;
270 }
271 else
272 {
273 ioSwing = Quat(0, mSinSwingYHalfMaxAngle, 0, mCosSwingYHalfMaxAngle);
274 outClampedAxis |= cClampedSwingYMax;
275 }
276 }
277 else if ((outClampedAxis & cClampedSwingZMin) != 0)
278 {
279 float y = ioSwing.GetY();
280 ioSwing = Quat(0, y, 0, sqrt(1.0f - Square(y)));
281 }
282 }
283 else
284 {
285 // Two degrees of freedom
286 if (mSwingType == ESwingType::Cone)
287 {
288 // Use ellipse to solve limits
289 Ellipse ellipse(mSinSwingYHalfMaxAngle, mSinSwingZHalfMaxAngle);
290 Float2 point(ioSwing.GetY(), ioSwing.GetZ());
291 if (!ellipse.IsInside(point))
292 {
293 Float2 closest = ellipse.GetClosestPoint(point);
294 ioSwing = Quat(0, closest.x, closest.y, sqrt(max(0.0f, 1.0f - Square(closest.x) - Square(closest.y))));
295 outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here
296 }
297 }
298 else
299 {
300 // Use pyramid to solve limits
301 // The quaternion rotating by angle y around the Y axis then rotating by angle z around the Z axis is:
302 // q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y)
303 // [q.x, q.y, q.z, q.w] = [-sin(y / 2) * sin(z / 2), sin(y / 2) * cos(z / 2), cos(y / 2) * sin(z / 2), cos(y / 2) * cos(z / 2)]
304 // So we can calculate y / 2 = atan2(q.y, q.w) and z / 2 = atan2(q.z, q.w)
305 Vec4 half_angle = Vec4::sATan2(ioSwing.GetXYZW().Swizzle<SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z>(), ioSwing.GetXYZW().SplatW());
306 Vec4 min_half_angle(mSwingYHalfMinAngle, mSwingYHalfMinAngle, mSwingZHalfMinAngle, mSwingZHalfMinAngle);
307 Vec4 max_half_angle(mSwingYHalfMaxAngle, mSwingYHalfMaxAngle, mSwingZHalfMaxAngle, mSwingZHalfMaxAngle);
308 Vec4 clamped_half_angle = Vec4::sMin(Vec4::sMax(half_angle, min_half_angle), max_half_angle);
309 UVec4 unclamped = Vec4::sEquals(half_angle, clamped_half_angle);
310 if (!unclamped.TestAllTrue())
311 {
312 // We now calculate the quaternion again using the formula for q above,
313 // but we leave out the x component in order to not introduce twist
314 Vec4 s, c;
315 clamped_half_angle.SinCos(s, c);
316 ioSwing = Quat(0, s.GetY() * c.GetZ(), c.GetY() * s.GetZ(), c.GetY() * c.GetZ()).Normalized();
317 outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here
318 }
319 }
320 }
321
322 // Flip sign back
323 if (negate_swing)
324 ioSwing = -ioSwing;
325 if (negate_twist)
326 ioTwist = -ioTwist;
327
328 JPH_ASSERT(ioSwing.IsNormalized());
329 JPH_ASSERT(ioTwist.IsNormalized());
330 }
331
337 inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, QuatArg inConstraintRotation, QuatArg inConstraintToWorld)
338 {
339 // Decompose into swing and twist
340 Quat q_swing, q_twist;
341 inConstraintRotation.GetSwingTwist(q_swing, q_twist);
342
343 // Clamp against joint limits
344 Quat q_clamped_swing = q_swing, q_clamped_twist = q_twist;
345 uint clamped_axis;
346 ClampSwingTwist(q_clamped_swing, q_clamped_twist, clamped_axis);
347
348 if (mRotationFlags & SwingYLocked)
349 {
350 Quat twist_to_world = inConstraintToWorld * q_swing;
351 mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY();
352 mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ();
353
354 if (mRotationFlags & SwingZLocked)
355 {
356 // Swing fully locked
357 mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
358 mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis);
359 }
360 else
361 {
362 // Swing only locked around Y
363 mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
364 if ((clamped_axis & (cClampedSwingZMin | cClampedSwingZMax)) != 0)
365 {
366 if ((clamped_axis & cClampedSwingZMin) != 0)
367 mWorldSpaceSwingLimitZRotationAxis = -mWorldSpaceSwingLimitZRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0]
368 mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis);
369 }
370 else
371 mSwingLimitZConstraintPart.Deactivate();
372 }
373 }
374 else if (mRotationFlags & SwingZLocked)
375 {
376 // Swing only locked around Z
377 Quat twist_to_world = inConstraintToWorld * q_swing;
378 mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY();
379 mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ();
380
381 if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax)) != 0)
382 {
383 if ((clamped_axis & cClampedSwingYMin) != 0)
384 mWorldSpaceSwingLimitYRotationAxis = -mWorldSpaceSwingLimitYRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0]
385 mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
386 }
387 else
388 mSwingLimitYConstraintPart.Deactivate();
389 mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis);
390 }
391 else if ((mRotationFlags & SwingYZFree) != SwingYZFree)
392 {
393 // Swing has limits around Y and Z
395 {
396 // Calculate axis of rotation from clamped swing to swing
397 Vec3 current = (inConstraintToWorld * q_swing).RotateAxisX();
398 Vec3 desired = (inConstraintToWorld * q_clamped_swing).RotateAxisX();
399 mWorldSpaceSwingLimitYRotationAxis = desired.Cross(current);
400 float len = mWorldSpaceSwingLimitYRotationAxis.Length();
401 if (len != 0.0f)
402 {
403 mWorldSpaceSwingLimitYRotationAxis /= len;
404 mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
405 }
406 else
407 mSwingLimitYConstraintPart.Deactivate();
408 }
409 else
410 mSwingLimitYConstraintPart.Deactivate();
411 mSwingLimitZConstraintPart.Deactivate();
412 }
413 else
414 {
415 // No swing limits
416 mSwingLimitYConstraintPart.Deactivate();
417 mSwingLimitZConstraintPart.Deactivate();
418 }
419
420 if (mRotationFlags & TwistXLocked)
421 {
422 // Twist locked, always activate constraint
423 mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX();
424 mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis);
425 }
426 else if ((mRotationFlags & TwistXFree) == 0)
427 {
428 // Twist has limits
429 if ((clamped_axis & (cClampedTwistMin | cClampedTwistMax)) != 0)
430 {
431 mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX();
432 if ((clamped_axis & cClampedTwistMin) != 0)
433 mWorldSpaceTwistLimitRotationAxis = -mWorldSpaceTwistLimitRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0]
434 mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis);
435 }
436 else
437 mTwistLimitConstraintPart.Deactivate();
438 }
439 else
440 {
441 // No twist limits
442 mTwistLimitConstraintPart.Deactivate();
443 }
444 }
445
448 {
449 mSwingLimitYConstraintPart.Deactivate();
450 mSwingLimitZConstraintPart.Deactivate();
451 mTwistLimitConstraintPart.Deactivate();
452 }
453
455 inline bool IsActive() const
456 {
457 return mSwingLimitYConstraintPart.IsActive() || mSwingLimitZConstraintPart.IsActive() || mTwistLimitConstraintPart.IsActive();
458 }
459
461 inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
462 {
463 mSwingLimitYConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio);
464 mSwingLimitZConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio);
465 mTwistLimitConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio);
466 }
467
469 inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2)
470 {
471 bool impulse = false;
472
473 // Solve swing constraint
474 if (mSwingLimitYConstraintPart.IsActive())
475 impulse |= mSwingLimitYConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitYRotationAxis, -FLT_MAX, mSinSwingYHalfMinAngle == mSinSwingYHalfMaxAngle? FLT_MAX : 0.0f);
476
477 if (mSwingLimitZConstraintPart.IsActive())
478 impulse |= mSwingLimitZConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitZRotationAxis, -FLT_MAX, mSinSwingZHalfMinAngle == mSinSwingZHalfMaxAngle? FLT_MAX : 0.0f);
479
480 // Solve twist constraint
481 if (mTwistLimitConstraintPart.IsActive())
482 impulse |= mTwistLimitConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceTwistLimitRotationAxis, -FLT_MAX, mSinTwistHalfMinAngle == mSinTwistHalfMaxAngle? FLT_MAX : 0.0f);
483
484 return impulse;
485 }
486
493 inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inConstraintRotation, QuatArg inConstraintToBody1, QuatArg inConstraintToBody2, float inBaumgarte) const
494 {
495 Quat q_swing, q_twist;
496 inConstraintRotation.GetSwingTwist(q_swing, q_twist);
497
498 uint clamped_axis;
499 ClampSwingTwist(q_swing, q_twist, clamped_axis);
500
501 // Solve rotation violations
502 if (clamped_axis != 0)
503 {
505 Quat inv_initial_orientation = inConstraintToBody2 * (inConstraintToBody1 * q_swing * q_twist).Conjugated();
506 part.CalculateConstraintProperties(ioBody1, Mat44::sRotation(ioBody1.GetRotation()), ioBody2, Mat44::sRotation(ioBody2.GetRotation()));
507 return part.SolvePositionConstraint(ioBody1, ioBody2, inv_initial_orientation, inBaumgarte);
508 }
509
510 return false;
511 }
512
514 inline float GetTotalSwingYLambda() const
515 {
516 return mSwingLimitYConstraintPart.GetTotalLambda();
517 }
518
519 inline float GetTotalSwingZLambda() const
520 {
521 return mSwingLimitZConstraintPart.GetTotalLambda();
522 }
523
525 inline float GetTotalTwistLambda() const
526 {
527 return mTwistLimitConstraintPart.GetTotalLambda();
528 }
529
531 void SaveState(StateRecorder &inStream) const
532 {
533 mSwingLimitYConstraintPart.SaveState(inStream);
534 mSwingLimitZConstraintPart.SaveState(inStream);
535 mTwistLimitConstraintPart.SaveState(inStream);
536 }
537
540 {
541 mSwingLimitYConstraintPart.RestoreState(inStream);
542 mSwingLimitZConstraintPart.RestoreState(inStream);
543 mTwistLimitConstraintPart.RestoreState(inStream);
544 }
545
546private:
547 // CONFIGURATION PROPERTIES FOLLOW
548
549 enum ERotationFlags
550 {
552 TwistXLocked = 1 << 0,
553 SwingYLocked = 1 << 1,
554 SwingZLocked = 1 << 2,
555
557 TwistXFree = 1 << 3,
558 SwingYFree = 1 << 4,
559 SwingZFree = 1 << 5,
560 SwingYZFree = SwingYFree | SwingZFree
561 };
562
563 uint8 mRotationFlags;
564
565 // Constants
566 ESwingType mSwingType = ESwingType::Cone;
567 float mSinTwistHalfMinAngle;
568 float mSinTwistHalfMaxAngle;
569 float mCosTwistHalfMinAngle;
570 float mCosTwistHalfMaxAngle;
571 float mSwingYHalfMinAngle;
572 float mSwingYHalfMaxAngle;
573 float mSwingZHalfMinAngle;
574 float mSwingZHalfMaxAngle;
575 float mSinSwingYHalfMinAngle;
576 float mSinSwingYHalfMaxAngle;
577 float mSinSwingZHalfMinAngle;
578 float mSinSwingZHalfMaxAngle;
579 float mCosSwingYHalfMinAngle;
580 float mCosSwingYHalfMaxAngle;
581 float mCosSwingZHalfMinAngle;
582 float mCosSwingZHalfMaxAngle;
583
584 // RUN TIME PROPERTIES FOLLOW
585
587 Vec3 mWorldSpaceSwingLimitYRotationAxis;
588 Vec3 mWorldSpaceSwingLimitZRotationAxis;
589 Vec3 mWorldSpaceTwistLimitRotationAxis;
590
592 AngleConstraintPart mSwingLimitYConstraintPart;
593 AngleConstraintPart mSwingLimitZConstraintPart;
594 AngleConstraintPart mTwistLimitConstraintPart;
595};
596
std::uint8_t uint8
Definition Core.h:482
unsigned int uint
Definition Core.h:481
#define JPH_NAMESPACE_END
Definition Core.h:414
#define JPH_NAMESPACE_BEGIN
Definition Core.h:408
#define JPH_ASSERT(...)
Definition IssueReporting.h:33
JPH_INLINE constexpr T Square(T inV)
Square a value.
Definition Math.h:55
JPH_INLINE constexpr float DegreesToRadians(float inV)
Convert a value from degrees to radians.
Definition Math.h:16
ESwingType
How the swing limit behaves.
Definition SwingTwistConstraintPart.h:15
@ Pyramid
Swing is limited by a pyramid shape, note that this pyramid starts to deform for larger swing angles.
@ Cone
Swing is limited by a cone shape, note that this cone starts to deform for larger swing angles....
@ SWIZZLE_Z
Use the Z component.
Definition Swizzle.h:14
@ SWIZZLE_Y
Use the Y component.
Definition Swizzle.h:13
Definition AngleConstraintPart.h:37
void Deactivate()
Deactivate this constraint.
Definition AngleConstraintPart.h:148
bool IsActive() const
Check if constraint is active.
Definition AngleConstraintPart.h:155
void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias=0.0f)
Definition AngleConstraintPart.h:81
void RestoreState(StateRecorder &inStream)
Restore state of this constraint part.
Definition AngleConstraintPart.h:244
void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
Definition AngleConstraintPart.h:164
bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
Definition AngleConstraintPart.h:176
void SaveState(StateRecorder &inStream) const
Save state of this constraint part.
Definition AngleConstraintPart.h:238
float GetTotalLambda() const
Return lagrange multiplier.
Definition AngleConstraintPart.h:190
Definition Body.h:35
Quat GetRotation() const
World space rotation of the body.
Definition Body.h:267
Definition Ellipse.h:14
Float2 GetClosestPoint(const Float2 &inPoint) const
Definition Ellipse.h:30
bool IsInside(const Float2 &inPoint) const
Check if inPoint is inside the ellipse.
Definition Ellipse.h:22
Class that holds 2 floats, used as a storage class mainly.
Definition Float2.h:11
float y
Definition Float2.h:31
float x
Definition Float2.h:30
static JPH_INLINE Mat44 sRotation(Vec3Arg inAxis, float inAngle)
Rotate around arbitrary axis.
Definition Mat44.inl:139
Definition Quat.h:33
JPH_INLINE float GetW() const
Get W component (real part)
Definition Quat.h:78
JPH_INLINE float GetY() const
Get Y component (imaginary part j)
Definition Quat.h:72
JPH_INLINE float GetZ() const
Get Z component (imaginary part k)
Definition Quat.h:75
JPH_INLINE float GetX() const
Get X component (imaginary part i)
Definition Quat.h:69
static JPH_INLINE Quat sIdentity()
Definition Quat.h:103
JPH_INLINE Vec4 GetXYZW() const
Get the quaternion as a Vec4.
Definition Quat.h:84
JPH_INLINE void GetSwingTwist(Quat &outSwing, Quat &outTwist) const
Definition Quat.inl:215
JPH_INLINE Quat Normalized() const
Normalize the quaternion (make it length 1)
Definition Quat.h:139
JPH_INLINE Vec3 RotateAxisZ() const
Rotate a the vector (0, 0, 1) with this quaternion.
Definition Quat.inl:306
JPH_INLINE Vec3 RotateAxisY() const
Rotate a the vector (0, 1, 0) with this quaternion.
Definition Quat.inl:297
bool IsNormalized(float inTolerance=1.0e-5f) const
If the length of this quaternion is 1 +/- inTolerance.
Definition Quat.h:59
Definition RotationEulerConstraintPart.h:36
bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const
Iteratively update the position constraint. Makes sure C(...) = 0.
Definition RotationEulerConstraintPart.h:182
void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2)
Calculate properties used during the functions below.
Definition RotationEulerConstraintPart.h:139
Definition StateRecorder.h:110
Definition SwingTwistConstraintPart.h:33
float GetTotalSwingYLambda() const
Return lagrange multiplier for swing.
Definition SwingTwistConstraintPart.h:514
ESwingType GetSwingType() const
Get the swing type for this part.
Definition SwingTwistConstraintPart.h:42
void RestoreState(StateRecorder &inStream)
Restore state of this constraint part.
Definition SwingTwistConstraintPart.h:539
float GetTotalSwingZLambda() const
Definition SwingTwistConstraintPart.h:519
float GetTotalTwistLambda() const
Return lagrange multiplier for twist.
Definition SwingTwistConstraintPart.h:525
static constexpr uint cClampedSwingYMax
Definition SwingTwistConstraintPart.h:155
void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses.
Definition SwingTwistConstraintPart.h:461
static constexpr uint cClampedTwistMin
Flags to indicate which axis got clamped by ClampSwingTwist.
Definition SwingTwistConstraintPart.h:152
void ClampSwingTwist(Quat &ioSwing, Quat &ioTwist, uint &outClampedAxis) const
Clamp twist and swing against the constraint limits, returns which parts were clamped (everything ass...
Definition SwingTwistConstraintPart.h:175
static JPH_INLINE bool sDistanceToMinShorter(float inDeltaMin, float inDeltaMax)
Helper function to determine if we're clamped against the min or max limit.
Definition SwingTwistConstraintPart.h:160
static constexpr uint cClampedTwistMax
Definition SwingTwistConstraintPart.h:153
static constexpr uint cClampedSwingZMin
Definition SwingTwistConstraintPart.h:156
void SetSwingType(ESwingType inSwingType)
Override the swing type.
Definition SwingTwistConstraintPart.h:36
void SaveState(StateRecorder &inStream) const
Save state of this constraint part.
Definition SwingTwistConstraintPart.h:531
static constexpr uint cClampedSwingYMin
Definition SwingTwistConstraintPart.h:154
void SetLimits(float inTwistMinAngle, float inTwistMaxAngle, float inSwingYMinAngle, float inSwingYMaxAngle, float inSwingZMinAngle, float inSwingZMaxAngle)
Set limits for this constraint (see description above for parameters)
Definition SwingTwistConstraintPart.h:48
void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, QuatArg inConstraintRotation, QuatArg inConstraintToWorld)
Definition SwingTwistConstraintPart.h:337
static constexpr uint cClampedSwingZMax
Definition SwingTwistConstraintPart.h:157
bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2)
Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equ...
Definition SwingTwistConstraintPart.h:469
void Deactivate()
Deactivate this constraint.
Definition SwingTwistConstraintPart.h:447
bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inConstraintRotation, QuatArg inConstraintToBody1, QuatArg inConstraintToBody2, float inBaumgarte) const
Definition SwingTwistConstraintPart.h:493
bool IsActive() const
Check if constraint is active.
Definition SwingTwistConstraintPart.h:455
Definition UVec4.h:12
JPH_INLINE bool TestAllTrue() const
Test if all components are true (true is when highest bit of component is set)
Definition UVec4.inl:408
Definition Vec3.h:17
JPH_INLINE Vec3 Cross(Vec3Arg inV2) const
Cross product.
Definition Vec3.inl:590
JPH_INLINE float Length() const
Length of vector.
Definition Vec3.inl:677
Definition Vec4.h:14
static Vec4 sATan2(Vec4Arg inY, Vec4Arg inX)
Calculate the arc tangent of y / x using the signs of the arguments to determine the correct quadrant...
Definition Vec4.inl:948
JPH_INLINE float GetW() const
Definition Vec4.h:116
static JPH_INLINE UVec4 sEquals(Vec4Arg inV1, Vec4Arg inV2)
Equals (component wise)
Definition Vec4.inl:166
static JPH_INLINE Vec4 sMin(Vec4Arg inV1, Vec4Arg inV2)
Return the minimum value of each of the components.
Definition Vec4.inl:138
JPH_INLINE Vec4 SplatW() const
Replicate the W component to all components.
Definition Vec4.inl:591
JPH_INLINE float GetX() const
Get individual components.
Definition Vec4.h:113
JPH_INLINE Vec4 Swizzle() const
Swizzle the elements in inV.
static JPH_INLINE Vec4 sMax(Vec4Arg inV1, Vec4Arg inV2)
Return the maximum of each of the components.
Definition Vec4.inl:152
JPH_INLINE float GetZ() const
Definition Vec4.h:115
JPH_INLINE float GetY() const
Definition Vec4.h:114
void SinCos(Vec4 &outSin, Vec4 &outCos) const
Calculate the sine and cosine for each element of this vector (input in radians)
Definition Vec4.inl:778