123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
- // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #pragma once
- #include <Jolt/Geometry/Ellipse.h>
- #include <Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h>
- #include <Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h>
- JPH_NAMESPACE_BEGIN
- /// How the swing limit behaves
- enum class ESwingType : uint8
- {
- Cone, ///< Swing is limited by a cone shape, note that this cone starts to deform for larger swing angles. Cone limits only support limits that are symmetric around 0.
- Pyramid, ///< Swing is limited by a pyramid shape, note that this pyramid starts to deform for larger swing angles.
- };
- /// Quaternion based constraint that decomposes the rotation in constraint space in swing and twist: q = q_swing * q_twist
- /// where q_swing.x = 0 and where q_twist.y = q_twist.z = 0
- ///
- /// - Rotation around the twist (x-axis) is within [inTwistMinAngle, inTwistMaxAngle].
- /// - Rotation around the swing axis (y and z axis) are limited to an ellipsoid in quaternion space formed by the equation:
- ///
- /// (q_swing.y / sin(inSwingYHalfAngle / 2))^2 + (q_swing.z / sin(inSwingZHalfAngle / 2))^2 <= 1
- ///
- /// Which roughly corresponds to an elliptic cone shape with major axis (inSwingYHalfAngle, inSwingZHalfAngle).
- ///
- /// In case inSwingYHalfAngle = 0, the rotation around Y will be constrained to 0 and the rotation around Z
- /// will be constrained between [-inSwingZHalfAngle, inSwingZHalfAngle]. Vice versa if inSwingZHalfAngle = 0.
- class SwingTwistConstraintPart
- {
- public:
- /// Override the swing type
- void SetSwingType(ESwingType inSwingType)
- {
- mSwingType = inSwingType;
- }
- /// Get the swing type for this part
- ESwingType GetSwingType() const
- {
- return mSwingType;
- }
- /// Set limits for this constraint (see description above for parameters)
- void SetLimits(float inTwistMinAngle, float inTwistMaxAngle, float inSwingYMinAngle, float inSwingYMaxAngle, float inSwingZMinAngle, float inSwingZMaxAngle)
- {
- constexpr float cLockedAngle = DegreesToRadians(0.5f);
- constexpr float cFreeAngle = DegreesToRadians(179.5f);
- // Assume sane input
- JPH_ASSERT(inTwistMinAngle <= inTwistMinAngle);
- JPH_ASSERT(inSwingYMinAngle <= inSwingYMaxAngle);
- JPH_ASSERT(inSwingZMinAngle <= inSwingZMaxAngle);
- JPH_ASSERT(inSwingYMinAngle >= -JPH_PI && inSwingYMaxAngle <= JPH_PI);
- JPH_ASSERT(inSwingZMinAngle >= -JPH_PI && inSwingZMaxAngle <= JPH_PI);
- // Calculate the sine and cosine of the half angles
- Vec4 half_twist = 0.5f * Vec4(inTwistMinAngle, inTwistMaxAngle, 0, 0);
- Vec4 twist_s, twist_c;
- half_twist.SinCos(twist_s, twist_c);
- Vec4 half_swing = 0.5f * Vec4(inSwingYMinAngle, inSwingYMaxAngle, inSwingZMinAngle, inSwingZMaxAngle);
- Vec4 swing_s, swing_c;
- half_swing.SinCos(swing_s, swing_c);
- // Store half angles for pyramid limit
- mSwingYHalfMinAngle = half_swing.GetX();
- mSwingYHalfMaxAngle = half_swing.GetY();
- mSwingZHalfMinAngle = half_swing.GetZ();
- mSwingZHalfMaxAngle = half_swing.GetW();
- // Store axis flags which are used at runtime to quickly decided which contraints to apply
- mRotationFlags = 0;
- if (inTwistMinAngle > -cLockedAngle && inTwistMaxAngle < cLockedAngle)
- {
- mRotationFlags |= TwistXLocked;
- mSinTwistHalfMinAngle = 0.0f;
- mSinTwistHalfMaxAngle = 0.0f;
- mCosTwistHalfMinAngle = 1.0f;
- mCosTwistHalfMaxAngle = 1.0f;
- }
- else if (inTwistMinAngle < -cFreeAngle && inTwistMaxAngle > cFreeAngle)
- {
- mRotationFlags |= TwistXFree;
- mSinTwistHalfMinAngle = -1.0f;
- mSinTwistHalfMaxAngle = 1.0f;
- mCosTwistHalfMinAngle = 0.0f;
- mCosTwistHalfMaxAngle = 0.0f;
- }
- else
- {
- mSinTwistHalfMinAngle = twist_s.GetX();
- mSinTwistHalfMaxAngle = twist_s.GetY();
- mCosTwistHalfMinAngle = twist_c.GetX();
- mCosTwistHalfMaxAngle = twist_c.GetY();
- }
- if (inSwingYMinAngle > -cLockedAngle && inSwingYMaxAngle < cLockedAngle)
- {
- mRotationFlags |= SwingYLocked;
- mSinSwingYHalfMinAngle = 0.0f;
- mSinSwingYHalfMaxAngle = 0.0f;
- mCosSwingYHalfMinAngle = 1.0f;
- mCosSwingYHalfMaxAngle = 1.0f;
- }
- else if (inSwingYMinAngle < -cFreeAngle && inSwingYMaxAngle > cFreeAngle)
- {
- mRotationFlags |= SwingYFree;
- mSinSwingYHalfMinAngle = -1.0f;
- mSinSwingYHalfMaxAngle = 1.0f;
- mCosSwingYHalfMinAngle = 0.0f;
- mCosSwingYHalfMaxAngle = 0.0f;
- }
- else
- {
- mSinSwingYHalfMinAngle = swing_s.GetX();
- mSinSwingYHalfMaxAngle = swing_s.GetY();
- mCosSwingYHalfMinAngle = swing_c.GetX();
- mCosSwingYHalfMaxAngle = swing_c.GetY();
- JPH_ASSERT(mSinSwingYHalfMinAngle <= mSinSwingYHalfMaxAngle);
- }
- if (inSwingZMinAngle > -cLockedAngle && inSwingZMaxAngle < cLockedAngle)
- {
- mRotationFlags |= SwingZLocked;
- mSinSwingZHalfMinAngle = 0.0f;
- mSinSwingZHalfMaxAngle = 0.0f;
- mCosSwingZHalfMinAngle = 1.0f;
- mCosSwingZHalfMaxAngle = 1.0f;
- }
- else if (inSwingZMinAngle < -cFreeAngle && inSwingZMaxAngle > cFreeAngle)
- {
- mRotationFlags |= SwingZFree;
- mSinSwingZHalfMinAngle = -1.0f;
- mSinSwingZHalfMaxAngle = 1.0f;
- mCosSwingZHalfMinAngle = 0.0f;
- mCosSwingZHalfMaxAngle = 0.0f;
- }
- else
- {
- mSinSwingZHalfMinAngle = swing_s.GetZ();
- mSinSwingZHalfMaxAngle = swing_s.GetW();
- mCosSwingZHalfMinAngle = swing_c.GetZ();
- mCosSwingZHalfMaxAngle = swing_c.GetW();
- JPH_ASSERT(mSinSwingZHalfMinAngle <= mSinSwingZHalfMaxAngle);
- }
- }
- /// Flags to indicate which axis got clamped by ClampSwingTwist
- static constexpr uint cClampedTwistMin = 1 << 0;
- static constexpr uint cClampedTwistMax = 1 << 1;
- static constexpr uint cClampedSwingYMin = 1 << 2;
- static constexpr uint cClampedSwingYMax = 1 << 3;
- static constexpr uint cClampedSwingZMin = 1 << 4;
- static constexpr uint cClampedSwingZMax = 1 << 5;
- /// Helper function to determine if we're clamped against the min or max limit
- static JPH_INLINE bool sDistanceToMinShorter(float inDeltaMin, float inDeltaMax)
- {
- // We're outside of the limits, get actual delta to min/max range
- // 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)
- // We should actually be working with angles rather than sin(angle / 2). When the difference is small the approximation is accurate, but
- // 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
- // to 180 rather than 0 (you'd expect anything > -90 to go to 0).
- inDeltaMin = abs(inDeltaMin);
- if (inDeltaMin > 1.0f) inDeltaMin = 2.0f - inDeltaMin;
- inDeltaMax = abs(inDeltaMax);
- if (inDeltaMax > 1.0f) inDeltaMax = 2.0f - inDeltaMax;
- return inDeltaMin < inDeltaMax;
- }
- /// Clamp twist and swing against the constraint limits, returns which parts were clamped (everything assumed in constraint space)
- inline void ClampSwingTwist(Quat &ioSwing, Quat &ioTwist, uint &outClampedAxis) const
- {
- // Start with not clamped
- outClampedAxis = 0;
- // Check that swing and twist quaternions don't contain rotations around the wrong axis
- JPH_ASSERT(ioSwing.GetX() == 0.0f);
- JPH_ASSERT(ioTwist.GetY() == 0.0f);
- JPH_ASSERT(ioTwist.GetZ() == 0.0f);
- // Ensure quaternions have w > 0
- bool negate_swing = ioSwing.GetW() < 0.0f;
- if (negate_swing)
- ioSwing = -ioSwing;
- bool negate_twist = ioTwist.GetW() < 0.0f;
- if (negate_twist)
- ioTwist = -ioTwist;
- if (mRotationFlags & TwistXLocked)
- {
- // Twist axis is locked, clamp whenever twist is not identity
- outClampedAxis |= ioTwist.GetX() != 0.0f? (cClampedTwistMin | cClampedTwistMax) : 0;
- ioTwist = Quat::sIdentity();
- }
- else if ((mRotationFlags & TwistXFree) == 0)
- {
- // Twist axis has limit, clamp whenever out of range
- float delta_min = mSinTwistHalfMinAngle - ioTwist.GetX();
- float delta_max = ioTwist.GetX() - mSinTwistHalfMaxAngle;
- if (delta_min > 0.0f || delta_max > 0.0f)
- {
- // Pick the twist that corresponds to the smallest delta
- if (sDistanceToMinShorter(delta_min, delta_max))
- {
- ioTwist = Quat(mSinTwistHalfMinAngle, 0, 0, mCosTwistHalfMinAngle);
- outClampedAxis |= cClampedTwistMin;
- }
- else
- {
- ioTwist = Quat(mSinTwistHalfMaxAngle, 0, 0, mCosTwistHalfMaxAngle);
- outClampedAxis |= cClampedTwistMax;
- }
- }
- }
- // Clamp swing
- if (mRotationFlags & SwingYLocked)
- {
- if (mRotationFlags & SwingZLocked)
- {
- // Both swing Y and Z are disabled, no degrees of freedom in swing
- outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0;
- outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0;
- ioSwing = Quat::sIdentity();
- }
- else
- {
- // Swing Y angle disabled, only 1 degree of freedom in swing
- outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0;
- float delta_min = mSinSwingZHalfMinAngle - ioSwing.GetZ();
- float delta_max = ioSwing.GetZ() - mSinSwingZHalfMaxAngle;
- if (delta_min > 0.0f || delta_max > 0.0f)
- {
- // Pick the swing that corresponds to the smallest delta
- if (sDistanceToMinShorter(delta_min, delta_max))
- {
- ioSwing = Quat(0, 0, mSinSwingZHalfMinAngle, mCosSwingZHalfMinAngle);
- outClampedAxis |= cClampedSwingZMin;
- }
- else
- {
- ioSwing = Quat(0, 0, mSinSwingZHalfMaxAngle, mCosSwingZHalfMaxAngle);
- outClampedAxis |= cClampedSwingZMax;
- }
- }
- else if ((outClampedAxis & cClampedSwingYMin) != 0)
- {
- float z = ioSwing.GetZ();
- ioSwing = Quat(0, 0, z, sqrt(1.0f - Square(z)));
- }
- }
- }
- else if (mRotationFlags & SwingZLocked)
- {
- // Swing Z angle disabled, only 1 degree of freedom in swing
- outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0;
- float delta_min = mSinSwingYHalfMinAngle - ioSwing.GetY();
- float delta_max = ioSwing.GetY() - mSinSwingYHalfMaxAngle;
- if (delta_min > 0.0f || delta_max > 0.0f)
- {
- // Pick the swing that corresponds to the smallest delta
- if (sDistanceToMinShorter(delta_min, delta_max))
- {
- ioSwing = Quat(0, mSinSwingYHalfMinAngle, 0, mCosSwingYHalfMinAngle);
- outClampedAxis |= cClampedSwingYMin;
- }
- else
- {
- ioSwing = Quat(0, mSinSwingYHalfMaxAngle, 0, mCosSwingYHalfMaxAngle);
- outClampedAxis |= cClampedSwingYMax;
- }
- }
- else if ((outClampedAxis & cClampedSwingZMin) != 0)
- {
- float y = ioSwing.GetY();
- ioSwing = Quat(0, y, 0, sqrt(1.0f - Square(y)));
- }
- }
- else
- {
- // Two degrees of freedom
- if (mSwingType == ESwingType::Cone)
- {
- // Use ellipse to solve limits
- Ellipse ellipse(mSinSwingYHalfMaxAngle, mSinSwingZHalfMaxAngle);
- Float2 point(ioSwing.GetY(), ioSwing.GetZ());
- if (!ellipse.IsInside(point))
- {
- Float2 closest = ellipse.GetClosestPoint(point);
- ioSwing = Quat(0, closest.x, closest.y, sqrt(max(0.0f, 1.0f - Square(closest.x) - Square(closest.y))));
- outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here
- }
- }
- else
- {
- // Use pyramid to solve limits
- // The quaterion rotating by angle y around the Y axis then rotating by angle z around the Z axis is:
- // q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y)
- // [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)]
- // So we can calculate y / 2 = atan2(q.y, q.w) and z / 2 = atan2(q.z, q.w)
- Vec4 half_angle = Vec4::sATan2(ioSwing.GetXYZW().Swizzle<SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z>(), ioSwing.GetXYZW().SplatW());
- Vec4 min_half_angle(mSwingYHalfMinAngle, mSwingYHalfMinAngle, mSwingZHalfMinAngle, mSwingZHalfMinAngle);
- Vec4 max_half_angle(mSwingYHalfMaxAngle, mSwingYHalfMaxAngle, mSwingZHalfMaxAngle, mSwingZHalfMaxAngle);
- Vec4 clamped_half_angle = Vec4::sMin(Vec4::sMax(half_angle, min_half_angle), max_half_angle);
- UVec4 unclamped = Vec4::sEquals(half_angle, clamped_half_angle);
- if (!unclamped.TestAllTrue())
- {
- // We now calculate the quaternion again using the formula for q above,
- // but we leave out the x component in order to not introduce twist
- Vec4 s, c;
- clamped_half_angle.SinCos(s, c);
- ioSwing = Quat(0, s.GetY() * c.GetZ(), c.GetY() * s.GetZ(), c.GetY() * c.GetZ()).Normalized();
- outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here
- }
- }
- }
- // Flip sign back
- if (negate_swing)
- ioSwing = -ioSwing;
- if (negate_twist)
- ioTwist = -ioTwist;
- JPH_ASSERT(ioSwing.IsNormalized());
- JPH_ASSERT(ioTwist.IsNormalized());
- }
- /// Calculate properties used during the functions below
- /// @param inBody1 The first body that this constraint is attached to
- /// @param inBody2 The second body that this constraint is attached to
- /// @param inConstraintRotation The current rotation of the constraint in constraint space
- /// @param inConstraintToWorld Rotates from constraint space into world space
- inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, QuatArg inConstraintRotation, QuatArg inConstraintToWorld)
- {
- // Decompose into swing and twist
- Quat q_swing, q_twist;
- inConstraintRotation.GetSwingTwist(q_swing, q_twist);
- // Clamp against joint limits
- Quat q_clamped_swing = q_swing, q_clamped_twist = q_twist;
- uint clamped_axis;
- ClampSwingTwist(q_clamped_swing, q_clamped_twist, clamped_axis);
- if (mRotationFlags & SwingYLocked)
- {
- Quat twist_to_world = inConstraintToWorld * q_swing;
- mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY();
- mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ();
- if (mRotationFlags & SwingZLocked)
- {
- // Swing fully locked
- mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
- mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis);
- }
- else
- {
- // Swing only locked around Y
- mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
- if ((clamped_axis & (cClampedSwingZMin | cClampedSwingZMax)) != 0)
- {
- if ((clamped_axis & cClampedSwingZMin) != 0)
- mWorldSpaceSwingLimitZRotationAxis = -mWorldSpaceSwingLimitZRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0]
- mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis);
- }
- else
- mSwingLimitZConstraintPart.Deactivate();
- }
- }
- else if (mRotationFlags & SwingZLocked)
- {
- // Swing only locked around Z
- Quat twist_to_world = inConstraintToWorld * q_swing;
- mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY();
- mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ();
- if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax)) != 0)
- {
- if ((clamped_axis & cClampedSwingYMin) != 0)
- mWorldSpaceSwingLimitYRotationAxis = -mWorldSpaceSwingLimitYRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0]
- mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
- }
- else
- mSwingLimitYConstraintPart.Deactivate();
- mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis);
- }
- else if ((mRotationFlags & SwingYZFree) != SwingYZFree)
- {
- // Swing has limits around Y and Z
- if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax)) != 0)
- {
- // Calculate axis of rotation from clamped swing to swing
- Vec3 current = (inConstraintToWorld * q_swing).RotateAxisX();
- Vec3 desired = (inConstraintToWorld * q_clamped_swing).RotateAxisX();
- mWorldSpaceSwingLimitYRotationAxis = desired.Cross(current);
- float len = mWorldSpaceSwingLimitYRotationAxis.Length();
- if (len != 0.0f)
- {
- mWorldSpaceSwingLimitYRotationAxis /= len;
- mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
- }
- else
- mSwingLimitYConstraintPart.Deactivate();
- }
- else
- mSwingLimitYConstraintPart.Deactivate();
- mSwingLimitZConstraintPart.Deactivate();
- }
- else
- {
- // No swing limits
- mSwingLimitYConstraintPart.Deactivate();
- mSwingLimitZConstraintPart.Deactivate();
- }
- if (mRotationFlags & TwistXLocked)
- {
- // Twist locked, always activate constraint
- mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX();
- mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis);
- }
- else if ((mRotationFlags & TwistXFree) == 0)
- {
- // Twist has limits
- if ((clamped_axis & (cClampedTwistMin | cClampedTwistMax)) != 0)
- {
- mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX();
- if ((clamped_axis & cClampedTwistMin) != 0)
- mWorldSpaceTwistLimitRotationAxis = -mWorldSpaceTwistLimitRotationAxis; // Flip axis if hittin min limit because the impulse limit is going to be between [-FLT_MAX, 0]
- mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis);
- }
- else
- mTwistLimitConstraintPart.Deactivate();
- }
- else
- {
- // No twist limits
- mTwistLimitConstraintPart.Deactivate();
- }
- }
- /// Deactivate this constraint
- void Deactivate()
- {
- mSwingLimitYConstraintPart.Deactivate();
- mSwingLimitZConstraintPart.Deactivate();
- mTwistLimitConstraintPart.Deactivate();
- }
- /// Check if constraint is active
- inline bool IsActive() const
- {
- return mSwingLimitYConstraintPart.IsActive() || mSwingLimitZConstraintPart.IsActive() || mTwistLimitConstraintPart.IsActive();
- }
- /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses
- inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
- {
- mSwingLimitYConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio);
- mSwingLimitZConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio);
- mTwistLimitConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio);
- }
- /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
- inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2)
- {
- bool impulse = false;
- // Solve swing constraint
- if (mSwingLimitYConstraintPart.IsActive())
- impulse |= mSwingLimitYConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitYRotationAxis, -FLT_MAX, mSinSwingYHalfMinAngle == mSinSwingYHalfMaxAngle? FLT_MAX : 0.0f);
- if (mSwingLimitZConstraintPart.IsActive())
- impulse |= mSwingLimitZConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitZRotationAxis, -FLT_MAX, mSinSwingZHalfMinAngle == mSinSwingZHalfMaxAngle? FLT_MAX : 0.0f);
- // Solve twist constraint
- if (mTwistLimitConstraintPart.IsActive())
- impulse |= mTwistLimitConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceTwistLimitRotationAxis, -FLT_MAX, mSinTwistHalfMinAngle == mSinTwistHalfMaxAngle? FLT_MAX : 0.0f);
- return impulse;
- }
- /// Iteratively update the position constraint. Makes sure C(...) = 0.
- /// @param ioBody1 The first body that this constraint is attached to
- /// @param ioBody2 The second body that this constraint is attached to
- /// @param inConstraintRotation The current rotation of the constraint in constraint space
- /// @param inConstraintToBody1 , inConstraintToBody2 Rotates from constraint space to body 1/2 space
- /// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
- inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inConstraintRotation, QuatArg inConstraintToBody1, QuatArg inConstraintToBody2, float inBaumgarte) const
- {
- Quat q_swing, q_twist;
- inConstraintRotation.GetSwingTwist(q_swing, q_twist);
- uint clamped_axis;
- ClampSwingTwist(q_swing, q_twist, clamped_axis);
- // Solve rotation violations
- if (clamped_axis != 0)
- {
- RotationEulerConstraintPart part;
- Quat inv_initial_orientation = inConstraintToBody2 * (inConstraintToBody1 * q_swing * q_twist).Conjugated();
- part.CalculateConstraintProperties(ioBody1, Mat44::sRotation(ioBody1.GetRotation()), ioBody2, Mat44::sRotation(ioBody2.GetRotation()));
- return part.SolvePositionConstraint(ioBody1, ioBody2, inv_initial_orientation, inBaumgarte);
- }
- return false;
- }
- /// Return lagrange multiplier for swing
- inline float GetTotalSwingYLambda() const
- {
- return mSwingLimitYConstraintPart.GetTotalLambda();
- }
- inline float GetTotalSwingZLambda() const
- {
- return mSwingLimitZConstraintPart.GetTotalLambda();
- }
- /// Return lagrange multiplier for twist
- inline float GetTotalTwistLambda() const
- {
- return mTwistLimitConstraintPart.GetTotalLambda();
- }
- /// Save state of this constraint part
- void SaveState(StateRecorder &inStream) const
- {
- mSwingLimitYConstraintPart.SaveState(inStream);
- mSwingLimitZConstraintPart.SaveState(inStream);
- mTwistLimitConstraintPart.SaveState(inStream);
- }
- /// Restore state of this constraint part
- void RestoreState(StateRecorder &inStream)
- {
- mSwingLimitYConstraintPart.RestoreState(inStream);
- mSwingLimitZConstraintPart.RestoreState(inStream);
- mTwistLimitConstraintPart.RestoreState(inStream);
- }
- private:
- // CONFIGURATION PROPERTIES FOLLOW
- enum ERotationFlags
- {
- /// Indicates that axis is completely locked (cannot rotate around this axis)
- TwistXLocked = 1 << 0,
- SwingYLocked = 1 << 1,
- SwingZLocked = 1 << 2,
- /// Indicates that axis is completely free (can rotate around without limits)
- TwistXFree = 1 << 3,
- SwingYFree = 1 << 4,
- SwingZFree = 1 << 5,
- SwingYZFree = SwingYFree | SwingZFree
- };
- uint8 mRotationFlags;
- // Constants
- ESwingType mSwingType = ESwingType::Cone;
- float mSinTwistHalfMinAngle;
- float mSinTwistHalfMaxAngle;
- float mCosTwistHalfMinAngle;
- float mCosTwistHalfMaxAngle;
- float mSwingYHalfMinAngle;
- float mSwingYHalfMaxAngle;
- float mSwingZHalfMinAngle;
- float mSwingZHalfMaxAngle;
- float mSinSwingYHalfMinAngle;
- float mSinSwingYHalfMaxAngle;
- float mSinSwingZHalfMinAngle;
- float mSinSwingZHalfMaxAngle;
- float mCosSwingYHalfMinAngle;
- float mCosSwingYHalfMaxAngle;
- float mCosSwingZHalfMinAngle;
- float mCosSwingZHalfMaxAngle;
- // RUN TIME PROPERTIES FOLLOW
- /// Rotation axis for the angle constraint parts
- Vec3 mWorldSpaceSwingLimitYRotationAxis;
- Vec3 mWorldSpaceSwingLimitZRotationAxis;
- Vec3 mWorldSpaceTwistLimitRotationAxis;
- /// The constraint parts
- AngleConstraintPart mSwingLimitYConstraintPart;
- AngleConstraintPart mSwingLimitZConstraintPart;
- AngleConstraintPart mTwistLimitConstraintPart;
- };
- JPH_NAMESPACE_END
|