From e1d0beae1a7d5f287c7f41d34e291a7737de1576 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 8 May 2012 11:56:39 +0000 Subject: [PATCH] math: support all Tait-Bryan angle combinations in quaternions. --- src/lol/math/vector.h | 23 +++++++++++-- src/math/vector.cpp | 80 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/src/lol/math/vector.h b/src/lol/math/vector.h index 143c26dc..008ebb92 100644 --- a/src/lol/math/vector.h +++ b/src/lol/math/vector.h @@ -925,8 +925,27 @@ template struct Quat static Quat rotate(T angle, T x, T y, T z); static Quat rotate(T angle, Vec3 const &v); - static Quat fromeuler(T x, T y, T z); - static Quat fromeuler(Vec3 const &v); + + /* Convert from Tait-Bryan angles (incorrectly called Euler angles, + * but since everyone does it…). The axes in fromeuler_xyz are + * x, then y', then z", ie. the axes are attached to the model. + * If you want to apply yaw around x, pitch around y, and roll + * around z, use fromeuler_xyz. + * If you want to rotate around static axes, reverse the order in + * the function name (_zyx instead of _xyz) AND reverse the order + * of the arguments. */ + static Quat fromeuler_xyz(Vec3 const &v); + static Quat fromeuler_xzy(Vec3 const &v); + static Quat fromeuler_yxz(Vec3 const &v); + static Quat fromeuler_yzx(Vec3 const &v); + static Quat fromeuler_zxy(Vec3 const &v); + static Quat fromeuler_zyx(Vec3 const &v); + static Quat fromeuler_xyz(T phi, T theta, T psi); + static Quat fromeuler_xzy(T phi, T theta, T psi); + static Quat fromeuler_yxz(T phi, T theta, T psi); + static Quat fromeuler_yzx(T phi, T theta, T psi); + static Quat fromeuler_zxy(T phi, T theta, T psi); + static Quat fromeuler_zyx(T phi, T theta, T psi); inline Quat operator *(Quat const &val) const { diff --git a/src/math/vector.cpp b/src/math/vector.cpp index 58525c9e..13b9904b 100644 --- a/src/math/vector.cpp +++ b/src/math/vector.cpp @@ -538,25 +538,85 @@ template<> mat3 mat3::fromeuler(float x, float y, float z) return mat3::fromeuler(vec3(x, y, z)); } -template<> quat quat::fromeuler(vec3 const &v) +static inline quat fromeuler_generic(vec3 const &v, int s, int i, int j, int k) { using std::sin; using std::cos; vec3 half_angles = (M_PI / 360.0f) * v; - float sx = sin(half_angles.x), cx = cos(half_angles.x); - float sy = sin(half_angles.y), cy = cos(half_angles.y); - float sz = sin(half_angles.z), cz = cos(half_angles.z); + float s0 = sin(half_angles[0]), c0 = cos(half_angles[0]); + float s1 = sin(half_angles[1]), c1 = cos(half_angles[1]); + float s2 = sin(half_angles[2]), c2 = cos(half_angles[2]); - return quat(cx * cy * cz + sx * sy * sz, - sx * cy * cz - cx * sy * sz, - cx * sy * cz + sx * cy * sz, - cx * cy * sz - sx * sy * cz); + vec4 v1(c0 * c1 * c2, c0 * c1 * s2, c0 * s1 * c2, s0 * c1 * c2); + vec4 v2(s0 * s1 * s2, -s0 * s1 * c2, s0 * c1 * s2, -c0 * s1 * s2); + + if (s > 0) + v1 += v2; + else + v1 -= v2; + + return quat(v1[0], v1[i], v1[j], v1[k]); +} + +template<> quat quat::fromeuler_xyz(vec3 const &v) +{ + return fromeuler_generic(v, -1, 3, 2, 1); +} + +template<> quat quat::fromeuler_xzy(vec3 const &v) +{ + return fromeuler_generic(v, 1, 3, 1, 2); +} + +template<> quat quat::fromeuler_yxz(vec3 const &v) +{ + return fromeuler_generic(v, 1, 2, 3, 1); +} + +template<> quat quat::fromeuler_yzx(vec3 const &v) +{ + return fromeuler_generic(v, -1, 1, 3, 2); +} + +template<> quat quat::fromeuler_zxy(vec3 const &v) +{ + return fromeuler_generic(v, -1, 2, 1, 3); +} + +template<> quat quat::fromeuler_zyx(vec3 const &v) +{ + return fromeuler_generic(v, 1, 1, 2, 3); +} + +template<> quat quat::fromeuler_xyz(float phi, float theta, float psi) +{ + return quat::fromeuler_zyx(vec3(phi, theta, psi)); +} + +template<> quat quat::fromeuler_xzy(float phi, float theta, float psi) +{ + return quat::fromeuler_yxz(vec3(phi, theta, psi)); +} + +template<> quat quat::fromeuler_yxz(float phi, float theta, float psi) +{ + return quat::fromeuler_yxz(vec3(phi, theta, psi)); +} + +template<> quat quat::fromeuler_yzx(float phi, float theta, float psi) +{ + return quat::fromeuler_yxz(vec3(phi, theta, psi)); +} + +template<> quat quat::fromeuler_zxy(float phi, float theta, float psi) +{ + return quat::fromeuler_yxz(vec3(phi, theta, psi)); } -template<> quat quat::fromeuler(float x, float y, float z) +template<> quat quat::fromeuler_zyx(float phi, float theta, float psi) { - return quat::fromeuler(vec3(x, y, z)); + return quat::fromeuler_yxz(vec3(phi, theta, psi)); } template<> mat4 mat4::lookat(vec3 eye, vec3 center, vec3 up)