|
@@ -10,6 +10,13 @@
|
|
|
|
|
|
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
|
|
|
+ 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
|
|
|
///
|
|
@@ -25,20 +32,44 @@ JPH_NAMESPACE_BEGIN
|
|
|
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 inSwingYHalfAngle, float inSwingZHalfAngle)
|
|
|
+ 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(inSwingYHalfAngle >= 0.0f && inSwingYHalfAngle <= JPH_PI);
|
|
|
- JPH_ASSERT(inSwingZHalfAngle >= 0.0f && inSwingZHalfAngle <= JPH_PI);
|
|
|
+ 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 s, c;
|
|
|
- (0.5f * Vec4(inTwistMinAngle, inTwistMaxAngle, inSwingYHalfAngle, inSwingZHalfAngle)).SinCos(s, c);
|
|
|
+ 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;
|
|
@@ -60,51 +91,91 @@ public:
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- mSinTwistHalfMinAngle = s.GetX();
|
|
|
- mSinTwistHalfMaxAngle = s.GetY();
|
|
|
- mCosTwistHalfMinAngle = c.GetX();
|
|
|
- mCosTwistHalfMaxAngle = c.GetY();
|
|
|
+ mSinTwistHalfMinAngle = twist_s.GetX();
|
|
|
+ mSinTwistHalfMaxAngle = twist_s.GetY();
|
|
|
+ mCosTwistHalfMinAngle = twist_c.GetX();
|
|
|
+ mCosTwistHalfMaxAngle = twist_c.GetY();
|
|
|
}
|
|
|
|
|
|
- if (inSwingYHalfAngle < cLockedAngle)
|
|
|
+ if (inSwingYMinAngle > -cLockedAngle && inSwingYMaxAngle < cLockedAngle)
|
|
|
{
|
|
|
mRotationFlags |= SwingYLocked;
|
|
|
- mSinSwingYQuarterAngle = 0.0f;
|
|
|
+ mSinSwingYHalfMinAngle = 0.0f;
|
|
|
+ mSinSwingYHalfMaxAngle = 0.0f;
|
|
|
+ mCosSwingYHalfMinAngle = 1.0f;
|
|
|
+ mCosSwingYHalfMaxAngle = 1.0f;
|
|
|
}
|
|
|
- else if (inSwingYHalfAngle > cFreeAngle)
|
|
|
+ else if (inSwingYMinAngle < -cFreeAngle && inSwingYMaxAngle > cFreeAngle)
|
|
|
{
|
|
|
mRotationFlags |= SwingYFree;
|
|
|
- mSinSwingYQuarterAngle = 1.0f;
|
|
|
+ mSinSwingYHalfMinAngle = -1.0f;
|
|
|
+ mSinSwingYHalfMaxAngle = 1.0f;
|
|
|
+ mCosSwingYHalfMinAngle = 0.0f;
|
|
|
+ mCosSwingYHalfMaxAngle = 0.0f;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- mSinSwingYQuarterAngle = s.GetZ();
|
|
|
+ mSinSwingYHalfMinAngle = swing_s.GetX();
|
|
|
+ mSinSwingYHalfMaxAngle = swing_s.GetY();
|
|
|
+ mCosSwingYHalfMinAngle = swing_c.GetX();
|
|
|
+ mCosSwingYHalfMaxAngle = swing_c.GetY();
|
|
|
+ JPH_ASSERT(mSinSwingYHalfMinAngle < mSinSwingYHalfMaxAngle);
|
|
|
}
|
|
|
|
|
|
- if (inSwingZHalfAngle < cLockedAngle)
|
|
|
+ if (inSwingZMinAngle > -cLockedAngle && inSwingZMaxAngle < cLockedAngle)
|
|
|
{
|
|
|
mRotationFlags |= SwingZLocked;
|
|
|
- mSinSwingZQuarterAngle = 0.0f;
|
|
|
+ mSinSwingZHalfMinAngle = 0.0f;
|
|
|
+ mSinSwingZHalfMaxAngle = 0.0f;
|
|
|
+ mCosSwingZHalfMinAngle = 1.0f;
|
|
|
+ mCosSwingZHalfMaxAngle = 1.0f;
|
|
|
}
|
|
|
- else if (inSwingZHalfAngle > cFreeAngle)
|
|
|
+ else if (inSwingZMinAngle < -cFreeAngle && inSwingZMaxAngle > cFreeAngle)
|
|
|
{
|
|
|
mRotationFlags |= SwingZFree;
|
|
|
- mSinSwingZQuarterAngle = 1.0f;
|
|
|
+ mSinSwingZHalfMinAngle = -1.0f;
|
|
|
+ mSinSwingZHalfMaxAngle = 1.0f;
|
|
|
+ mCosSwingZHalfMinAngle = 0.0f;
|
|
|
+ mCosSwingZHalfMaxAngle = 0.0f;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- mSinSwingZQuarterAngle = s.GetW();
|
|
|
+ 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, bool &outSwingYClamped, bool &outSwingZClamped, Quat &ioTwist, bool &outTwistClampedToMin, bool &outTwistClampedToMax) const
|
|
|
+ inline void ClampSwingTwist(Quat &ioSwing, Quat &ioTwist, uint &outClampedAxis) const
|
|
|
{
|
|
|
// Start with not clamped
|
|
|
- outTwistClampedToMin = false;
|
|
|
- outTwistClampedToMax = false;
|
|
|
- outSwingYClamped = false;
|
|
|
- outSwingZClamped = false;
|
|
|
+ outClampedAxis = 0;
|
|
|
|
|
|
// Check that swing and twist quaternions don't contain rotations around the wrong axis
|
|
|
JPH_ASSERT(ioSwing.GetX() == 0.0f);
|
|
@@ -122,11 +193,8 @@ public:
|
|
|
if (mRotationFlags & TwistXLocked)
|
|
|
{
|
|
|
// Twist axis is locked, clamp whenever twist is not identity
|
|
|
- if (ioTwist.GetX() != 0.0f)
|
|
|
- {
|
|
|
- ioTwist = Quat::sIdentity();
|
|
|
- outTwistClampedToMin = outTwistClampedToMax = true;
|
|
|
- }
|
|
|
+ outClampedAxis |= ioTwist.GetX() != 0.0f? (cClampedTwistMin | cClampedTwistMax) : 0;
|
|
|
+ ioTwist = Quat::sIdentity();
|
|
|
}
|
|
|
else if ((mRotationFlags & TwistXFree) == 0)
|
|
|
{
|
|
@@ -135,26 +203,16 @@ public:
|
|
|
float delta_max = ioTwist.GetX() - mSinTwistHalfMaxAngle;
|
|
|
if (delta_min > 0.0f || delta_max > 0.0f)
|
|
|
{
|
|
|
- // We're outside of the limits, get actual delta to min/max range
|
|
|
- // Note that a 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).
|
|
|
- delta_min = abs(delta_min);
|
|
|
- if (delta_min > 1.0f) delta_min = 2.0f - delta_min;
|
|
|
- delta_max = abs(delta_max);
|
|
|
- if (delta_max > 1.0f) delta_max = 2.0f - delta_max;
|
|
|
-
|
|
|
// Pick the twist that corresponds to the smallest delta
|
|
|
- if (delta_min < delta_max)
|
|
|
+ if (sDistanceToMinShorter(delta_min, delta_max))
|
|
|
{
|
|
|
ioTwist = Quat(mSinTwistHalfMinAngle, 0, 0, mCosTwistHalfMinAngle);
|
|
|
- outTwistClampedToMin = true;
|
|
|
+ outClampedAxis |= cClampedTwistMin;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
ioTwist = Quat(mSinTwistHalfMaxAngle, 0, 0, mCosTwistHalfMaxAngle);
|
|
|
- outTwistClampedToMax = true;
|
|
|
+ outClampedAxis |= cClampedTwistMax;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -165,41 +223,99 @@ public:
|
|
|
if (mRotationFlags & SwingZLocked)
|
|
|
{
|
|
|
// Both swing Y and Z are disabled, no degrees of freedom in swing
|
|
|
- outSwingYClamped = ioSwing.GetY() != 0.0f;
|
|
|
- outSwingZClamped = ioSwing.GetZ() != 0.0f;
|
|
|
- if (outSwingYClamped || outSwingZClamped)
|
|
|
- ioSwing = Quat::sIdentity();
|
|
|
+ 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
|
|
|
- float z = Clamp(ioSwing.GetZ(), -mSinSwingZQuarterAngle, mSinSwingZQuarterAngle);
|
|
|
- outSwingYClamped = ioSwing.GetY() != 0.0f;
|
|
|
- outSwingZClamped = z != ioSwing.GetZ();
|
|
|
- if (outSwingYClamped || outSwingZClamped)
|
|
|
+ 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
|
|
|
- float y = Clamp(ioSwing.GetY(), -mSinSwingYQuarterAngle, mSinSwingYQuarterAngle);
|
|
|
- outSwingYClamped = y != ioSwing.GetY();
|
|
|
- outSwingZClamped = ioSwing.GetZ() != 0.0f;
|
|
|
- if (outSwingYClamped || outSwingZClamped)
|
|
|
+ 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, use ellipse to solve limits
|
|
|
- Ellipse ellipse(mSinSwingYQuarterAngle, mSinSwingZQuarterAngle);
|
|
|
- Float2 point(ioSwing.GetY(), ioSwing.GetZ());
|
|
|
- if (!ellipse.IsInside(point))
|
|
|
+ // Two degrees of freedom
|
|
|
+ if (mSwingType == ESwingType::Cone)
|
|
|
{
|
|
|
- Float2 closest = ellipse.GetClosestPoint(point);
|
|
|
- ioSwing = Quat(0, closest.x, closest.y, sqrt(max(0.0f, 1.0f - Square(closest.x) - Square(closest.y))));
|
|
|
- outSwingYClamped = true;
|
|
|
- outSwingZClamped = true;
|
|
|
+ // 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
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -226,8 +342,8 @@ public:
|
|
|
|
|
|
// Clamp against joint limits
|
|
|
Quat q_clamped_swing = q_swing, q_clamped_twist = q_twist;
|
|
|
- bool swing_y_clamped, swing_z_clamped, twist_clamped_to_min, twist_clamped_to_max;
|
|
|
- ClampSwingTwist(q_clamped_swing, swing_y_clamped, swing_z_clamped, q_clamped_twist, twist_clamped_to_min, twist_clamped_to_max);
|
|
|
+ uint clamped_axis;
|
|
|
+ ClampSwingTwist(q_clamped_swing, q_clamped_twist, clamped_axis);
|
|
|
|
|
|
if (mRotationFlags & SwingYLocked)
|
|
|
{
|
|
@@ -245,10 +361,10 @@ public:
|
|
|
{
|
|
|
// Swing only locked around Y
|
|
|
mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis);
|
|
|
- if (swing_z_clamped)
|
|
|
+ if ((clamped_axis & (cClampedSwingZMin | cClampedSwingZMax)) != 0)
|
|
|
{
|
|
|
- if (Sign(q_swing.GetW()) * q_swing.GetZ() < 0.0f)
|
|
|
- mWorldSpaceSwingLimitZRotationAxis = -mWorldSpaceSwingLimitZRotationAxis; // Flip axis if angle is negative because the impulse limit is going to be between [-FLT_MAX, 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
|
|
@@ -262,10 +378,10 @@ public:
|
|
|
mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY();
|
|
|
mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ();
|
|
|
|
|
|
- if (swing_y_clamped)
|
|
|
+ if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax)) != 0)
|
|
|
{
|
|
|
- if (Sign(q_swing.GetW()) * q_swing.GetY() < 0.0f)
|
|
|
- mWorldSpaceSwingLimitYRotationAxis = -mWorldSpaceSwingLimitYRotationAxis; // Flip axis if angle is negative because the impulse limit is going to be between [-FLT_MAX, 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
|
|
@@ -275,7 +391,7 @@ public:
|
|
|
else if ((mRotationFlags & SwingYZFree) != SwingYZFree)
|
|
|
{
|
|
|
// Swing has limits around Y and Z
|
|
|
- if (swing_y_clamped || swing_z_clamped)
|
|
|
+ if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax)) != 0)
|
|
|
{
|
|
|
// Calculate axis of rotation from clamped swing to swing
|
|
|
Vec3 current = (inConstraintToWorld * q_swing).RotateAxisX();
|
|
@@ -310,10 +426,10 @@ public:
|
|
|
else if ((mRotationFlags & TwistXFree) == 0)
|
|
|
{
|
|
|
// Twist has limits
|
|
|
- if (twist_clamped_to_min || twist_clamped_to_max)
|
|
|
+ if ((clamped_axis & (cClampedTwistMin | cClampedTwistMax)) != 0)
|
|
|
{
|
|
|
mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX();
|
|
|
- if (twist_clamped_to_min)
|
|
|
+ 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);
|
|
|
}
|
|
@@ -379,11 +495,11 @@ public:
|
|
|
Quat q_swing, q_twist;
|
|
|
inConstraintRotation.GetSwingTwist(q_swing, q_twist);
|
|
|
|
|
|
- bool swing_y_clamped, swing_z_clamped, twist_clamped_to_min, twist_clamped_to_max;
|
|
|
- ClampSwingTwist(q_swing, swing_y_clamped, swing_z_clamped, q_twist, twist_clamped_to_min, twist_clamped_to_max);
|
|
|
+ uint clamped_axis;
|
|
|
+ ClampSwingTwist(q_swing, q_twist, clamped_axis);
|
|
|
|
|
|
// Solve rotation violations
|
|
|
- if (swing_y_clamped || swing_z_clamped || twist_clamped_to_min || twist_clamped_to_max)
|
|
|
+ if (clamped_axis != 0)
|
|
|
{
|
|
|
RotationEulerConstraintPart part;
|
|
|
Quat inv_initial_orientation = inConstraintToBody2 * (inConstraintToBody1 * q_swing * q_twist).Conjugated();
|
|
@@ -447,12 +563,23 @@ private:
|
|
|
uint8 mRotationFlags;
|
|
|
|
|
|
// Constants
|
|
|
+ ESwingType mSwingType = ESwingType::Cone;
|
|
|
float mSinTwistHalfMinAngle;
|
|
|
float mSinTwistHalfMaxAngle;
|
|
|
float mCosTwistHalfMinAngle;
|
|
|
float mCosTwistHalfMaxAngle;
|
|
|
- float mSinSwingYQuarterAngle;
|
|
|
- float mSinSwingZQuarterAngle;
|
|
|
+ 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
|
|
|
|