From def84d569dbc6c93d92bf92b294a2a69dfccb16a Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 1 Nov 2011 17:55:23 +0000 Subject: [PATCH] core: implement a quaternion class and tighten some vector operation rules to avoid common programming errors. --- src/matrix.cpp | 41 ++++------ src/matrix.h | 203 ++++++++++++++++++++++++++++++++++------------- test/Makefile.am | 2 +- 3 files changed, 166 insertions(+), 80 deletions(-) diff --git a/src/matrix.cpp b/src/matrix.cpp index dce0c763..64919fef 100644 --- a/src/matrix.cpp +++ b/src/matrix.cpp @@ -53,30 +53,6 @@ template<> vec3 cross(vec3 v1, vec3 v2) v1.x * v2.y - v1.y * v2.x); } -template<> vec2 normalize(vec2 v) -{ - float norm = v.len(); - if (!norm) - return vec2(0); - return v / norm; -} - -template<> vec3 normalize(vec3 v) -{ - float norm = v.len(); - if (!norm) - return vec3(0); - return v / norm; -} - -template<> vec4 normalize(vec4 v) -{ - float norm = v.len(); - if (!norm) - return vec4(0.0f); - return v / norm; -} - static inline float det3(float a, float b, float c, float d, float e, float f, float g, float h, float i) @@ -151,6 +127,11 @@ template<> void ivec4::printf() const Log::Debug("[ %i %i %i %i ]\n", x, y, z, w); } +template<> void quat::printf() const +{ + Log::Debug("[ %6.6f %6.6f %6.6f %6.6f ]\n", x, y, z, w); +} + template<> void mat4::printf() const { mat4 const &p = *this; @@ -182,6 +163,12 @@ template<> std::ostream &operator<<(std::ostream &stream, ivec4 const &v) << v.z << ", " << v.w << ")"; } +template<> std::ostream &operator<<(std::ostream &stream, iquat const &v) +{ + return stream << "(" << v.x << ", " << v.y << ", " + << v.z << ", " << v.w << ")"; +} + template<> std::ostream &operator<<(std::ostream &stream, vec2 const &v) { return stream << "(" << v.x << ", " << v.y << ")"; @@ -198,6 +185,12 @@ template<> std::ostream &operator<<(std::ostream &stream, vec4 const &v) << v.z << ", " << v.w << ")"; } +template<> std::ostream &operator<<(std::ostream &stream, quat const &v) +{ + return stream << "(" << v.x << ", " << v.y << ", " + << v.z << ", " << v.w << ")"; +} + template<> std::ostream &operator<<(std::ostream &stream, mat4 const &m) { stream << "((" << m[0][0] << ", " << m[1][0] diff --git a/src/matrix.h b/src/matrix.h index 50fafc6e..4c2c0b8c 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -24,62 +24,60 @@ namespace lol { -#define VECTOR_OP(elems, op) \ - template \ - inline Vec##elems operator op(Vec##elems const &val) const \ +#define VECTOR_OP(op) \ + inline type_t operator op(type_t const &val) const \ { \ - Vec##elems ret; \ - for (int n = 0; n < elems; n++) \ + type_t ret; \ + for (size_t n = 0; n < sizeof(*this) / sizeof(T); n++) \ ret[n] = (*this)[n] op val[n]; \ return ret; \ } \ \ - template \ - inline Vec##elems operator op##=(Vec##elems const &val) \ + inline type_t operator op##=(type_t const &val) \ { \ return *this = (*this) op val; \ } -#define BOOL_OP(elems, op, op2, ret) \ - inline bool operator op(Vec##elems const &val) const \ +#define BOOL_OP(op, op2, ret) \ + inline bool operator op(type_t const &val) const \ { \ - for (int n = 0; n < elems; n++) \ + for (size_t n = 0; n < sizeof(*this) / sizeof(T); n++) \ if (!((*this)[n] op2 val[n])) \ return !ret; \ return ret; \ } -#define SCALAR_OP(elems, op) \ - inline Vec##elems operator op(T const &val) const \ +#define SCALAR_OP(op) \ + inline type_t operator op(T const &val) const \ { \ - Vec##elems ret; \ - for (int n = 0; n < elems; n++) \ + type_t ret; \ + for (size_t n = 0; n < sizeof(*this) / sizeof(T); n++) \ ret[n] = (*this)[n] op val; \ return ret; \ } \ \ - inline Vec##elems operator op##=(T const &val) \ + inline type_t operator op##=(T const &val) \ { \ return *this = (*this) op val; \ } -#define LINEAR_OPS(elems) \ +#define LINEAR_OPS() \ inline T& operator[](int n) { return *(&x + n); } \ inline T const& operator[](int n) const { return *(&x + n); } \ \ - VECTOR_OP(elems, -) \ - VECTOR_OP(elems, +) \ + VECTOR_OP(-) \ + VECTOR_OP(+) \ \ - BOOL_OP(elems, ==, ==, true) \ - BOOL_OP(elems, !=, ==, false) \ + BOOL_OP(==, ==, true) \ + BOOL_OP(!=, ==, false) \ \ - SCALAR_OP(elems, *) \ - SCALAR_OP(elems, /) \ + SCALAR_OP(*) \ + SCALAR_OP(/) \ \ - inline Vec##elems operator -() const \ + inline type_t operator -() const \ { \ - Vec##elems ret; \ - for (int n = 0; n < elems; n++) \ + type_t ret; \ + for (size_t n = 0; n < sizeof(*this) / sizeof(T); n++) \ ret[n] = -(*this)[n]; \ return ret; \ } \ @@ -87,7 +85,7 @@ namespace lol inline T sqlen() const \ { \ T acc = 0; \ - for (int n = 0; n < elems; n++) \ + for (size_t n = 0; n < sizeof(*this) / sizeof(T); n++) \ acc += (*this)[n] * (*this)[n]; \ return acc; \ } \ @@ -98,25 +96,52 @@ namespace lol return sqrtf((float)sqlen()); \ } \ \ - template \ - friend Vec##elems normalize(Vec##elems); \ - \ void printf() const; +#define QUATERNION_OPS() \ + inline type_t operator *(type_t const &val) const \ + { \ + type_t ret; \ + Vec3 v1(x, y, z); \ + Vec3 v2(val.x, val.y, val.z); \ + Vec3 v3 = cross(v1, v2) + w * v2 + val.w * v1; \ + ret.x = v3.x; \ + ret.y = v3.y; \ + ret.z = v3.z; \ + ret.w = w * val.w - dot(v1, v2); \ + return ret; \ + } \ + \ + inline type_t operator *=(type_t const &val) \ + { \ + return *this = (*this) * val; \ + } \ + \ + inline type_t operator ~() const \ + { \ + type_t ret; \ + for (int n = 0; n < 3; n++) \ + ret[n] = -(*this)[n]; \ + ret[3] = (*this)[3]; \ + return ret; \ + } \ + \ + inline T norm() const { return sqlen(); } + #define OTHER_OPS(elems) \ - VECTOR_OP(elems, *) \ - VECTOR_OP(elems, /) \ + VECTOR_OP(*) \ + VECTOR_OP(/) \ \ - BOOL_OP(elems, <=, <=, true) \ - BOOL_OP(elems, >=, >=, true) \ - BOOL_OP(elems, <, <, true) \ - BOOL_OP(elems, >, >, true) \ + BOOL_OP(<=, <=, true) \ + BOOL_OP(>=, >=, true) \ + BOOL_OP(<, <, true) \ + BOOL_OP(>, >, true) \ \ template \ inline operator Vec##elems() const \ { \ Vec##elems ret; \ - for (int n = 0; n < elems; n++) \ + for (size_t n = 0; n < sizeof(*this) / sizeof(T); n++) \ ret[n] = static_cast((*this)[n]); \ return ret; \ } \ @@ -195,11 +220,13 @@ template struct Vec4; template struct Vec2 { + typedef Vec2 type_t; + inline Vec2() { } explicit inline Vec2(T val) { x = y = val; } inline Vec2(T _x, T _y) { x = _x; y = _y; } - LINEAR_OPS(2) + LINEAR_OPS() OTHER_OPS(2) SWIZZLE22(x); SWIZZLE22(y); @@ -232,13 +259,15 @@ typedef Vec2 u64vec2; template struct Vec3 { + typedef Vec3 type_t; + inline Vec3() { } explicit inline Vec3(T val) { x = y = z = val; } inline Vec3(T _x, T _y, T _z) { x = _x; y = _y; z = _z; } inline Vec3(Vec2 _xy, T _z) { x = _xy.x; y = _xy.y; z = _z; } inline Vec3(T _x, Vec2 _yz) { x = _x; y = _yz.x; z = _yz.y; } - LINEAR_OPS(3) + LINEAR_OPS() OTHER_OPS(3) SWIZZLE23(x); SWIZZLE23(y); SWIZZLE23(z); @@ -275,6 +304,8 @@ typedef Vec3 u64vec3; template struct Vec4 { + typedef Vec4 type_t; + inline Vec4() { } explicit inline Vec4(T val) : x(val), y(val), z(val), w(val) { } inline Vec4(T _x, T _y, T _z, T _w) : x(_x), y(_y), z(_z), w(_w) { } @@ -285,7 +316,7 @@ template struct Vec4 inline Vec4(Vec3 _xyz, T _w) : x(_xyz.x), y(_xyz.y), z(_xyz.z), w(_w) { } inline Vec4(T _x, Vec3 _yzw) : x(_x), y(_yzw.x), z(_yzw.y), w(_yzw.z) { } - LINEAR_OPS(4) + LINEAR_OPS() OTHER_OPS(4) SWIZZLE24(x); SWIZZLE24(y); SWIZZLE24(z); SWIZZLE24(w); @@ -314,30 +345,90 @@ typedef Vec4 uvec4; typedef Vec4 i64vec4; typedef Vec4 u64vec4; -#define SCALAR_GLOBAL(elems, op, U) \ +/* + * 4-element quaternions + */ + +template struct Quat +{ + typedef Quat type_t; + + inline Quat() { } + inline Quat(T val) : x(0), y(0), z(0), w(val) { } + inline Quat(T _x, T _y, T _z, T _w) : x(_x), y(_y), z(_z), w(_w) { } + + LINEAR_OPS() + QUATERNION_OPS() + +#if !defined __ANDROID__ + template + friend std::ostream &operator<<(std::ostream &stream, Quat const &v); +#endif + + T x, y, z, w; +}; + +template +static inline Quat re(Quat const &val) +{ + return ~val / val.norm(); +} + +template +static inline Quat operator /(T x, Quat const &y) +{ + return x * re(y); +} + +template +static inline Quat operator /(Quat x, Quat const &y) +{ + return x * re(y); +} + +typedef Quat f16quat; +typedef Quat quat; +typedef Quat i8quat; +typedef Quat u8quat; +typedef Quat i16quat; +typedef Quat u16quat; +typedef Quat iquat; +typedef Quat uquat; +typedef Quat i64quat; +typedef Quat u64quat; + +/* + * Common operators for all vector types, including quaternions + */ + +#define SCALAR_GLOBAL(tname, op, U) \ template \ - static inline Vec##elems operator op(U const &val, \ - Vec##elems const &that) \ + static inline tname operator op(U const &val, tname const &that) \ { \ - Vec##elems ret; \ - for (int n = 0; n < elems; n++) \ + tname ret; \ + for (unsigned int n = 0; n < sizeof(that) / sizeof(that[0]); n++) \ ret[n] = val op that[n]; \ return ret; \ } -#define SCALAR_GLOBAL2(elems, op) \ - SCALAR_GLOBAL(elems, op, int) \ - SCALAR_GLOBAL(elems, op, float) +#define SCALAR_GLOBAL2(tname, op) \ + SCALAR_GLOBAL(tname, op, int) \ + SCALAR_GLOBAL(tname, op, float) -#define GLOBALS(elems) \ - SCALAR_GLOBAL2(elems, -) \ - SCALAR_GLOBAL2(elems, +) \ - SCALAR_GLOBAL2(elems, *) \ - SCALAR_GLOBAL2(elems, /) +#define GLOBALS(tname) \ + SCALAR_GLOBAL2(tname, *) \ + \ + template \ + static inline tname normalize(tname const &val) \ + { \ + T norm = val.len(); \ + return norm ? val / norm : val * 0; \ + } -GLOBALS(2) -GLOBALS(3) -GLOBALS(4) +GLOBALS(Vec2) +GLOBALS(Vec3) +GLOBALS(Vec4) +GLOBALS(Quat) /* * 4×4-element matrices @@ -345,6 +436,8 @@ GLOBALS(4) template struct Mat4 { + typedef Mat4 type_t; + inline Mat4() { } explicit inline Mat4(T val) { diff --git a/test/Makefile.am b/test/Makefile.am index d86c00a0..c127bd23 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -24,7 +24,7 @@ TESTS = testsuite testsuite_SOURCES = testsuite.cpp \ unit/matrix.cpp unit/half.cpp unit/trig.cpp unit/build.cpp \ - unit/real.cpp unit/image.cpp + unit/real.cpp unit/image.cpp unit/quat.cpp testsuite_CPPFLAGS = @LOL_CFLAGS@ @PIPI_CFLAGS@ testsuite_LDFLAGS = $(top_builddir)/src/liblol.a @LOL_LIBS@ @PIPI_LIBS@ testsuite_DEPENDENCIES = $(top_builddir)/src/liblol.a