Browse Source

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.
legacy
Sam Hocevar sam 12 years ago
parent
commit
552dfee5b1
2 changed files with 81 additions and 77 deletions
  1. +29
    -12
      src/lol/math/vector.h
  2. +52
    -65
      src/math/vector.cpp

+ 29
- 12
src/lol/math/vector.h View File

@@ -125,9 +125,9 @@ template<typename T, int N> 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<T> const & operator =(tname<T> const &that) \
@@ -233,7 +233,7 @@ template <typename T> struct Vec2 : BVec2<T>
explicit inline Vec2(XVec2<U, N> const &v)
: BVec2<T>(v[0], v[1]) {}

DECLARE_MEMBER_OPS(Vec2)
DECLARE_MEMBER_OPS(Vec2, x)

#if !defined __ANDROID__
template<typename U>
@@ -251,7 +251,7 @@ template <typename T> 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<T> operator *(Cmplx<T> const &val) const
{
@@ -491,7 +491,7 @@ template <typename T> struct Vec3 : BVec3<T>

static Vec3<T> toeuler(Quat<T> const &q);

DECLARE_MEMBER_OPS(Vec3)
DECLARE_MEMBER_OPS(Vec3, x)

#if !defined __ANDROID__
template<typename U>
@@ -900,7 +900,7 @@ template <typename T> struct Vec4 : BVec4<T>
explicit inline Vec4(XVec4<U, N> const &v)
: BVec4<T>(v[0], v[1], v[2], v[3]) {}

DECLARE_MEMBER_OPS(Vec4)
DECLARE_MEMBER_OPS(Vec4, x)

#if !defined __ANDROID__
template<typename U>
@@ -915,17 +915,34 @@ template <typename T> struct Vec4 : BVec4<T>
template <typename T> 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<T> const &m);
Quat(Mat4<T> const &m);

DECLARE_MEMBER_OPS(Quat)
DECLARE_MEMBER_OPS(Quat, w)

static Quat<T> rotate(T angle, T x, T y, T z);
static Quat<T> rotate(T angle, Vec3<T> 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<T> fromeuler_xyx(Vec3<T> const &v);
static Quat<T> fromeuler_xzx(Vec3<T> const &v);
static Quat<T> fromeuler_yxy(Vec3<T> const &v);
static Quat<T> fromeuler_yzy(Vec3<T> const &v);
static Quat<T> fromeuler_zxz(Vec3<T> const &v);
static Quat<T> fromeuler_zyz(Vec3<T> const &v);
static Quat<T> fromeuler_xyx(T phi, T theta, T psi);
static Quat<T> fromeuler_xzx(T phi, T theta, T psi);
static Quat<T> fromeuler_yxy(T phi, T theta, T psi);
static Quat<T> fromeuler_yzy(T phi, T theta, T psi);
static Quat<T> fromeuler_zxz(T phi, T theta, T psi);
static Quat<T> 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 <typename T> struct Quat
friend std::ostream &operator<<(std::ostream &stream, Quat<U> 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<typename T>


+ 52
- 65
src/math/vector.cpp View File

@@ -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)
{


Loading…
Cancel
Save