mirror of
https://github.com/JulianGro/overte.git
synced 2025-08-05 22:59:52 +02:00
fix some bugs in RotationConstraints
This commit is contained in:
parent
b6cef3d1a9
commit
3d661095dc
5 changed files with 113 additions and 25 deletions
|
@ -15,7 +15,6 @@
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
ElbowConstraint::ElbowConstraint() :
|
ElbowConstraint::ElbowConstraint() :
|
||||||
_referenceRotation(),
|
|
||||||
_minAngle(-PI),
|
_minAngle(-PI),
|
||||||
_maxAngle(PI)
|
_maxAngle(PI)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue