Files

130 lines
3.9 KiB
C++

#include "gamelib/animstack/coneconstraint.h"
#include <cmath>
namespace {
idQuat Multiply(const idQuat& lhs, const idQuat& rhs) {
return idQuat(
lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y,
lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z,
lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x,
lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z);
}
idVec3 ToForward(const idQuat& quaternion) {
return idVec3(
1.0f - 2.0f * (quaternion.y * quaternion.y
+ quaternion.z * quaternion.z),
2.0f * (quaternion.x * quaternion.y
- quaternion.w * quaternion.z),
2.0f * (quaternion.w * quaternion.y
+ quaternion.x * quaternion.z));
}
idVec3 ConstrainDirection(
const idVec3& center,
const idVec3& direction,
const float limitAngleDot,
const float halfLimitAngleCosine,
const float halfLimitAngleSine) {
const float dot = center.Dot(direction);
if (dot >= limitAngleDot) {
return direction;
}
idVec3 tangent = direction - center * dot;
if (tangent.NormalizeFast() == 0.0f) {
return center;
}
const float sine = 2.0f * halfLimitAngleSine
* halfLimitAngleCosine;
return center * limitAngleDot + tangent * sine;
}
} // namespace
idConeConstraint_Vec3::idConeConstraint_Vec3(
const idConeConstraint_Vec3& other) = default;
idConeConstraint_Quat::idConeConstraint_Quat(
const idConeConstraint_Quat& other) = default;
idConeConstraint_Vec3::idConeConstraint_Vec3(
const idVec3& center_,
const radians_t maxAngle_)
: current(center_)
, center(center_)
, limitAngleDot(std::cos(maxAngle_.value))
, halfLimitAngleCosine(std::cos(maxAngle_.value * 0.5f))
, halfLimitAngleSine(std::sin(maxAngle_.value * 0.5f)) {
}
void idConeConstraint_Vec3::RotateTo(
const idVec3& dir,
const float lerpRate) {
const idVec3 target = ConstrainDirection(
center,
dir,
limitAngleDot,
halfLimitAngleCosine,
halfLimitAngleSine);
current = current + (target - current) * lerpRate;
current.NormalizeFast();
}
idConeConstraint_Quat::idConeConstraint_Quat(
const idQuat& center_,
const radians_t maxAngle_)
: current(center_)
, center(center_)
, limitAngleDot(std::cos(maxAngle_.value))
, halfLimitAngleCosine(std::cos(maxAngle_.value * 0.5f))
, halfLimitAngleSine(std::sin(maxAngle_.value * 0.5f)) {
}
void idConeConstraint_Quat::RotateTo(
const idVec3& dir,
const float lerpRate) {
const idVec3 forward = ToForward(current);
const float forwardDot = dir.Dot(forward);
if (forwardDot > -1.0f && forwardDot < 1.0f) {
idVec3 blendedForward = forward
+ (dir - forward) * (lerpRate * 0.5f);
blendedForward.NormalizeFast();
const float rotationDot = blendedForward.Dot(forward);
const float rotationSine = rotationDot < 1.0f
? std::sqrt(1.0f - rotationDot * rotationDot)
: 0.0f;
idVec3 rotationAxis = blendedForward.Cross(forward);
rotationAxis.NormalizeFast();
const idQuat rotation(
rotationAxis.x * rotationSine,
rotationAxis.y * rotationSine,
rotationAxis.z * rotationSine,
rotationDot);
current = Multiply(current, rotation);
current.Normalize();
}
const idVec3 centerForward = ToForward(center);
const idVec3 currentForward = ToForward(current);
if (centerForward.Dot(currentForward) < limitAngleDot) {
idVec3 rotationAxis = currentForward.Cross(centerForward);
rotationAxis.NormalizeFast();
const idQuat rotation(
rotationAxis.x * halfLimitAngleSine,
rotationAxis.y * halfLimitAngleSine,
rotationAxis.z * halfLimitAngleSine,
halfLimitAngleCosine);
current = Multiply(center, rotation);
current.Normalize();
}
}