Changeset 1320


Ignore:
Timestamp:
Apr 30, 2012, 8:40:52 PM (9 years ago)
Author:
sam
Message:

math: replace mat3::rotate(quat) with an explicit constructor, and add
more unit tests for the quaternion to 3×3 matrix conversion.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/lol/math/vector.h

    r1317 r1320  
    15661566        v2(mat[2].xyz) {}
    15671567
     1568    explicit Mat3(Quat<T> const &q);
     1569
    15681570    inline Vec3<T>& operator[](size_t n) { return (&v0)[n]; }
    15691571    inline Vec3<T> const& operator[](size_t n) const { return (&v0)[n]; }
     
    15741576    static Mat3<T> rotate(T angle, T x, T y, T z);
    15751577    static Mat3<T> rotate(T angle, Vec3<T> v);
    1576     static Mat3<T> rotate(Quat<T> q);
    15771578    static Mat3<T> fromeuler(T x, T y, T z);
    15781579    static Mat3<T> fromeuler(Vec3<T> const &v);
     
    16761677        v3((T)0, (T)0, (T)0, val) {}
    16771678
     1679    explicit Mat4(Quat<T> const &q);
     1680
    16781681    inline Vec4<T>& operator[](size_t n) { return (&v0)[n]; }
    16791682    inline Vec4<T> const& operator[](size_t n) const { return (&v0)[n]; }
     
    17061709    {
    17071710        return Mat4<T>(Mat3<T>::rotate(angle, v), (T)1);
    1708     }
    1709 
    1710     static inline Mat4<T> rotate(Quat<T> q)
    1711     {
    1712         return Mat4<T>(Mat3<T>::rotate(q), (T)1);
    17131711    }
    17141712
  • trunk/src/math/vector.cpp

    r1319 r1320  
    362362    float ct = std::cos(angle);
    363363
    364     float len = sqrtf(x * x + y * y + z * z);
     364    float len = std::sqrt(x * x + y * y + z * z);
    365365    float invlen = len ? 1.0f / len : 0.0f;
    366366    x *= invlen;
     
    394394}
    395395
    396 template<> mat3 mat3::rotate(quat q)
     396template<> mat3::Mat3(quat const &q)
    397397{
    398398    float n = norm(q);
    399399
    400400    if (!n)
    401         return mat3(1.0f);
    402 
    403     mat3 ret;
     401    {
     402        for (int j = 0; j < 3; j++)
     403            for (int i = 0; i < 3; i++)
     404                (*this)[i][j] = (i == j) ? 1.f : 0.f;
     405        return;
     406    }
     407
    404408    float s = 2.0f / n;
    405409
    406     ret[0][0] = 1.0f - s * (q.y * q.y + q.z * q.z);
    407     ret[0][1] = s * (q.x * q.y - q.z * q.w);
    408     ret[0][2] = s * (q.x * q.z + q.y * q.w);
    409 
    410     ret[1][0] = s * (q.x * q.y + q.z * q.w);
    411     ret[1][1] = 1.0f - s * (q.z * q.z + q.x * q.x);
    412     ret[1][2] = s * (q.y * q.z - q.x * q.w);
    413 
    414     ret[2][0] = s * (q.x * q.z - q.y * q.w);
    415     ret[2][1] = s * (q.y * q.z + q.x * q.w);
    416     ret[2][2] = 1.0f - s * (q.x * q.x + q.y * q.y);
    417 
    418     return ret;
    419 }
    420 
    421 static void MatrixToQuat(quat &that, mat3 const &m)
     410    v0[0] = 1.0f - s * (q.y * q.y + q.z * q.z);
     411    v0[1] = s * (q.x * q.y + q.z * q.w);
     412    v0[2] = s * (q.x * q.z - q.y * q.w);
     413
     414    v1[0] = s * (q.x * q.y - q.z * q.w);
     415    v1[1] = 1.0f - s * (q.z * q.z + q.x * q.x);
     416    v1[2] = s * (q.y * q.z + q.x * q.w);
     417
     418    v2[0] = s * (q.x * q.z + q.y * q.w);
     419    v2[1] = s * (q.y * q.z - q.x * q.w);
     420    v2[2] = 1.0f - s * (q.x * q.x + q.y * q.y);
     421}
     422
     423template<> mat4::Mat4(quat const &q)
     424{
     425    *this = mat4(mat3(q), 1.f);
     426}
     427
     428static inline void MatrixToQuat(quat &that, mat3 const &m)
    422429{
    423430    /* See http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/christian.htm for a version with no branches */
     
    425432    if (t > 0)
    426433    {
    427         that.w = 0.5f * sqrtf(1.0f + t);
     434        that.w = 0.5f * std::sqrt(1.0f + t);
    428435        float s = 0.25f / that.w;
    429436        that.x = s * (m[1][2] - m[2][1]);
     
    433440    else if (m[0][0] > m[1][1] && m[0][0] > m[2][2])
    434441    {
    435         that.x = 0.5f * sqrtf(1.0f + m[0][0] - m[1][1] - m[2][2]);
     442        that.x = 0.5f * std::sqrt(1.0f + m[0][0] - m[1][1] - m[2][2]);
    436443        float s = 0.25f / that.x;
    437444        that.y = s * (m[0][1] + m[1][0]);
     
    441448    else if (m[1][1] > m[2][2])
    442449    {
    443         that.y = 0.5f * sqrtf(1.0f - m[0][0] + m[1][1] - m[2][2]);
     450        that.y = 0.5f * std::sqrt(1.0f - m[0][0] + m[1][1] - m[2][2]);
    444451        float s = 0.25f / that.y;
    445452        that.x = s * (m[0][1] + m[1][0]);
     
    449456    else
    450457    {
    451         that.z = 0.5f * sqrtf(1.0f - m[0][0] - m[1][1] + m[2][2]);
     458        that.z = 0.5f * std::sqrt(1.0f - m[0][0] - m[1][1] + m[2][2]);
    452459        float s = 0.25f / that.z;
    453460        that.x = s * (m[2][0] + m[0][2]);
  • trunk/test/unit/rotation.cpp

    r1318 r1320  
    2727    LOLUNIT_TEST(Rotate2D)
    2828    {
    29         /* Check that rotation is CCW */
     29        /* Rotations must be CCW */
    3030        mat2 m90 = mat2::rotate(90.f);
    3131
     
    3939    LOLUNIT_TEST(Compose2D)
    4040    {
    41         /* Check that rotating 20 degrees twice means rotating 40 degrees */
     41        /* Rotating 20 degrees twice must equal rotating 40 degrees */
    4242        mat2 m20 = mat2::rotate(20.f);
    4343        mat2 m40 = mat2::rotate(40.f);
     
    4646        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[0][0], m40[0][0], 1e-5);
    4747        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[1][0], m40[1][0], 1e-5);
     48
    4849        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[0][1], m40[0][1], 1e-5);
    4950        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[1][1], m40[1][1], 1e-5);
     
    5253    LOLUNIT_TEST(Rotate3D)
    5354    {
    54         /* Check that rotation is CCW around each axis */
     55        /* Rotations must be CCW along each axis */
    5556        mat3 m90x = mat3::rotate(90.f, 1.f, 0.f, 0.f);
    5657        mat3 m90y = mat3::rotate(90.f, 0.f, 1.f, 0.f);
     
    7778    LOLUNIT_TEST(Compose3D)
    7879    {
    79         /* Check that rotating 20 degrees twice means rotating 40 degrees */
     80        /* Rotating 20 degrees twice must equal rotating 40 degrees */
    8081        mat3 m20 = mat3::rotate(20.f, 1.f, 2.f, 3.f);
    8182        mat3 m40 = mat3::rotate(40.f, 1.f, 2.f, 3.f);
     
    8586        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[1][0], m40[1][0], 1e-5);
    8687        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[2][0], m40[2][0], 1e-5);
     88
    8789        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[0][1], m40[0][1], 1e-5);
    8890        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[1][1], m40[1][1], 1e-5);
    8991        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[2][1], m40[2][1], 1e-5);
     92
    9093        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[0][2], m40[0][2], 1e-5);
    9194        LOLUNIT_ASSERT_DOUBLES_EQUAL(m20x20[1][2], m40[1][2], 1e-5);
     
    9598    LOLUNIT_TEST(QuaternionTransform)
    9699    {
    97         /* Check that rotating using a quaternion is the same as rotating
    98          * using a matrix */
     100        /* Rotating using a quaternion must equal rotating using a matrix */
    99101        mat3 m20 = mat3::rotate(20.f, 1.f, 2.f, 3.f);
    100102        quat q20 = quat::rotate(20.f, 1.f, 2.f, 3.f);
     
    111113    LOLUNIT_TEST(QuaternionFromMatrix)
    112114    {
     115        /* A rotation matrix converted to a quaternion should match the
     116         * quaternion built with the same parameters */
    113117        quat q1 = quat::rotate(20.f, 1.f, 2.f, 3.f);
    114118        quat q2 = quat(mat3::rotate(20.f, 1.f, 2.f, 3.f));
     
    119123        LOLUNIT_ASSERT_DOUBLES_EQUAL(q2.z, q1.z, 1e-5);
    120124    }
     125
     126    LOLUNIT_TEST(MatrixFromQuaternion)
     127    {
     128        /* A quaternion converted to a rotation matrix should match the
     129         * rotation matrix built with the same parameters */
     130        mat3 m1 = mat3::rotate(60.f, 1.f, -2.f, 3.f);
     131        mat3 m2 = mat3(quat::rotate(60.f, 1.f, -2.f, 3.f));
     132
     133        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[0][0], m1[0][0], 1e-5);
     134        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[1][0], m1[1][0], 1e-5);
     135        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[2][0], m1[2][0], 1e-5);
     136
     137        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[0][1], m1[0][1], 1e-5);
     138        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[1][1], m1[1][1], 1e-5);
     139        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[2][1], m1[2][1], 1e-5);
     140
     141        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[0][2], m1[0][2], 1e-5);
     142        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[1][2], m1[1][2], 1e-5);
     143        LOLUNIT_ASSERT_DOUBLES_EQUAL(m2[2][2], m1[2][2], 1e-5);
     144    }
     145
     146    LOLUNIT_TEST(MatrixCompositionThroughQuaternions)
     147    {
     148        /* Combining two rotation matrices should match the matrix created
     149         * from the combination of the two equivalent quaternions */
     150        mat3 m1 = mat3::rotate(60.f, 1.f, -2.f, 3.f);
     151        mat3 m2 = mat3::rotate(20.f, -3.f, 1.f, -3.f);
     152
     153        mat3 m3 = m2 * m1;
     154        mat3 m4(quat(m2) * quat(m1));
     155
     156        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[0][0], m3[0][0], 1e-5);
     157        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[1][0], m3[1][0], 1e-5);
     158        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[2][0], m3[2][0], 1e-5);
     159
     160        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[0][1], m3[0][1], 1e-5);
     161        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[1][1], m3[1][1], 1e-5);
     162        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[2][1], m3[2][1], 1e-5);
     163
     164        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[0][2], m3[0][2], 1e-5);
     165        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[1][2], m3[1][2], 1e-5);
     166        LOLUNIT_ASSERT_DOUBLES_EQUAL(m4[2][2], m3[2][2], 1e-5);
     167    }
     168
     169    LOLUNIT_TEST(QuaternionCompositionThroughMatrices)
     170    {
     171        /* Combining two quaternions should match the quaternion created
     172         * from the combination of the two equivalent rotation matrices */
     173        quat q1 = quat::rotate(60.f, 1.f, -2.f, 3.f);
     174        quat q2 = quat::rotate(20.f, -3.f, 1.f, -2.f);
     175
     176        quat q3 = q2 * q1;
     177        quat q4(mat3(q2) * mat3(q1));
     178
     179        LOLUNIT_ASSERT_DOUBLES_EQUAL(q4.w, q3.w, 1e-5);
     180        LOLUNIT_ASSERT_DOUBLES_EQUAL(q4.x, q3.x, 1e-5);
     181        LOLUNIT_ASSERT_DOUBLES_EQUAL(q4.y, q3.y, 1e-5);
     182        LOLUNIT_ASSERT_DOUBLES_EQUAL(q4.z, q3.z, 1e-5);
     183    }
    121184};
    122185
Note: See TracChangeset for help on using the changeset viewer.