diff --git a/include/cglm/euler.h b/include/cglm/euler.h index 8a4acfd..fb4adc0 100644 --- a/include/cglm/euler.h +++ b/include/cglm/euler.h @@ -43,6 +43,8 @@ #include "common.h" +#include "handed/euler_to_quat_rh.h" + /*! * if you have axis order like vec3 orderVec = [0, 1, 2] or [0, 2, 1]... * vector then you can convert it to this enum by doing this: @@ -194,7 +196,6 @@ glm_euler_xzy(vec3 angles, mat4 dest) { dest[3][3] = 1.0f; } - /*! * @brief build rotation matrix from euler angles * @@ -465,18 +466,11 @@ glm_euler_by_order(vec3 angles, glm_euler_seq ord, mat4 dest) { CGLM_INLINE void glm_euler_xyz_quat(vec3 angles, versor dest) { - float xc, yc, zc, - xs, ys, zs; - - xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); - ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); - zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); - - dest[0] = xc * ys * zs + xs * yc * zc; - dest[1] = xc * ys * zc - xs * yc * zs; - dest[2] = xc * yc * zs + xs * ys * zc; - dest[3] = xc * yc * zc - xs * ys * zs; - +#ifdef CGLM_FORCE_LEFT_HANDED + glm_euler_xyz_quat_lh(angles, dest); +#else + glm_euler_xyz_quat_rh(angles, dest); +#endif } /*! @@ -489,18 +483,11 @@ glm_euler_xyz_quat(vec3 angles, versor dest) { CGLM_INLINE void glm_euler_xzy_quat(vec3 angles, versor dest) { - float xc, yc, zc, - xs, ys, zs; - - xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); - ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); - zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); - - dest[0] = -xc * zs * ys + xs * zc * yc; - dest[1] = xc * zc * ys - xs * zs * yc; - dest[2] = xc * zs * yc + xs * zc * ys; - dest[3] = xc * zc * yc + xs * zs * ys; - +#ifdef CGLM_FORCE_LEFT_HANDED + glm_euler_xzy_quat_lh(angles, dest); +#else + glm_euler_xzy_quat_rh(angles, dest); +#endif } /*! @@ -513,17 +500,11 @@ glm_euler_xzy_quat(vec3 angles, versor dest) { CGLM_INLINE void glm_euler_yxz_quat(vec3 angles, versor dest) { - float xc, yc, zc, - xs, ys, zs; - - xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); - ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); - zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); - - dest[0] = yc * xs * zc + ys * xc * zs; - dest[1] = -yc * xs * zs + ys * xc * zc; - dest[2] = yc * xc * zs - ys * xs * zc; - dest[3] = yc * xc * zc + ys * xs * zs; +#ifdef CGLM_FORCE_LEFT_HANDED + glm_euler_yxz_quat_lh(angles, dest); +#else + glm_euler_yxz_quat_rh(angles, dest); +#endif } /*! @@ -536,18 +517,11 @@ glm_euler_yxz_quat(vec3 angles, versor dest) { CGLM_INLINE void glm_euler_yzx_quat(vec3 angles, versor dest) { - float xc, yc, zc, - xs, ys, zs; - - xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); - ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); - zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); - - dest[0] = yc * zc * xs + ys * zs * xc; - dest[1] = yc * zs * xs + ys * zc * xc; - dest[2] = yc * zs * xc - ys * zc * xs; - dest[3] = yc * zc * xc - ys * zs * xs; - +#ifdef CGLM_FORCE_LEFT_HANDED + glm_euler_yzx_quat_lh(angles, dest); +#else + glm_euler_yzx_quat_rh(angles, dest); +#endif } /*! @@ -560,17 +534,11 @@ glm_euler_yzx_quat(vec3 angles, versor dest) { CGLM_INLINE void glm_euler_zxy_quat(vec3 angles, versor dest) { - float xc, yc, zc, - xs, ys, zs; - - xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); - ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); - zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); - - dest[0] = zc * xs * yc - zs * xc * ys; - dest[1] = zc * xc * ys + zs * xs * yc; - dest[2] = zc * xs * ys + zs * xc * yc; - dest[3] = zc * xc * yc - zs * xs * ys; +#ifdef CGLM_FORCE_LEFT_HANDED + glm_euler_zxy_quat_lh(angles, dest); +#else + glm_euler_zxy_quat_rh(angles, dest); +#endif } /*! @@ -583,17 +551,11 @@ glm_euler_zxy_quat(vec3 angles, versor dest) { CGLM_INLINE void glm_euler_zyx_quat(vec3 angles, versor dest) { - float xc, yc, zc, - xs, ys, zs; - - xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); - ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); - zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); - - dest[0] = zc * yc * xs - zs * ys * xc; - dest[1] = zc * ys * xc + zs * yc * xs; - dest[2] = -zc * ys * xs + zs * yc * xc; - dest[3] = zc * yc * xc + zs * ys * xs; +#ifdef CGLM_FORCE_LEFT_HANDED + glm_euler_zyx_quat_lh(angles, dest); +#else + glm_euler_zyx_quat_rh(angles, dest); +#endif } diff --git a/include/cglm/handed/euler_to_quat_rh.h b/include/cglm/handed/euler_to_quat_rh.h new file mode 100644 index 0000000..b65108c --- /dev/null +++ b/include/cglm/handed/euler_to_quat_rh.h @@ -0,0 +1,165 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +/* + Functions: + CGLM_INLINE void glm_euler_xyz_quat_rh(vec3 angles, versor dest); + CGLM_INLINE void glm_euler_xzy_quat_rh(vec3 angles, versor dest); + CGLM_INLINE void glm_euler_yxz_quat_rh(vec3 angles, versor dest); + CGLM_INLINE void glm_euler_yzx_quat_rh(vec3 angles, versor dest); + CGLM_INLINE void glm_euler_zxy_quat_rh(vec3 angles, versor dest); + CGLM_INLINE void glm_euler_zyx_quat_rh(vec3 angles, versor dest); + */ + +#ifndef cglm_euler_to_quat_rh_h +#define cglm_euler_to_quat_rh_h + +#include "../common.h" + +/*! + * @brief creates NEW quaternion using rotation angles and does + * rotations in x y z order in right hand (roll pitch yaw) + * + * @param[out] q quaternion + * @param[in] angle angles x y z (radians) + */ +CGLM_INLINE +void +glm_euler_xyz_quat_rh(vec3 angles, versor dest) { + float xc, yc, zc, + xs, ys, zs; + + xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); + ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); + zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); + + dest[0] = xc * ys * zs + xs * yc * zc; + dest[1] = xc * ys * zc - xs * yc * zs; + dest[2] = xc * yc * zs + xs * ys * zc; + dest[3] = xc * yc * zc - xs * ys * zs; + +} + +/*! + * @brief creates NEW quaternion using rotation angles and does + * rotations in x z y order in right hand (roll yaw pitch) + * + * @param[out] q quaternion + * @param[in] angle angles x y z (radians) + */ +CGLM_INLINE +void +glm_euler_xzy_quat_rh(vec3 angles, versor dest) { + float xc, yc, zc, + xs, ys, zs; + + xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); + ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); + zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); + + dest[0] = -xc * zs * ys + xs * zc * yc; + dest[1] = xc * zc * ys - xs * zs * yc; + dest[2] = xc * zs * yc + xs * zc * ys; + dest[3] = xc * zc * yc + xs * zs * ys; + +} + +/*! + * @brief creates NEW quaternion using rotation angles and does + * rotations in y x z order in right hand (pitch roll yaw) + * + * @param[out] q quaternion + * @param[in] angle angles x y z (radians) + */ +CGLM_INLINE +void +glm_euler_yxz_quat_rh(vec3 angles, versor dest) { + float xc, yc, zc, + xs, ys, zs; + + xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); + ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); + zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); + + dest[0] = yc * xs * zc + ys * xc * zs; + dest[1] = -yc * xs * zs + ys * xc * zc; + dest[2] = yc * xc * zs - ys * xs * zc; + dest[3] = yc * xc * zc + ys * xs * zs; +} + +/*! + * @brief creates NEW quaternion using rotation angles and does + * rotations in y z x order in right hand (pitch yaw roll) + * + * @param[out] q quaternion + * @param[in] angle angles x y z (radians) + */ +CGLM_INLINE +void +glm_euler_yzx_quat_rh(vec3 angles, versor dest) { + float xc, yc, zc, + xs, ys, zs; + + xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); + ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); + zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); + + dest[0] = yc * zc * xs + ys * zs * xc; + dest[1] = yc * zs * xs + ys * zc * xc; + dest[2] = yc * zs * xc - ys * zc * xs; + dest[3] = yc * zc * xc - ys * zs * xs; + +} + +/*! + * @brief creates NEW quaternion using rotation angles and does + * rotations in z x y order in right hand (yaw roll pitch) + * + * @param[out] q quaternion + * @param[in] angle angles x y z (radians) + */ +CGLM_INLINE +void +glm_euler_zxy_quat_rh(vec3 angles, versor dest) { + float xc, yc, zc, + xs, ys, zs; + + xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); + ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); + zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); + + dest[0] = zc * xs * yc - zs * xc * ys; + dest[1] = zc * xc * ys + zs * xs * yc; + dest[2] = zc * xs * ys + zs * xc * yc; + dest[3] = zc * xc * yc - zs * xs * ys; +} + +/*! + * @brief creates NEW quaternion using rotation angles and does + * rotations in z y x order in right hand (yaw pitch roll) + * + * @param[out] q quaternion + * @param[in] angle angles x y z (radians) + */ +CGLM_INLINE +void +glm_euler_zyx_quat_rh(vec3 angles, versor dest) { + float xc, yc, zc, + xs, ys, zs; + + xs = sinf(angles[0] * 0.5f); xc = cosf(angles[0] * 0.5f); + ys = sinf(angles[1] * 0.5f); yc = cosf(angles[1] * 0.5f); + zs = sinf(angles[2] * 0.5f); zc = cosf(angles[2] * 0.5f); + + dest[0] = zc * yc * xs - zs * ys * xc; + dest[1] = zc * ys * xc + zs * yc * xs; + dest[2] = -zc * ys * xs + zs * yc * xc; + dest[3] = zc * yc * xc + zs * ys * xs; +} + + +#endif /*cglm_euler_to_quat_rh_h*/ diff --git a/test/src/test_euler.c b/test/src/test_euler.c index 8a98091..b71e0f0 100644 --- a/test/src/test_euler.c +++ b/test/src/test_euler.c @@ -7,571 +7,6 @@ #include "test_common.h" -TEST_IMPL(glm_euler_xyz_quat) { - vec3 axis_x = {1.0f, 0.0f, 0.0f}; - vec3 axis_y = {0.0f, 1.0f, 0.0f}; - vec3 axis_z = {0.0f, 0.0f, 1.0f}; - - /* random angles for testing */ - vec3 angles; - - /* quaternion representations for rotations */ - versor rot_x, rot_y, rot_z; - - versor expected; - versor result; - versor tmp; - - mat4 expected_mat4; - - /* 100 randomized tests */ - for (int i = 0; i < 100; i++) { - test_rand_vec3(angles); - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in xyz order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - - glm_euler_xyz_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - /* verify that it acts the same as rotating by 3 axis quaternions */ - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_XYZ, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - - - /* Start gimbal lock tests */ - for (float x = -90.0f; x <= 90.0f; x += 90.0f) { - for (float y = -90.0f; y <= 90.0f; y += 90.0f) { - for (float z = -90.0f; z <= 90.0f; z += 90.0f) { - angles[0] = x; - angles[1] = y; - angles[2] = z; - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in xyz order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - - /* use my function to get the quaternion */ - glm_euler_xyz_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_XYZ, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - } - } - TEST_SUCCESS -} - -TEST_IMPL(glm_euler_xzy_quat) { - vec3 axis_x = {1.0f, 0.0f, 0.0f}; - vec3 axis_y = {0.0f, 1.0f, 0.0f}; - vec3 axis_z = {0.0f, 0.0f, 1.0f}; - - /* random angles for testing */ - vec3 angles; - - /* quaternion representations for rotations */ - versor rot_x, rot_y, rot_z; - - versor expected; - versor result; - versor tmp; - - mat4 expected_mat4; - - /* 100 randomized tests */ - for (int i = 0; i < 100; i++) { - test_rand_vec3(angles); - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in xzy order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - - glm_euler_xzy_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - /* verify that it acts the same as rotating by 3 axis quaternions */ - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_XZY, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - - - /* Start gimbal lock tests */ - for (float x = -90.0f; x <= 90.0f; x += 90.0f) { - for (float y = -90.0f; y <= 90.0f; y += 90.0f) { - for (float z = -90.0f; z <= 90.0f; z += 90.0f) { - angles[0] = x; - angles[1] = y; - angles[2] = z; - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in xzy order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - - /* use my function to get the quaternion */ - glm_euler_xzy_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_XZY, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - } - } - TEST_SUCCESS -} - -TEST_IMPL(glm_euler_yxz_quat) { - vec3 axis_x = {1.0f, 0.0f, 0.0f}; - vec3 axis_y = {0.0f, 1.0f, 0.0f}; - vec3 axis_z = {0.0f, 0.0f, 1.0f}; - - /* random angles for testing */ - vec3 angles; - - /* quaternion representations for rotations */ - versor rot_x, rot_y, rot_z; - - versor expected; - versor result; - versor tmp; - - mat4 expected_mat4; - - /* 100 randomized tests */ - for (int i = 0; i < 100; i++) { - test_rand_vec3(angles); - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in yxz order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - - glm_euler_yxz_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - /* verify that it acts the same as rotating by 3 axis quaternions */ - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_YXZ, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - - - /* Start gimbal lock tests */ - for (float x = -90.0f; x <= 90.0f; x += 90.0f) { - for (float y = -90.0f; y <= 90.0f; y += 90.0f) { - for (float z = -90.0f; z <= 90.0f; z += 90.0f) { - angles[0] = x; - angles[1] = y; - angles[2] = z; - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in yxz order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - - /* use my function to get the quaternion */ - glm_euler_yxz_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_YXZ, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - } - } - TEST_SUCCESS -} - -TEST_IMPL(glm_euler_yzx_quat) { - vec3 axis_x = {1.0f, 0.0f, 0.0f}; - vec3 axis_y = {0.0f, 1.0f, 0.0f}; - vec3 axis_z = {0.0f, 0.0f, 1.0f}; - - /* random angles for testing */ - vec3 angles; - - /* quaternion representations for rotations */ - versor rot_x, rot_y, rot_z; - - versor expected; - versor result; - versor tmp; - - mat4 expected_mat4; - - /* 100 randomized tests */ - for (int i = 0; i < 100; i++) { - test_rand_vec3(angles); - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in yzx order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - - glm_euler_yzx_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - /* verify that it acts the same as rotating by 3 axis quaternions */ - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_YZX, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - - - /* Start gimbal lock tests */ - for (float x = -90.0f; x <= 90.0f; x += 90.0f) { - for (float y = -90.0f; y <= 90.0f; y += 90.0f) { - for (float z = -90.0f; z <= 90.0f; z += 90.0f) { - angles[0] = x; - angles[1] = y; - angles[2] = z; - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in yzx order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - - /* use my function to get the quaternion */ - glm_euler_yzx_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_YZX, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - } - } - TEST_SUCCESS -} - -TEST_IMPL(glm_euler_zxy_quat) { - vec3 axis_x = {1.0f, 0.0f, 0.0f}; - vec3 axis_y = {0.0f, 1.0f, 0.0f}; - vec3 axis_z = {0.0f, 0.0f, 1.0f}; - - /* random angles for testing */ - vec3 angles; - - /* quaternion representations for rotations */ - versor rot_x, rot_y, rot_z; - - versor expected; - versor result; - versor tmp; - - mat4 expected_mat4; - - /* 100 randomized tests */ - for (int i = 0; i < 100; i++) { - test_rand_vec3(angles); - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in zxy order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - - glm_euler_zxy_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - /* verify that it acts the same as rotating by 3 axis quaternions */ - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_ZXY, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - - - /* Start gimbal lock tests */ - for (float x = -90.0f; x <= 90.0f; x += 90.0f) { - for (float y = -90.0f; y <= 90.0f; y += 90.0f) { - for (float z = -90.0f; z <= 90.0f; z += 90.0f) { - angles[0] = x; - angles[1] = y; - angles[2] = z; - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in zxy order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - - /* use my function to get the quaternion */ - glm_euler_zxy_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_ZXY, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - } - } - TEST_SUCCESS -} - -TEST_IMPL(glm_euler_zyx_quat) { - vec3 axis_x = {1.0f, 0.0f, 0.0f}; - vec3 axis_y = {0.0f, 1.0f, 0.0f}; - vec3 axis_z = {0.0f, 0.0f, 1.0f}; - - /* random angles for testing */ - vec3 angles; - - /* quaternion representations for rotations */ - versor rot_x, rot_y, rot_z; - - versor expected; - versor result; - versor tmp; - - mat4 expected_mat4; - - /* 100 randomized tests */ - for (int i = 0; i < 100; i++) { - test_rand_vec3(angles); - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in zyx order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - - glm_euler_zyx_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - /* verify that it acts the same as rotating by 3 axis quaternions */ - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_ZYX, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - - - /* Start gimbal lock tests */ - for (float x = -90.0f; x <= 90.0f; x += 90.0f) { - for (float y = -90.0f; y <= 90.0f; y += 90.0f) { - for (float z = -90.0f; z <= 90.0f; z += 90.0f) { - angles[0] = x; - angles[1] = y; - angles[2] = z; - - /* create the rotation quaternions using the angles and axises */ - glm_quatv(rot_x, angles[0], axis_x); - glm_quatv(rot_y, angles[1], axis_y); - glm_quatv(rot_z, angles[2], axis_z); - - /* apply the rotations to a unit quaternion in xyz order */ - glm_quat_identity(expected); - - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_z, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_y, expected); - glm_quat_copy(expected, tmp); - glm_quat_mul(tmp, rot_x, expected); - - /* use my function to get the quaternion */ - glm_euler_zyx_quat(angles, result); - - /* verify if the magnitude of the quaternion stays 1 */ - ASSERT(test_eq(glm_quat_norm(result), 1.0f)) - - ASSERTIFY(test_assert_quat_eq(result, expected)) - - /* verify that it acts the same as glm_euler_by_order */ - glm_euler_by_order(angles, GLM_EULER_ZYX, expected_mat4); - glm_mat4_quat(expected_mat4, expected); - - ASSERTIFY(test_assert_quat_eq_abs(result, expected)); - } - } - } - TEST_SUCCESS -} - - TEST_IMPL(euler) { mat4 rot1, rot2; vec3 inAngles, outAngles; diff --git a/test/src/test_euler_to_quat_rh.h b/test/src/test_euler_to_quat_rh.h new file mode 100644 index 0000000..221192e --- /dev/null +++ b/test/src/test_euler_to_quat_rh.h @@ -0,0 +1,575 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +#include "test_common.h" +#include "../../include/cglm/handed/euler_to_quat_rh.h" + +TEST_IMPL(GLM_PREFIX, euler_xyz_quat_rh) { + vec3 axis_x = {1.0f, 0.0f, 0.0f}; + vec3 axis_y = {0.0f, 1.0f, 0.0f}; + vec3 axis_z = {0.0f, 0.0f, 1.0f}; + + /* random angles for testing */ + vec3 angles; + + /* quaternion representations for rotations */ + versor rot_x, rot_y, rot_z; + + versor expected; + versor result; + versor tmp; + + mat4 expected_mat4; + + /* 100 randomized tests */ + for (int i = 0; i < 100; i++) { + test_rand_vec3(angles); + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in xyz order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + + glm_euler_xyz_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + /* verify that it acts the same as rotating by 3 axis quaternions */ + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_XYZ, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + + + /* Start gimbal lock tests */ + for (float x = -90.0f; x <= 90.0f; x += 90.0f) { + for (float y = -90.0f; y <= 90.0f; y += 90.0f) { + for (float z = -90.0f; z <= 90.0f; z += 90.0f) { + angles[0] = x; + angles[1] = y; + angles[2] = z; + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in xyz order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + + /* use my function to get the quaternion */ + glm_euler_xyz_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_XYZ, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + } + } + TEST_SUCCESS +} + +TEST_IMPL(GLM_PREFIX, euler_xzy_quat_rh) { + vec3 axis_x = {1.0f, 0.0f, 0.0f}; + vec3 axis_y = {0.0f, 1.0f, 0.0f}; + vec3 axis_z = {0.0f, 0.0f, 1.0f}; + + /* random angles for testing */ + vec3 angles; + + /* quaternion representations for rotations */ + versor rot_x, rot_y, rot_z; + + versor expected; + versor result; + versor tmp; + + mat4 expected_mat4; + + /* 100 randomized tests */ + for (int i = 0; i < 100; i++) { + test_rand_vec3(angles); + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in xzy order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + + glm_euler_xzy_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + /* verify that it acts the same as rotating by 3 axis quaternions */ + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_XZY, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + + + /* Start gimbal lock tests */ + for (float x = -90.0f; x <= 90.0f; x += 90.0f) { + for (float y = -90.0f; y <= 90.0f; y += 90.0f) { + for (float z = -90.0f; z <= 90.0f; z += 90.0f) { + angles[0] = x; + angles[1] = y; + angles[2] = z; + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in xzy order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + + /* use my function to get the quaternion */ + glm_euler_xzy_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_XZY, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + } + } + TEST_SUCCESS +} + +TEST_IMPL(GLM_PREFIX, euler_yxz_quat_rh) { + vec3 axis_x = {1.0f, 0.0f, 0.0f}; + vec3 axis_y = {0.0f, 1.0f, 0.0f}; + vec3 axis_z = {0.0f, 0.0f, 1.0f}; + + /* random angles for testing */ + vec3 angles; + + /* quaternion representations for rotations */ + versor rot_x, rot_y, rot_z; + + versor expected; + versor result; + versor tmp; + + mat4 expected_mat4; + + /* 100 randomized tests */ + for (int i = 0; i < 100; i++) { + test_rand_vec3(angles); + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in yxz order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + + glm_euler_yxz_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + /* verify that it acts the same as rotating by 3 axis quaternions */ + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_YXZ, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + + + /* Start gimbal lock tests */ + for (float x = -90.0f; x <= 90.0f; x += 90.0f) { + for (float y = -90.0f; y <= 90.0f; y += 90.0f) { + for (float z = -90.0f; z <= 90.0f; z += 90.0f) { + angles[0] = x; + angles[1] = y; + angles[2] = z; + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in yxz order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + + /* use my function to get the quaternion */ + glm_euler_yxz_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_YXZ, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + } + } + TEST_SUCCESS +} + +TEST_IMPL(GLM_PREFIX, euler_yzx_quat_rh) { + vec3 axis_x = {1.0f, 0.0f, 0.0f}; + vec3 axis_y = {0.0f, 1.0f, 0.0f}; + vec3 axis_z = {0.0f, 0.0f, 1.0f}; + + /* random angles for testing */ + vec3 angles; + + /* quaternion representations for rotations */ + versor rot_x, rot_y, rot_z; + + versor expected; + versor result; + versor tmp; + + mat4 expected_mat4; + + /* 100 randomized tests */ + for (int i = 0; i < 100; i++) { + test_rand_vec3(angles); + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in yzx order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + + glm_euler_yzx_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + /* verify that it acts the same as rotating by 3 axis quaternions */ + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_YZX, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + + + /* Start gimbal lock tests */ + for (float x = -90.0f; x <= 90.0f; x += 90.0f) { + for (float y = -90.0f; y <= 90.0f; y += 90.0f) { + for (float z = -90.0f; z <= 90.0f; z += 90.0f) { + angles[0] = x; + angles[1] = y; + angles[2] = z; + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in yzx order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + + /* use my function to get the quaternion */ + glm_euler_yzx_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_YZX, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + } + } + TEST_SUCCESS +} + +TEST_IMPL(GLM_PREFIX, euler_zxy_quat_rh) { + vec3 axis_x = {1.0f, 0.0f, 0.0f}; + vec3 axis_y = {0.0f, 1.0f, 0.0f}; + vec3 axis_z = {0.0f, 0.0f, 1.0f}; + + /* random angles for testing */ + vec3 angles; + + /* quaternion representations for rotations */ + versor rot_x, rot_y, rot_z; + + versor expected; + versor result; + versor tmp; + + mat4 expected_mat4; + + /* 100 randomized tests */ + for (int i = 0; i < 100; i++) { + test_rand_vec3(angles); + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in zxy order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + + glm_euler_zxy_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + /* verify that it acts the same as rotating by 3 axis quaternions */ + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_ZXY, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + + + /* Start gimbal lock tests */ + for (float x = -90.0f; x <= 90.0f; x += 90.0f) { + for (float y = -90.0f; y <= 90.0f; y += 90.0f) { + for (float z = -90.0f; z <= 90.0f; z += 90.0f) { + angles[0] = x; + angles[1] = y; + angles[2] = z; + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in zxy order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + + /* use my function to get the quaternion */ + glm_euler_zxy_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_ZXY, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + } + } + TEST_SUCCESS +} + +TEST_IMPL(GLM_PREFIX, euler_zyx_quat_rh) { + vec3 axis_x = {1.0f, 0.0f, 0.0f}; + vec3 axis_y = {0.0f, 1.0f, 0.0f}; + vec3 axis_z = {0.0f, 0.0f, 1.0f}; + + /* random angles for testing */ + vec3 angles; + + /* quaternion representations for rotations */ + versor rot_x, rot_y, rot_z; + + versor expected; + versor result; + versor tmp; + + mat4 expected_mat4; + + /* 100 randomized tests */ + for (int i = 0; i < 100; i++) { + test_rand_vec3(angles); + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in zyx order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + + glm_euler_zyx_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + /* verify that it acts the same as rotating by 3 axis quaternions */ + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_ZYX, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + + + /* Start gimbal lock tests */ + for (float x = -90.0f; x <= 90.0f; x += 90.0f) { + for (float y = -90.0f; y <= 90.0f; y += 90.0f) { + for (float z = -90.0f; z <= 90.0f; z += 90.0f) { + angles[0] = x; + angles[1] = y; + angles[2] = z; + + /* create the rotation quaternions using the angles and axises */ + glm_quatv(rot_x, angles[0], axis_x); + glm_quatv(rot_y, angles[1], axis_y); + glm_quatv(rot_z, angles[2], axis_z); + + /* apply the rotations to a unit quaternion in xyz order */ + glm_quat_identity(expected); + + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_z, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_y, expected); + glm_quat_copy(expected, tmp); + glm_quat_mul(tmp, rot_x, expected); + + /* use my function to get the quaternion */ + glm_euler_zyx_quat_rh(angles, result); + + /* verify if the magnitude of the quaternion stays 1 */ + ASSERT(test_eq(glm_quat_norm(result), 1.0f)) + + ASSERTIFY(test_assert_quat_eq(result, expected)) + + /* verify that it acts the same as glm_euler_by_order */ + glm_euler_by_order(angles, GLM_EULER_ZYX, expected_mat4); + glm_mat4_quat(expected_mat4, expected); + + ASSERTIFY(test_assert_quat_eq_abs(result, expected)); + } + } + } + TEST_SUCCESS +} + + diff --git a/test/src/tests.c b/test/src/tests.c index 4d1e2c9..274f7e0 100644 --- a/test/src/tests.c +++ b/test/src/tests.c @@ -39,6 +39,7 @@ #include "test_cam_lh_zo.h" #include "test_cam_rh_no.h" #include "test_cam_rh_zo.h" +#include "test_euler_to_quat_rh.h" #undef GLM #undef GLM_PREFIX @@ -76,6 +77,7 @@ #include "test_cam_lh_zo.h" #include "test_cam_rh_no.h" #include "test_cam_rh_zo.h" +#include "test_euler_to_quat_rh.h" #undef GLM #undef GLM_PREFIX diff --git a/test/tests.h b/test/tests.h index c707ce9..48d135e 100644 --- a/test/tests.h +++ b/test/tests.h @@ -360,12 +360,12 @@ TEST_DECLARE(glmc_plane_normalize) TEST_DECLARE(clamp) /* euler */ -TEST_DECLARE(glm_euler_xyz_quat) -TEST_DECLARE(glm_euler_xzy_quat) -TEST_DECLARE(glm_euler_yxz_quat) -TEST_DECLARE(glm_euler_yzx_quat) -TEST_DECLARE(glm_euler_zxy_quat) -TEST_DECLARE(glm_euler_zyx_quat) +TEST_DECLARE(glm_euler_xyz_quat_rh) +TEST_DECLARE(glm_euler_xzy_quat_rh) +TEST_DECLARE(glm_euler_yxz_quat_rh) +TEST_DECLARE(glm_euler_yzx_quat_rh) +TEST_DECLARE(glm_euler_zxy_quat_rh) +TEST_DECLARE(glm_euler_zyx_quat_rh) TEST_DECLARE(euler) /* ray */ @@ -1361,12 +1361,12 @@ TEST_LIST { TEST_ENTRY(clamp) /* euler */ - TEST_ENTRY(glm_euler_xyz_quat) - TEST_ENTRY(glm_euler_xzy_quat) - TEST_ENTRY(glm_euler_yxz_quat) - TEST_ENTRY(glm_euler_yzx_quat) - TEST_ENTRY(glm_euler_zxy_quat) - TEST_ENTRY(glm_euler_zyx_quat) + TEST_ENTRY(glm_euler_xyz_quat_rh) + TEST_ENTRY(glm_euler_xzy_quat_rh) + TEST_ENTRY(glm_euler_yxz_quat_rh) + TEST_ENTRY(glm_euler_yzx_quat_rh) + TEST_ENTRY(glm_euler_zxy_quat_rh) + TEST_ENTRY(glm_euler_zyx_quat_rh) TEST_ENTRY(euler) /* ray */