From 552dfee5b1252222b7b520a0691a514bf40a5e4f Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 8 May 2012 22:03:01 +0000 Subject: [PATCH] math: implement quaternion creation from true Euler angles (as opposed to the Tait-Bryan angles we had for now). Also, change quaternion storage order to wxyz in order to match the constructors. --- src/lol/math/vector.h | 41 ++++++++++----- src/math/vector.cpp | 117 +++++++++++++++++++----------------------- 2 files changed, 81 insertions(+), 77 deletions(-) diff --git a/src/lol/math/vector.h b/src/lol/math/vector.h index 7a708bbc..3fefd918 100644 --- a/src/lol/math/vector.h +++ b/src/lol/math/vector.h @@ -125,9 +125,9 @@ template struct XVec4 * Helper macro for vector type member functions */ -#define DECLARE_MEMBER_OPS(tname) \ - inline T& operator[](size_t n) { return *(&this->x + n); } \ - inline T const& operator[](size_t n) const { return *(&this->x + n); } \ +#define DECLARE_MEMBER_OPS(tname, first) \ + inline T& operator[](size_t n) { return *(&this->first + n); } \ + inline T const& operator[](size_t n) const { return *(&this->first + n); } \ \ /* Visual Studio insists on having an assignment operator. */ \ inline tname const & operator =(tname const &that) \ @@ -233,7 +233,7 @@ template struct Vec2 : BVec2 explicit inline Vec2(XVec2 const &v) : BVec2(v[0], v[1]) {} - DECLARE_MEMBER_OPS(Vec2) + DECLARE_MEMBER_OPS(Vec2, x) #if !defined __ANDROID__ template @@ -251,7 +251,7 @@ template struct Cmplx inline Cmplx(T X) : x(X), y(0) {} inline Cmplx(T X, T Y) : x(X), y(Y) {} - DECLARE_MEMBER_OPS(Cmplx) + DECLARE_MEMBER_OPS(Cmplx, x) inline Cmplx operator *(Cmplx const &val) const { @@ -491,7 +491,7 @@ template struct Vec3 : BVec3 static Vec3 toeuler(Quat const &q); - DECLARE_MEMBER_OPS(Vec3) + DECLARE_MEMBER_OPS(Vec3, x) #if !defined __ANDROID__ template @@ -900,7 +900,7 @@ template struct Vec4 : BVec4 explicit inline Vec4(XVec4 const &v) : BVec4(v[0], v[1], v[2], v[3]) {} - DECLARE_MEMBER_OPS(Vec4) + DECLARE_MEMBER_OPS(Vec4, x) #if !defined __ANDROID__ template @@ -915,17 +915,34 @@ template struct Vec4 : BVec4 template struct Quat { inline Quat() {} - inline Quat(T W) : x(0), y(0), z(0), w(W) {} - inline Quat(T W, T X, T Y, T Z) : x(X), y(Y), z(Z), w(W) {} + inline Quat(T W) : w(W), x(0), y(0), z(0) {} + inline Quat(T W, T X, T Y, T Z) : w(W), x(X), y(Y), z(Z) {} Quat(Mat3 const &m); Quat(Mat4 const &m); - DECLARE_MEMBER_OPS(Quat) + DECLARE_MEMBER_OPS(Quat, w) static Quat rotate(T angle, T x, T y, T z); static Quat rotate(T angle, Vec3 const &v); + /* Convert from Euler angles. The axes in fromeuler_xyx are + * x, then y', then x", ie. the axes are attached to the model. + * If you want to rotate around static axes, just reverse the order + * of the arguments. */ + static Quat fromeuler_xyx(Vec3 const &v); + static Quat fromeuler_xzx(Vec3 const &v); + static Quat fromeuler_yxy(Vec3 const &v); + static Quat fromeuler_yzy(Vec3 const &v); + static Quat fromeuler_zxz(Vec3 const &v); + static Quat fromeuler_zyz(Vec3 const &v); + static Quat fromeuler_xyx(T phi, T theta, T psi); + static Quat fromeuler_xzx(T phi, T theta, T psi); + static Quat fromeuler_yxy(T phi, T theta, T psi); + static Quat fromeuler_yzy(T phi, T theta, T psi); + static Quat fromeuler_zxz(T phi, T theta, T psi); + static Quat fromeuler_zyz(T phi, T theta, T psi); + /* 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. @@ -978,8 +995,8 @@ template struct Quat friend std::ostream &operator<<(std::ostream &stream, Quat const &v); #endif - /* Storage order is still xyzw because operator[] uses &this->x */ - T x, y, z, w; + /* XXX: storage order is wxyz, unlike vectors! */ + T w, x, y, z; }; template diff --git a/src/math/vector.cpp b/src/math/vector.cpp index 2451d292..e1e12bf4 100644 --- a/src/math/vector.cpp +++ b/src/math/vector.cpp @@ -538,7 +538,7 @@ template<> mat3 mat3::fromeuler(float x, float y, float z) return mat3::fromeuler(vec3(x, y, z)); } -static inline quat fromeuler_generic(vec3 const &v, int s, int i, int j, int k) +static inline quat quat_fromeuler_generic(vec3 const &v, int i, int j, int k) { using std::sin; using std::cos; @@ -548,76 +548,63 @@ static inline quat fromeuler_generic(vec3 const &v, int s, int i, int j, int k) float s1 = sin(half_angles[1]), c1 = cos(half_angles[1]); float s2 = sin(half_angles[2]), c2 = cos(half_angles[2]); - 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); + quat ret; - if (s > 0) - v1 += v2; + if (i == k) + { + ret[0] = c1 * (c0 * c2 - s0 * s2); + ret[1 + i] = c1 * (c0 * s2 + s0 * c2); + ret[1 + j] = s1 * (c0 * c2 + s0 * s2); + if ((2 + i - j) % 3) + ret[4 - i - j] = s1 * (s0 * c2 - c0 * s2); + else + ret[4 - i - j] = s1 * (c0 * s2 - s0 * c2); + } 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)); -} + { + vec4 v1(c0 * c1 * c2, s0 * c1 * c2, c0 * s1 * c2, c0 * c1 * s2); + vec4 v2(s0 * s1 * s2, -c0 * s1 * s2, s0 * c1 * s2, -s0 * s1 * c2); + + if ((2 + i - j) % 3) + v1 -= v2; + else + v1 += v2; + + ret[0] = v1[0]; + ret[1 + i] = v1[1]; + ret[1 + j] = v1[2]; + ret[4 - i - j] = v1[3]; + } -template<> quat quat::fromeuler_yzx(float phi, float theta, float psi) -{ - return quat::fromeuler_yxz(vec3(phi, theta, psi)); + return ret; } -template<> quat quat::fromeuler_zxy(float phi, float theta, float psi) -{ - return quat::fromeuler_yxz(vec3(phi, theta, psi)); -} +#define QUAT_FROMEULER_GENERIC(name, i, j, k) \ + template<> quat quat::fromeuler_##name(vec3 const &v) \ + { \ + return quat_fromeuler_generic(v, i, j, k); \ + } \ + \ + template<> quat quat::fromeuler_##name(float phi, float theta, float psi) \ + { \ + return quat::fromeuler_##name(vec3(phi, theta, psi)); \ + } -template<> quat quat::fromeuler_zyx(float phi, float theta, float psi) -{ - return quat::fromeuler_yxz(vec3(phi, theta, psi)); -} +QUAT_FROMEULER_GENERIC(xyx, 0, 1, 0) +QUAT_FROMEULER_GENERIC(xzx, 0, 2, 0) +QUAT_FROMEULER_GENERIC(yxy, 1, 0, 1) +QUAT_FROMEULER_GENERIC(yzy, 1, 2, 1) +QUAT_FROMEULER_GENERIC(zxz, 2, 0, 2) +QUAT_FROMEULER_GENERIC(zyz, 2, 1, 2) + +QUAT_FROMEULER_GENERIC(xyz, 0, 1, 2) +QUAT_FROMEULER_GENERIC(xzy, 0, 2, 1) +QUAT_FROMEULER_GENERIC(yxz, 1, 0, 2) +QUAT_FROMEULER_GENERIC(yzx, 1, 2, 0) +QUAT_FROMEULER_GENERIC(zxy, 2, 0, 1) +QUAT_FROMEULER_GENERIC(zyx, 2, 1, 0) + +#undef QUAT_FROMEULER_GENERIC template<> mat4 mat4::lookat(vec3 eye, vec3 center, vec3 up) {