fix some bugs in RotationConstraints

This commit is contained in:
Andrew Meadows 2015-09-08 18:20:03 -07:00
parent b6cef3d1a9
commit 3d661095dc
5 changed files with 113 additions and 25 deletions

View file

@ -15,7 +15,6 @@
#include <NumericalConstants.h> #include <NumericalConstants.h>
ElbowConstraint::ElbowConstraint() : ElbowConstraint::ElbowConstraint() :
_referenceRotation(),
_minAngle(-PI), _minAngle(-PI),
_maxAngle(PI) _maxAngle(PI)
{ {

View file

@ -15,12 +15,10 @@
class ElbowConstraint : public RotationConstraint { class ElbowConstraint : public RotationConstraint {
public: public:
ElbowConstraint(); ElbowConstraint();
virtual void setReferenceRotation(const glm::quat& rotation) override { _referenceRotation = rotation; }
void setHingeAxis(const glm::vec3& axis); void setHingeAxis(const glm::vec3& axis);
void setAngleLimits(float minAngle, float maxAngle); void setAngleLimits(float minAngle, float maxAngle);
virtual bool apply(glm::quat& rotation) const override; virtual bool apply(glm::quat& rotation) const override;
protected: protected:
glm::quat _referenceRotation;
glm::vec3 _axis; glm::vec3 _axis;
glm::vec3 _perpAxis; glm::vec3 _perpAxis;
float _minAngle; float _minAngle;

View file

@ -15,15 +15,21 @@
class RotationConstraint { class RotationConstraint {
public: public:
RotationConstraint() {} RotationConstraint() : _referenceRotation() {}
virtual ~RotationConstraint() {} virtual ~RotationConstraint() {}
/// \param rotation the default rotation that represents /// \param referenceRotation the rotation from which rotation changes are measured.
virtual void setReferenceRotation(const glm::quat& rotation) = 0; virtual void setReferenceRotation(const glm::quat& rotation) { _referenceRotation = rotation; }
/// \return the rotation from which rotation changes are measured.
const glm::quat& getReferenceRotation() const { return _referenceRotation; }
/// \param rotation rotation to clamp /// \param rotation rotation to clamp
/// \return true if rotation is clamped /// \return true if rotation is clamped
virtual bool apply(glm::quat& rotation) const = 0; virtual bool apply(glm::quat& rotation) const = 0;
protected:
glm::quat _referenceRotation = glm::quat();
}; };
#endif // hifi_RotationConstraint_h #endif // hifi_RotationConstraint_h

View file

@ -32,14 +32,20 @@ void SwingTwistConstraint::SwingLimitFunction::setCone(float maxAngle) {
} }
void SwingTwistConstraint::SwingLimitFunction::setMinDots(const std::vector<float>& minDots) { void SwingTwistConstraint::SwingLimitFunction::setMinDots(const std::vector<float>& minDots) {
int numDots = minDots.size(); uint32_t numDots = minDots.size();
_minDots.clear(); _minDots.clear();
_minDots.reserve(numDots); if (numDots == 0) {
for (int i = 0; i < numDots; ++i) { // push two copies of MIN_MINDOT
_minDots.push_back(glm::clamp(minDots[i], MIN_MINDOT, MAX_MINDOT)); _minDots.push_back(MIN_MINDOT);
_minDots.push_back(MIN_MINDOT);
} else {
_minDots.reserve(numDots);
for (uint32_t i = 0; i < numDots; ++i) {
_minDots.push_back(glm::clamp(minDots[i], MIN_MINDOT, MAX_MINDOT));
}
// push the first value to the back to establish cyclic boundary conditions
_minDots.push_back(_minDots[0]);
} }
// push the first value to the back to establish cyclic boundary conditions
_minDots.push_back(_minDots[0]);
} }
float SwingTwistConstraint::SwingLimitFunction::getMinDot(float theta) const { float SwingTwistConstraint::SwingLimitFunction::getMinDot(float theta) const {
@ -58,8 +64,8 @@ float SwingTwistConstraint::SwingLimitFunction::getMinDot(float theta) const {
} }
SwingTwistConstraint::SwingTwistConstraint() : SwingTwistConstraint::SwingTwistConstraint() :
RotationConstraint(),
_swingLimitFunction(), _swingLimitFunction(),
_referenceRotation(),
_minTwist(-PI), _minTwist(-PI),
_maxTwist(PI) _maxTwist(PI)
{ {
@ -69,6 +75,85 @@ void SwingTwistConstraint::setSwingLimits(std::vector<float> minDots) {
_swingLimitFunction.setMinDots(minDots); _swingLimitFunction.setMinDots(minDots);
} }
void SwingTwistConstraint::setSwingLimits(const std::vector<glm::vec3>& swungDirections) {
struct SwingLimitData {
SwingLimitData() : _theta(0.0f), _minDot(1.0f) {}
SwingLimitData(float theta, float minDot) : _theta(theta), _minDot(minDot) {}
float _theta;
float _minDot;
bool operator<(const SwingLimitData& other) const { return _theta < other._theta; }
};
std::vector<SwingLimitData> limits;
uint32_t numLimits = swungDirections.size();
limits.reserve(numLimits);
// compute the limit pairs: <theta, minDot>
const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f);
for (uint32_t i = 0; i < numLimits; ++i) {
float directionLength = glm::length(swungDirections[i]);
if (directionLength > EPSILON) {
glm::vec3 swingAxis = glm::cross(yAxis, swungDirections[i]);
float theta = atan2f(-swingAxis.z, swingAxis.x);
if (theta < 0.0f) {
theta += TWO_PI;
}
limits.push_back(SwingLimitData(theta, swungDirections[i].y / directionLength));
}
}
std::vector<float> minDots;
numLimits = limits.size();
if (numLimits == 0) {
// trivial case: nearly free constraint
std::vector<float> minDots;
_swingLimitFunction.setMinDots(minDots);
} else if (numLimits == 1) {
// trivial case: uniform conical constraint
std::vector<float> minDots;
minDots.push_back(limits[0]._minDot);
_swingLimitFunction.setMinDots(minDots);
} else {
// interesting case: potentially non-uniform constraints
// sort limits by theta
std::sort(limits.begin(), limits.end());
// extrapolate evenly distributed limits for fast lookup table
float deltaTheta = TWO_PI / (float)(numLimits);
uint32_t rightIndex = 0;
for (uint32_t i = 0; i < numLimits; ++i) {
float theta = (float)i * deltaTheta;
uint32_t leftIndex = (rightIndex - 1) % numLimits;
while (rightIndex < numLimits && theta > limits[rightIndex]._theta) {
leftIndex = rightIndex++;
}
if (leftIndex == numLimits - 1) {
// we straddle the boundary
rightIndex = 0;
}
float rightTheta = limits[rightIndex]._theta;
float leftTheta = limits[leftIndex]._theta;
if (leftTheta > rightTheta) {
// we straddle the boundary, but we need to figure out which way to stride
// in order to keep theta between left and right
if (leftTheta > theta) {
leftTheta -= TWO_PI;
} else {
rightTheta += TWO_PI;
}
}
// blend between the left and right minDots to get the value that corresponds to this theta
float rightWeight = (theta - leftTheta) / (rightTheta - leftTheta);
minDots.push_back((1.0f - rightWeight) * limits[leftIndex]._minDot + rightWeight * limits[rightIndex]._minDot);
}
}
_swingLimitFunction.setMinDots(minDots);
}
void SwingTwistConstraint::setTwistLimits(float minTwist, float maxTwist) { void SwingTwistConstraint::setTwistLimits(float minTwist, float maxTwist) {
// NOTE: min/maxTwist angles should be in the range [-PI, PI] // NOTE: min/maxTwist angles should be in the range [-PI, PI]
_minTwist = glm::min(minTwist, maxTwist); _minTwist = glm::min(minTwist, maxTwist);
@ -107,13 +192,10 @@ bool SwingTwistConstraint::apply(glm::quat& rotation) const {
float theta = atan2f(-swingAxis.z, swingAxis.x); float theta = atan2f(-swingAxis.z, swingAxis.x);
float minDot = _swingLimitFunction.getMinDot(theta); float minDot = _swingLimitFunction.getMinDot(theta);
if (glm::dot(swungY, yAxis) < minDot) { if (glm::dot(swungY, yAxis) < minDot) {
// The swing limits are violated so we use the maxAngle to supply a new rotation. // The swing limits are violated so we extract the angle from midDot and
float maxAngle = acosf(minDot); // use it to supply a new rotation.
if (minDot < 0.0f) {
maxAngle = PI - maxAngle;
}
swingAxis /= axisLength; swingAxis /= axisLength;
swingRotation = glm::angleAxis(maxAngle, swingAxis); swingRotation = glm::angleAxis(acosf(minDot), swingAxis);
swingWasClamped = true; swingWasClamped = true;
} }
} }

View file

@ -16,7 +16,7 @@
#include <math.h> #include <math.h>
class SwingTwistConstraint : RotationConstraint { class SwingTwistConstraint : public RotationConstraint {
public: public:
// The SwingTwistConstraint starts in the "referenceRotation" and then measures an initial twist // The SwingTwistConstraint starts in the "referenceRotation" and then measures an initial twist
// about the yAxis followed by a swing about some axis that lies in the XZ plane, such that the twist // about the yAxis followed by a swing about some axis that lies in the XZ plane, such that the twist
@ -25,10 +25,7 @@ public:
SwingTwistConstraint(); SwingTwistConstraint();
/// \param referenceRotation the rotation from which rotation changes are measured. /// \param minDots vector of minimum dot products between the twist and swung axes
virtual void setReferenceRotation(const glm::quat& referenceRotation) override { _referenceRotation = referenceRotation; }
/// \param minDots vector of minimum dot products between the twist and swung axes.
/// \brief The values are minimum dot-products between the twist axis and the swung axes /// \brief The values are minimum dot-products between the twist axis and the swung axes
/// that correspond to swing axes equally spaced around the XZ plane. Another way to /// that correspond to swing axes equally spaced around the XZ plane. Another way to
/// think about it is that the dot-products correspond to correspond to angles (theta) /// think about it is that the dot-products correspond to correspond to angles (theta)
@ -38,6 +35,13 @@ public:
/// description of how this works. /// description of how this works.
void setSwingLimits(std::vector<float> minDots); void setSwingLimits(std::vector<float> minDots);
/// \param swungDirections vector of directions that lie on the swing limit boundary
/// \brief For each swungDirection we compute the corresponding [theta, minDot] pair.
/// We expect the values of theta to NOT be uniformly spaced around the range [0, TWO_PI]
/// so we'll use the input set to extrapolate a lookup function of evenly spaced values.
void setSwingLimits(const std::vector<glm::vec3>& swungDirections);
/// \param minTwist the minimum angle of rotation about the twist axis /// \param minTwist the minimum angle of rotation about the twist axis
/// \param maxTwist the maximum angle of rotation about the twist axis /// \param maxTwist the maximum angle of rotation about the twist axis
void setTwistLimits(float minTwist, float maxTwist); void setTwistLimits(float minTwist, float maxTwist);
@ -71,7 +75,6 @@ public:
protected: protected:
SwingLimitFunction _swingLimitFunction; SwingLimitFunction _swingLimitFunction;
glm::quat _referenceRotation;
float _minTwist; float _minTwist;
float _maxTwist; float _maxTwist;
}; };