diff --git a/.gitignore b/.gitignore index 180c5d90..37f68c81 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,5 @@ cglm_test_ios/* cglm_test_iosTests/* docs/build/* win/cglm_test_* -* copy.* +* copy.* +*.o diff --git a/CREDITS b/CREDITS index 810f0b4f..44e55a81 100644 --- a/CREDITS +++ b/CREDITS @@ -43,3 +43,10 @@ https://github.com/erich666/GraphicsGems/blob/master/gems/TransBox.c 6. Cull frustum http://www.txutxi.com/?p=584 http://old.cescg.org/CESCG-2002/DSykoraJJelinek/ + +7. Quaternions +Initial mat4_quat is borrowed from Apple's simd library + + +8. Vector Rotation using Quaternion +https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion diff --git a/configure.ac b/configure.ac index afb694b9..6e222485 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ #***************************************************************************** AC_PREREQ([2.69]) -AC_INIT([cglm], [0.3.6], [info@recp.me]) +AC_INIT([cglm], [0.4.0], [info@recp.me]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/docs/source/quat.rst b/docs/source/quat.rst index 863eed52..ab998381 100644 --- a/docs/source/quat.rst +++ b/docs/source/quat.rst @@ -5,17 +5,16 @@ quaternions Header: cglm/quat.h - **Important:** *cglm* stores quaternion as [w, x, y, z] in memory, don't - forget that when changing quaternion items manually. For instance *quat[3]* - is *quat.z* and *quat[0*] is *quat.w*. This may change in the future if *cglm* - will got enough request to do that. Probably it will not be changed in near - future + **Important:** *cglm* stores quaternion as **[x, y, z, w]** in memory + since **v0.4.0** it was **[w, x, y, z]** + before v0.4.0 ( **v0.3.5 and earlier** ). w is real part. -There are some TODOs for quaternions check TODO list to see them. +What you can do with quaternions with existing functions is (Some of them): -Also **versor** is identity quaternion so the type may change to **vec4** or -something else. This will not affect existing functions for your engine because -*versor* is alias of *vec4* +- You can rotate transform matrix using quaterion +- You can rotate vector using quaterion +- You can create view matrix using quaterion +- You can create a lookrotation (from source point to dest) Table of contents (click to go): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -28,14 +27,35 @@ Macros: Functions: 1. :c:func:`glm_quat_identity` +#. :c:func:`glm_quat_init` #. :c:func:`glm_quat` #. :c:func:`glm_quatv` +#. :c:func:`glm_quat_copy` #. :c:func:`glm_quat_norm` #. :c:func:`glm_quat_normalize` +#. :c:func:`glm_quat_normalize_to` #. :c:func:`glm_quat_dot` -#. :c:func:`glm_quat_mulv` +#. :c:func:`glm_quat_conjugate` +#. :c:func:`glm_quat_inv` +#. :c:func:`glm_quat_add` +#. :c:func:`glm_quat_sub` +#. :c:func:`glm_quat_real` +#. :c:func:`glm_quat_imag` +#. :c:func:`glm_quat_imagn` +#. :c:func:`glm_quat_imaglen` +#. :c:func:`glm_quat_angle` +#. :c:func:`glm_quat_axis` +#. :c:func:`glm_quat_mul` #. :c:func:`glm_quat_mat4` +#. :c:func:`glm_quat_mat4t` +#. :c:func:`glm_quat_mat3` +#. :c:func:`glm_quat_mat3t` +#. :c:func:`glm_quat_lerp` #. :c:func:`glm_quat_slerp` +#. :c:func:`glm_quat_look` +#. :c:func:`glm_quat_for` +#. :c:func:`glm_quat_forp` +#. :c:func:`glm_quat_rotatev` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -47,10 +67,23 @@ Functions documentation Parameters: | *[in, out]* **q** quaternion +.. c:function:: void glm_quat_init(versor q, float x, float y, float z, float w) + + | inits quaternion with given values + + Parameters: + | *[out]* **q** quaternion + | *[in]* **x** imag.x + | *[in]* **y** imag.y + | *[in]* **z** imag.z + | *[in]* **w** w (real part) + .. c:function:: void glm_quat(versor q, float angle, float x, float y, float z) | creates NEW quaternion with individual axis components + | given axis will be normalized + Parameters: | *[out]* **q** quaternion | *[in]* **angle** angle (radians) @@ -58,14 +91,24 @@ Functions documentation | *[in]* **y** axis.y | *[in]* **z** axis.z -.. c:function:: void glm_quatv(versor q, float angle, vec3 v) +.. c:function:: void glm_quatv(versor q, float angle, vec3 axis) | creates NEW quaternion with axis vector + | given axis will be normalized + Parameters: | *[out]* **q** quaternion | *[in]* **angle** angle (radians) - | *[in]* **v** axis + | *[in]* **axis** axis (will be normalized) + +.. c:function:: void glm_quat_copy(versor q, versor dest) + + | copy quaternion to another one + + Parameters: + | *[in]* **q** source quaternion + | *[out]* **dest** destination quaternion .. c:function:: float glm_quat_norm(versor q) @@ -77,6 +120,14 @@ Functions documentation Returns: norm (magnitude) +.. c:function:: void glm_quat_normalize_to(versor q, versor dest) + + | normalize quaternion and store result in dest, original one will not be normalized + + Parameters: + | *[in]* **q** quaternion to normalize into + | *[out]* **dest** destination quaternion + .. c:function:: void glm_quat_normalize(versor q) | normalize quaternion @@ -84,24 +135,118 @@ Functions documentation Parameters: | *[in, out]* **q** quaternion -.. c:function:: float glm_quat_dot(versor q, versor r) +.. c:function:: float glm_quat_dot(versor p, versor q) dot product of two quaternion Parameters: - | *[in]* **q1** quaternion 1 - | *[in]* **q2** quaternion 2 + | *[in]* **p** quaternion 1 + | *[in]* **q** quaternion 2 Returns: dot product -.. c:function:: void glm_quat_mulv(versor q1, versor q2, versor dest) +.. c:function:: void glm_quat_conjugate(versor q, versor dest) + + conjugate of quaternion + + Parameters: + | *[in]* **q** quaternion + | *[in]* **dest** conjugate + +.. c:function:: void glm_quat_inv(versor q, versor dest) + + inverse of non-zero quaternion + + Parameters: + | *[in]* **q** quaternion + | *[in]* **dest** inverse quaternion + +.. c:function:: void glm_quat_add(versor p, versor q, versor dest) + + add (componentwise) two quaternions and store result in dest + + Parameters: + | *[in]* **p** quaternion 1 + | *[in]* **q** quaternion 2 + | *[in]* **dest** result quaternion + +.. c:function:: void glm_quat_sub(versor p, versor q, versor dest) + + subtract (componentwise) two quaternions and store result in dest + + Parameters: + | *[in]* **p** quaternion 1 + | *[in]* **q** quaternion 2 + | *[in]* **dest** result quaternion + +.. c:function:: float glm_quat_real(versor q) + + returns real part of quaternion + + Parameters: + | *[in]* **q** quaternion + + Returns: + real part (quat.w) + +.. c:function:: void glm_quat_imag(versor q, vec3 dest) + + returns imaginary part of quaternion + + Parameters: + | *[in]* **q** quaternion + | *[out]* **dest** imag + +.. c:function:: void glm_quat_imagn(versor q, vec3 dest) + + returns normalized imaginary part of quaternion + + Parameters: + | *[in]* **q** quaternion + | *[out]* **dest** imag + +.. c:function:: float glm_quat_imaglen(versor q) + + returns length of imaginary part of quaternion + + Parameters: + | *[in]* **q** quaternion + + Returns: + norm of imaginary part + +.. c:function:: float glm_quat_angle(versor q) + + returns angle of quaternion + + Parameters: + | *[in]* **q** quaternion + + Returns: + angles of quat (radians) + +.. c:function:: void glm_quat_axis(versor q, versor dest) + + axis of quaternion + + Parameters: + | *[in]* **p** quaternion + | *[out]* **dest** axis of quaternion + +.. c:function:: void glm_quat_mul(versor p, versor q, versor dest) | multiplies two quaternion and stores result in dest + | this is also called Hamilton Product + + | According to WikiPedia: + | The product of two rotation quaternions [clarification needed] will be + equivalent to the rotation q followed by the rotation p + Parameters: - | *[in]* **q1** quaternion 1 - | *[in]* **q2** quaternion 2 + | *[in]* **p** quaternion 1 (first rotation) + | *[in]* **q** quaternion 2 (second rotation) | *[out]* **dest** result quaternion .. c:function:: void glm_quat_mat4(versor q, mat4 dest) @@ -112,13 +257,100 @@ Functions documentation | *[in]* **q** quaternion | *[out]* **dest** result matrix +.. c:function:: void glm_quat_mat4t(versor q, mat4 dest) + + | convert quaternion to mat4 (transposed). This is transposed version of glm_quat_mat4 + + Parameters: + | *[in]* **q** quaternion + | *[out]* **dest** result matrix + +.. c:function:: void glm_quat_mat3(versor q, mat3 dest) + + | convert quaternion to mat3 + + Parameters: + | *[in]* **q** quaternion + | *[out]* **dest** result matrix + +.. c:function:: void glm_quat_mat3t(versor q, mat3 dest) + + | convert quaternion to mat3 (transposed). This is transposed version of glm_quat_mat3 + + Parameters: + | *[in]* **q** quaternion + | *[out]* **dest** result matrix + +.. c:function:: void glm_quat_lerp(versor from, versor to, float t, versor dest) + + | interpolates between two quaternions + | using spherical linear interpolation (LERP) + + Parameters: + | *[in]* **from** from + | *[in]* **to** to + | *[in]* **t** interpolant (amount) clamped between 0 and 1 + | *[out]* **dest** result quaternion + .. c:function:: void glm_quat_slerp(versor q, versor r, float t, versor dest) | interpolates between two quaternions | using spherical linear interpolation (SLERP) Parameters: - | *[in]* **q** from - | *[in]* **r** to - | *[in]* **t** amout + | *[in]* **from** from + | *[in]* **to** to + | *[in]* **t** interpolant (amount) clamped between 0 and 1 | *[out]* **dest** result quaternion + +.. c:function:: void glm_quat_look(vec3 eye, versor ori, mat4 dest) + + | creates view matrix using quaternion as camera orientation + + Parameters: + | *[in]* **eye** eye + | *[in]* **ori** orientation in world space as quaternion + | *[out]* **dest** result matrix + +.. c:function:: void glm_quat_for(vec3 dir, vec3 fwd, vec3 up, versor dest) + + | creates look rotation quaternion + + Parameters: + | *[in]* **dir** direction to look + | *[in]* **fwd** forward vector + | *[in]* **up** up vector + | *[out]* **dest** result matrix + +.. c:function:: void glm_quat_forp(vec3 from, vec3 to, vec3 fwd, vec3 up, versor dest) + + | creates look rotation quaternion using source and destination positions p suffix stands for position + + | this is similar to glm_quat_for except this computes direction for glm_quat_for for you. + + Parameters: + | *[in]* **from** source point + | *[in]* **to** destination point + | *[in]* **fwd** forward vector + | *[in]* **up** up vector + | *[out]* **dest** result matrix + +.. c:function:: void glm_quat_rotatev(versor q, vec3 v, vec3 dest) + + | crotate vector using using quaternion + + Parameters: + | *[in]* **q** quaternion + | *[in]* **v** vector to rotate + | *[out]* **dest** rotated vector + +.. c:function:: void glm_quat_rotate(mat4 m, versor q, mat4 dest) + + | rotate existing transform matrix using quaternion + + instead of passing identity matrix, consider to use quat_mat4 functions + + Parameters: + | *[in]* **m** existing transform matrix to rotate + | *[in]* **q** quaternion + | *[out]* **dest** rotated matrix/transform diff --git a/docs/source/util.rst b/docs/source/util.rst index a9f4066d..f8dbac4b 100644 --- a/docs/source/util.rst +++ b/docs/source/util.rst @@ -22,6 +22,7 @@ Functions: #. :c:func:`glm_min` #. :c:func:`glm_max` #. :c:func:`glm_clamp` +#. :c:func:`glm_lerp` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -121,3 +122,17 @@ Functions documentation Returns: clamped value + +.. c:function:: float glm_lerp(float from, float to, float t) + + linear interpolation between two number + + | formula: from + s * (to - from) + + Parameters: + | *[in]* **from** from value + | *[in]* **to** to value + | *[in]* **t** interpolant (amount) clamped between 0 and 1 + + Returns: + interpolated value diff --git a/docs/source/vec3-ext.rst b/docs/source/vec3-ext.rst index e632de9e..c2c0bfc7 100644 --- a/docs/source/vec3-ext.rst +++ b/docs/source/vec3-ext.rst @@ -23,6 +23,11 @@ Functions: #. :c:func:`glm_vec_eqv_eps` #. :c:func:`glm_vec_max` #. :c:func:`glm_vec_min` +#. :c:func:`glm_vec_isnan` +#. :c:func:`glm_vec_isinf` +#. :c:func:`glm_vec_isvalid` +#. :c:func:`glm_vec_sign` +#. :c:func:`glm_vec_sqrt` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -96,3 +101,43 @@ Functions documentation Parameters: | *[in]* **v** vector + +.. c:function:: bool glm_vec_isnan(vec3 v) + + | check if one of items is NaN (not a number) + | you should only use this in DEBUG mode or very critical asserts + + Parameters: + | *[in]* **v** vector + +.. c:function:: bool glm_vec_isinf(vec3 v) + + | check if one of items is INFINITY + | you should only use this in DEBUG mode or very critical asserts + + Parameters: + | *[in]* **v** vector + +.. c:function:: bool glm_vec_isvalid(vec3 v) + + | check if all items are valid number + | you should only use this in DEBUG mode or very critical asserts + + Parameters: + | *[in]* **v** vector + +.. c:function:: void glm_vec_sign(vec3 v, vec3 dest) + + get sign of 32 bit float as +1, -1, 0 + + Parameters: + | *[in]* **v** vector + | *[out]* **dest** sign vector (only keeps signs as -1, 0, -1) + +.. c:function:: void glm_vec_sqrt(vec3 v, vec3 dest) + + square root of each vector item + + Parameters: + | *[in]* **v** vector + | *[out]* **dest** destination vector (sqrt(v)) diff --git a/docs/source/vec3.rst b/docs/source/vec3.rst index 355178d7..8529333d 100644 --- a/docs/source/vec3.rst +++ b/docs/source/vec3.rst @@ -40,6 +40,7 @@ Functions: #. :c:func:`glm_vec_scale` #. :c:func:`glm_vec_scale_as` #. :c:func:`glm_vec_flipsign` +#. :c:func:`glm_vec_flipsign_to` #. :c:func:`glm_vec_inv` #. :c:func:`glm_vec_inv_to` #. :c:func:`glm_vec_normalize` @@ -54,6 +55,7 @@ Functions: #. :c:func:`glm_vec_minv` #. :c:func:`glm_vec_ortho` #. :c:func:`glm_vec_clamp` +#. :c:func:`glm_vec_lerp` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -157,7 +159,15 @@ Functions documentation flip sign of all vec3 members Parameters: - | *[in, out]* **v** vector + | *[in, out]* **v** vector + +.. c:function:: void glm_vec_flipsign_to(vec3 v, vec3 dest) + + flip sign of all vec3 members and store result in dest + + Parameters: + | *[in]* **v** vector + | *[out]* **dest** negated vector .. c:function:: void glm_vec_inv(vec3 v) @@ -206,7 +216,7 @@ Functions documentation Parameters: | *[in, out]* **v** vector - | *[in]* **axis** axis vector (must be unit vector) + | *[in]* **axis** axis vector (will be normalized) | *[out]* **angle** angle (radians) .. c:function:: void glm_vec_rotate_m4(mat4 m, vec3 v, vec3 dest) @@ -281,3 +291,15 @@ Functions documentation | *[in, out]* **v** vector | *[in]* **minVal** minimum value | *[in]* **maxVal** maximum value + +.. c:function:: void glm_vec_lerp(vec3 from, vec3 to, float t, vec3 dest) + + linear interpolation between two vector + + | formula: from + s * (to - from) + + Parameters: + | *[in]* **from** from value + | *[in]* **to** to value + | *[in]* **t** interpolant (amount) clamped between 0 and 1 + | *[out]* **dest** destination diff --git a/docs/source/vec4-ext.rst b/docs/source/vec4-ext.rst index 11613ad2..722424ee 100644 --- a/docs/source/vec4-ext.rst +++ b/docs/source/vec4-ext.rst @@ -96,3 +96,43 @@ Functions documentation Parameters: | *[in]* **v** vector + +.. c:function:: bool glm_vec4_isnan(vec4 v) + + | check if one of items is NaN (not a number) + | you should only use this in DEBUG mode or very critical asserts + + Parameters: + | *[in]* **v** vector + +.. c:function:: bool glm_vec4_isinf(vec4 v) + + | check if one of items is INFINITY + | you should only use this in DEBUG mode or very critical asserts + + Parameters: + | *[in]* **v** vector + +.. c:function:: bool glm_vec4_isvalid(vec4 v) + + | check if all items are valid number + | you should only use this in DEBUG mode or very critical asserts + + Parameters: + | *[in]* **v** vector + +.. c:function:: void glm_vec4_sign(vec4 v, vec4 dest) + + get sign of 32 bit float as +1, -1, 0 + + Parameters: + | *[in]* **v** vector + | *[out]* **dest** sign vector (only keeps signs as -1, 0, -1) + +.. c:function:: void glm_vec4_sqrt(vec4 v, vec4 dest) + + square root of each vector item + + Parameters: + | *[in]* **v** vector + | *[out]* **dest** destination vector (sqrt(v)) diff --git a/docs/source/vec4.rst b/docs/source/vec4.rst index ac1b9c5e..69552466 100644 --- a/docs/source/vec4.rst +++ b/docs/source/vec4.rst @@ -32,6 +32,7 @@ Functions: #. :c:func:`glm_vec4_scale` #. :c:func:`glm_vec4_scale_as` #. :c:func:`glm_vec4_flipsign` +#. :c:func:`glm_vec_flipsign_to` #. :c:func:`glm_vec4_inv` #. :c:func:`glm_vec4_inv_to` #. :c:func:`glm_vec4_normalize` @@ -40,6 +41,12 @@ Functions: #. :c:func:`glm_vec4_maxv` #. :c:func:`glm_vec4_minv` #. :c:func:`glm_vec4_clamp` +#. :c:func:`glm_vec4_lerp` +#. :c:func:`glm_vec4_isnan` +#. :c:func:`glm_vec4_isinf` +#. :c:func:`glm_vec4_isvalid` +#. :c:func:`glm_vec4_sign` +#. :c:func:`glm_vec4_sqrt` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -146,6 +153,14 @@ Functions documentation Parameters: | *[in, out]* **v** vector +.. c:function:: void glm_vec4_flipsign_to(vec4 v, vec4 dest) + + flip sign of all vec4 members and store result in dest + + Parameters: + | *[in]* **v** vector + | *[out]* **dest** negated vector + .. c:function:: void glm_vec4_inv(vec4 v) make vector as inverse/opposite of itself @@ -213,3 +228,15 @@ Functions documentation | *[in, out]* **v** vector | *[in]* **minVal** minimum value | *[in]* **maxVal** maximum value + +.. c:function:: void glm_vec4_lerp(vec4 from, vec4 to, float t, vec4 dest) + + linear interpolation between two vector + + | formula: from + s * (to - from) + + Parameters: + | *[in]* **from** from value + | *[in]* **to** to value + | *[in]* **t** interpolant (amount) clamped between 0 and 1 + | *[out]* **dest** destination diff --git a/include/cglm/call/mat4.h b/include/cglm/call/mat4.h index 6ea81e47..c9ad796c 100644 --- a/include/cglm/call/mat4.h +++ b/include/cglm/call/mat4.h @@ -47,12 +47,16 @@ glmc_mat4_mul(mat4 m1, mat4 m2, mat4 dest); CGLM_EXPORT void -glmc_mat4_mulN(mat4 * __restrict matrices[], int len, mat4 dest); +glmc_mat4_mulN(mat4 * __restrict matrices[], uint32_t len, mat4 dest); CGLM_EXPORT void glmc_mat4_mulv(mat4 m, vec4 v, vec4 dest); +CGLM_EXPORT +void +glmc_mat4_quat(mat4 m, versor dest); + CGLM_EXPORT void glmc_mat4_transpose_to(mat4 m, mat4 dest); diff --git a/include/cglm/call/quat.h b/include/cglm/call/quat.h index 0dff5060..d250f52a 100644 --- a/include/cglm/call/quat.h +++ b/include/cglm/call/quat.h @@ -19,33 +19,79 @@ glmc_quat_identity(versor q); CGLM_EXPORT void -glmc_quat(versor q, - float angle, - float x, - float y, - float z); +glmc_quat_init(versor q, float x, float y, float z, float w); CGLM_EXPORT void -glmc_quatv(versor q, - float angle, - vec3 v); +glmc_quat(versor q, float angle, float x, float y, float z); + +CGLM_EXPORT +void +glmc_quatv(versor q, float angle, vec3 axis); + +CGLM_EXPORT +void +glmc_quat_copy(versor q, versor dest); CGLM_EXPORT float glmc_quat_norm(versor q); +CGLM_EXPORT +void +glmc_quat_normalize_to(versor q, versor dest); + CGLM_EXPORT void glmc_quat_normalize(versor q); CGLM_EXPORT float -glmc_quat_dot(versor q, versor r); +glmc_quat_dot(versor p, versor q); + +CGLM_EXPORT +void +glmc_quat_conjugate(versor q, versor dest); + +CGLM_EXPORT +void +glmc_quat_inv(versor q, versor dest); + +CGLM_EXPORT +void +glmc_quat_add(versor p, versor q, versor dest); + +CGLM_EXPORT +void +glmc_quat_sub(versor p, versor q, versor dest); + +CGLM_EXPORT +float +glmc_quat_real(versor q); + +CGLM_EXPORT +void +glmc_quat_imag(versor q, vec3 dest); + +CGLM_EXPORT +void +glmc_quat_imagn(versor q, vec3 dest); + +CGLM_EXPORT +float +glmc_quat_imaglen(versor q); + +CGLM_EXPORT +float +glmc_quat_angle(versor q); CGLM_EXPORT void -glmc_quat_mulv(versor q1, versor q2, versor dest); +glmc_quat_axis(versor q, versor dest); + +CGLM_EXPORT +void +glmc_quat_mul(versor p, versor q, versor dest); CGLM_EXPORT void @@ -53,10 +99,43 @@ glmc_quat_mat4(versor q, mat4 dest); CGLM_EXPORT void -glmc_quat_slerp(versor q, - versor r, - float t, - versor dest); +glmc_quat_mat4t(versor q, mat4 dest); + +CGLM_EXPORT +void +glmc_quat_mat3(versor q, mat3 dest); + +CGLM_EXPORT +void +glmc_quat_mat3t(versor q, mat3 dest); + +CGLM_EXPORT +void +glmc_quat_lerp(versor from, versor to, float t, versor dest); + +CGLM_EXPORT +void +glmc_quat_slerp(versor q, versor r, float t, versor dest); + +CGLM_EXPORT +void +glmc_quat_look(vec3 eye, versor ori, mat4 dest); + +CGLM_EXPORT +void +glmc_quat_for(vec3 dir, vec3 fwd, vec3 up, versor dest); + +CGLM_EXPORT +void +glmc_quat_forp(vec3 from, vec3 to, vec3 fwd, vec3 up, versor dest); + +CGLM_EXPORT +void +glmc_quat_rotatev(versor from, vec3 to, vec3 dest); + +CGLM_EXPORT +void +glmc_quat_rotate(mat4 m, versor q, mat4 dest); #ifdef __cplusplus } diff --git a/include/cglm/call/vec3.h b/include/cglm/call/vec3.h index 461de0bd..e79ec8f8 100644 --- a/include/cglm/call/vec3.h +++ b/include/cglm/call/vec3.h @@ -16,6 +16,10 @@ extern "C" { /* DEPRECATED! use _copy, _ucopy versions */ #define glmc_vec_dup(v, dest) glmc_vec_copy(v, dest) +CGLM_EXPORT +void +glmc_vec3(vec4 v4, vec3 dest); + CGLM_EXPORT void glmc_vec_copy(vec3 a, vec3 dest); @@ -64,6 +68,10 @@ CGLM_EXPORT void glmc_vec_flipsign(vec3 v); +CGLM_EXPORT +void +glmc_vec_flipsign_to(vec3 v, vec3 dest); + CGLM_EXPORT void glmc_vec_inv(vec3 v); @@ -108,6 +116,72 @@ CGLM_EXPORT void glmc_vec_clamp(vec3 v, float minVal, float maxVal); +CGLM_EXPORT +void +glmc_vec_ortho(vec3 v, vec3 dest); + +CGLM_EXPORT +void +glmc_vec_lerp(vec3 from, vec3 to, float t, vec3 dest); + +/* ext */ + +CGLM_EXPORT +void +glmc_vec_mulv(vec3 a, vec3 b, vec3 d); + +CGLM_EXPORT +void +glmc_vec_broadcast(float val, vec3 d); + +CGLM_EXPORT +bool +glmc_vec_eq(vec3 v, float val); + +CGLM_EXPORT +bool +glmc_vec_eq_eps(vec3 v, float val); + +CGLM_EXPORT +bool +glmc_vec_eq_all(vec3 v); + +CGLM_EXPORT +bool +glmc_vec_eqv(vec3 v1, vec3 v2); + +CGLM_EXPORT +bool +glmc_vec_eqv_eps(vec3 v1, vec3 v2); + +CGLM_EXPORT +float +glmc_vec_max(vec3 v); + +CGLM_EXPORT +float +glmc_vec_min(vec3 v); + +CGLM_EXPORT +bool +glmc_vec_isnan(vec3 v); + +CGLM_EXPORT +bool +glmc_vec_isinf(vec3 v); + +CGLM_EXPORT +bool +glmc_vec_isvalid(vec3 v); + +CGLM_EXPORT +void +glmc_vec_sign(vec3 v, vec3 dest); + +CGLM_EXPORT +void +glmc_vec_sqrt(vec3 v, vec3 dest); + #ifdef __cplusplus } #endif diff --git a/include/cglm/call/vec4.h b/include/cglm/call/vec4.h index b63af801..ad915e64 100644 --- a/include/cglm/call/vec4.h +++ b/include/cglm/call/vec4.h @@ -17,6 +17,10 @@ extern "C" { #define glmc_vec4_dup3(v, dest) glmc_vec4_copy3(v, dest) #define glmc_vec4_dup(v, dest) glmc_vec4_copy(v, dest) +CGLM_EXPORT +void +glmc_vec4(vec3 v3, float last, vec4 dest); + CGLM_EXPORT void glmc_vec4_copy3(vec4 a, vec3 dest); @@ -65,6 +69,10 @@ CGLM_EXPORT void glmc_vec4_flipsign(vec4 v); +CGLM_EXPORT +void +glmc_vec4_flipsign_to(vec4 v, vec4 dest); + CGLM_EXPORT void glmc_vec4_inv(vec4 v); @@ -89,6 +97,68 @@ CGLM_EXPORT void glmc_vec4_clamp(vec4 v, float minVal, float maxVal); +CGLM_EXPORT +void +glmc_vec4_lerp(vec4 from, vec4 to, float t, vec4 dest); + +/* ext */ + +CGLM_EXPORT +void +glmc_vec4_mulv(vec4 a, vec4 b, vec4 d); + +CGLM_EXPORT +void +glmc_vec4_broadcast(float val, vec4 d); + +CGLM_EXPORT +bool +glmc_vec4_eq(vec4 v, float val); + +CGLM_EXPORT +bool +glmc_vec4_eq_eps(vec4 v, float val); + +CGLM_EXPORT +bool +glmc_vec4_eq_all(vec4 v); + +CGLM_EXPORT +bool +glmc_vec4_eqv(vec4 v1, vec4 v2); + +CGLM_EXPORT +bool +glmc_vec4_eqv_eps(vec4 v1, vec4 v2); + +CGLM_EXPORT +float +glmc_vec4_max(vec4 v); + +CGLM_EXPORT +float +glmc_vec4_min(vec4 v); + +CGLM_EXPORT +bool +glmc_vec4_isnan(vec4 v); + +CGLM_EXPORT +bool +glmc_vec4_isinf(vec4 v); + +CGLM_EXPORT +bool +glmc_vec4_isvalid(vec4 v); + +CGLM_EXPORT +void +glmc_vec4_sign(vec4 v, vec4 dest); + +CGLM_EXPORT +void +glmc_vec4_sqrt(vec4 v, vec4 dest); + #ifdef __cplusplus } #endif diff --git a/include/cglm/mat3.h b/include/cglm/mat3.h index 61c4f3d4..9512aef8 100644 --- a/include/cglm/mat3.h +++ b/include/cglm/mat3.h @@ -186,6 +186,56 @@ glm_mat3_mulv(mat3 m, vec3 v, vec3 dest) { dest[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2]; } + +/*! + * @brief convert mat4's rotation part to quaternion + * + * @param[in] m left matrix + * @param[out] dest destination quaternion + */ +CGLM_INLINE +void +glm_mat3_quat(mat3 m, versor dest) { + float trace, r, rinv; + + /* it seems using like m12 instead of m[1][2] causes extra instructions */ + + trace = m[0][0] + m[1][1] + m[2][2]; + if (trace >= 0.0f) { + r = sqrtf(1.0f + trace); + rinv = 0.5f / r; + + dest[0] = rinv * (m[1][2] - m[2][1]); + dest[1] = rinv * (m[2][0] - m[0][2]); + dest[2] = rinv * (m[0][1] - m[1][0]); + dest[3] = r * 0.5f; + } else if (m[0][0] >= m[1][1] && m[0][0] >= m[2][2]) { + r = sqrtf(1.0f - m[1][1] - m[2][2] + m[0][0]); + rinv = 0.5f / r; + + dest[0] = r * 0.5f; + dest[1] = rinv * (m[0][1] + m[1][0]); + dest[2] = rinv * (m[0][2] + m[2][0]); + dest[3] = rinv * (m[1][2] - m[2][1]); + } else if (m[1][1] >= m[2][2]) { + r = sqrtf(1.0f - m[0][0] - m[2][2] + m[1][1]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][1] + m[1][0]); + dest[1] = r * 0.5f; + dest[2] = rinv * (m[1][2] + m[2][1]); + dest[3] = rinv * (m[2][0] - m[0][2]); + } else { + r = sqrtf(1.0f - m[0][0] - m[1][1] + m[2][2]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][2] + m[2][0]); + dest[1] = rinv * (m[1][2] + m[2][1]); + dest[2] = r * 0.5f; + dest[3] = rinv * (m[0][1] - m[1][0]); + } +} + /*! * @brief scale (multiply with scalar) matrix * diff --git a/include/cglm/mat4.h b/include/cglm/mat4.h index 45f54b4a..27ebe05d 100644 --- a/include/cglm/mat4.h +++ b/include/cglm/mat4.h @@ -45,6 +45,7 @@ #define cglm_mat_h #include "common.h" +#include "quat.h" #ifdef CGLM_SSE_FP # include "simd/sse2/mat4.h" @@ -58,7 +59,9 @@ # include "simd/neon/mat4.h" #endif -#include +#ifdef DEBUG +# include +#endif #define GLM_MAT4_IDENTITY_INIT {{1.0f, 0.0f, 0.0f, 0.0f}, \ {0.0f, 1.0f, 0.0f, 0.0f}, \ @@ -281,19 +284,17 @@ glm_mat4_mul(mat4 m1, mat4 m2, mat4 dest) { */ CGLM_INLINE void -glm_mat4_mulN(mat4 * __restrict matrices[], int len, mat4 dest) { - int i; +glm_mat4_mulN(mat4 * __restrict matrices[], uint32_t len, mat4 dest) { + uint32_t i; +#ifdef DEBUG assert(len > 1 && "there must be least 2 matrices to go!"); +#endif - glm_mat4_mul(*matrices[0], - *matrices[1], - dest); + glm_mat4_mul(*matrices[0], *matrices[1], dest); for (i = 2; i < len; i++) - glm_mat4_mul(dest, - *matrices[i], - dest); + glm_mat4_mul(dest, *matrices[i], dest); } /*! @@ -318,6 +319,55 @@ glm_mat4_mulv(mat4 m, vec4 v, vec4 dest) { #endif } +/*! + * @brief convert mat4's rotation part to quaternion + * + * @param[in] m left matrix + * @param[out] dest destination quaternion + */ +CGLM_INLINE +void +glm_mat4_quat(mat4 m, versor dest) { + float trace, r, rinv; + + /* it seems using like m12 instead of m[1][2] causes extra instructions */ + + trace = m[0][0] + m[1][1] + m[2][2]; + if (trace >= 0.0f) { + r = sqrtf(1.0f + trace); + rinv = 0.5f / r; + + dest[0] = rinv * (m[1][2] - m[2][1]); + dest[1] = rinv * (m[2][0] - m[0][2]); + dest[2] = rinv * (m[0][1] - m[1][0]); + dest[3] = r * 0.5f; + } else if (m[0][0] >= m[1][1] && m[0][0] >= m[2][2]) { + r = sqrtf(1.0f - m[1][1] - m[2][2] + m[0][0]); + rinv = 0.5f / r; + + dest[0] = r * 0.5f; + dest[1] = rinv * (m[0][1] + m[1][0]); + dest[2] = rinv * (m[0][2] + m[2][0]); + dest[3] = rinv * (m[1][2] - m[2][1]); + } else if (m[1][1] >= m[2][2]) { + r = sqrtf(1.0f - m[0][0] - m[2][2] + m[1][1]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][1] + m[1][0]); + dest[1] = r * 0.5f; + dest[2] = rinv * (m[1][2] + m[2][1]); + dest[3] = rinv * (m[2][0] - m[0][2]); + } else { + r = sqrtf(1.0f - m[0][0] - m[1][1] + m[2][2]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][2] + m[2][0]); + dest[1] = rinv * (m[1][2] + m[2][1]); + dest[2] = r * 0.5f; + dest[3] = rinv * (m[0][1] - m[1][0]); + } +} + /*! * @brief multiply vector with mat4's mat3 part(rotation) * @@ -568,5 +618,4 @@ glm_mat4_swap_row(mat4 mat, int row1, int row2) { mat[3][row2] = tmp[3]; } -#else #endif /* cglm_mat_h */ diff --git a/include/cglm/quat.h b/include/cglm/quat.h index 63236b16..d82a301d 100644 --- a/include/cglm/quat.h +++ b/include/cglm/quat.h @@ -11,15 +11,41 @@ GLM_QUAT_IDENTITY Functions: - CGLM_INLINE void glm_quat_identity(versor q); - CGLM_INLINE void glm_quat(versor q, float angle, float x, float y, float z); - CGLM_INLINE void glm_quatv(versor q, float angle, vec3 v); + CGLM_INLINE void glm_quat_identity(versor q); + CGLM_INLINE void glm_quat_init(versor q, float x, float y, float z, float w); + CGLM_INLINE void glm_quat(versor q, float angle, float x, float y, float z); + CGLM_INLINE void glm_quatv(versor q, float angle, vec3 axis); + CGLM_INLINE void glm_quat_copy(versor q, versor dest); CGLM_INLINE float glm_quat_norm(versor q); - CGLM_INLINE void glm_quat_normalize(versor q); - CGLM_INLINE float glm_quat_dot(versor q, versor r); - CGLM_INLINE void glm_quat_mulv(versor q1, versor q2, versor dest); - CGLM_INLINE void glm_quat_mat4(versor q, mat4 dest); - CGLM_INLINE void glm_quat_slerp(versor q, versor r, float t, versor dest); + CGLM_INLINE void glm_quat_normalize(versor q); + CGLM_INLINE void glm_quat_normalize_to(versor q, versor dest); + CGLM_INLINE float glm_quat_dot(versor q1, versor q2); + CGLM_INLINE void glm_quat_conjugate(versor q, versor dest); + CGLM_INLINE void glm_quat_inv(versor q, versor dest); + CGLM_INLINE void glm_quat_add(versor p, versor q, versor dest); + CGLM_INLINE void glm_quat_sub(versor p, versor q, versor dest); + CGLM_INLINE float glm_quat_real(versor q); + CGLM_INLINE void glm_quat_imag(versor q, vec3 dest); + CGLM_INLINE void glm_quat_imagn(versor q, vec3 dest); + CGLM_INLINE float glm_quat_imaglen(versor q); + CGLM_INLINE float glm_quat_angle(versor q); + CGLM_INLINE void glm_quat_axis(versor q, versor dest); + CGLM_INLINE void glm_quat_mul(versor p, versor q, versor dest); + CGLM_INLINE void glm_quat_mat4(versor q, mat4 dest); + CGLM_INLINE void glm_quat_mat4t(versor q, mat4 dest); + CGLM_INLINE void glm_quat_mat3(versor q, mat3 dest); + CGLM_INLINE void glm_quat_mat3t(versor q, mat3 dest); + CGLM_INLINE void glm_quat_lerp(versor from, versor to, float t, versor dest); + CGLM_INLINE void glm_quat_slerp(versor q, versor r, float t, versor dest); + CGLM_INLINE void glm_quat_look(vec3 eye, versor ori, mat4 dest); + CGLM_INLINE void glm_quat_for(vec3 dir, vec3 fwd, vec3 up, versor dest); + CGLM_INLINE void glm_quat_forp(vec3 from, + vec3 to, + vec3 fwd, + vec3 up, + versor dest); + CGLM_INLINE void glm_quat_rotatev(versor q, vec3 v, vec3 dest); + CGLM_INLINE void glm_quat_rotate(mat4 m, versor q, mat4 dest); */ #ifndef cglm_quat_h @@ -27,25 +53,32 @@ #include "common.h" #include "vec4.h" +#include "mat4.h" +#include "mat3.h" #ifdef CGLM_SSE_FP # include "simd/sse2/quat.h" #endif +CGLM_INLINE +void +glm_mat4_mulv(mat4 m, vec4 v, vec4 dest); + +CGLM_INLINE +void +glm_mat4_mul(mat4 m1, mat4 m2, mat4 dest); + /* - * IMPORTANT! cglm stores quat as [w, x, y, z] + * IMPORTANT: + * ---------------------------------------------------------------------------- + * cglm stores quat as [x, y, z, w] since v0.3.6 * - * Possible changes (these may be changed in the future): - * - versor is identity quat, we can define new type for quat. - * it can't be quat or quaternion becuase someone can use that name for - * variable name. maybe just vec4. - * - it stores [w, x, y, z] but it may change to [x, y, z, w] if we get enough - * feedback to change it. - * - in general we use last param as dest, but this header used first param - * as dest this may be changed but decided yet + * it was [w, x, y, z] before v0.3.6 it has been changed to [x, y, z, w] + * with v0.3.6 version. + * ---------------------------------------------------------------------------- */ -#define GLM_QUAT_IDENTITY_INIT {1.0f, 0.0f, 0.0f, 0.0f} +#define GLM_QUAT_IDENTITY_INIT {0.0f, 0.0f, 0.0f, 1.0f} #define GLM_QUAT_IDENTITY ((versor)GLM_QUAT_IDENTITY_INIT) /*! @@ -61,55 +94,74 @@ glm_quat_identity(versor q) { } /*! - * @brief creates NEW quaternion with individual axis components + * @brief inits quaterion with raw values + * + * @param[out] q quaternion + * @param[in] x x + * @param[in] y y + * @param[in] z z + * @param[in] w w (real part) + */ +CGLM_INLINE +void +glm_quat_init(versor q, float x, float y, float z, float w) { + q[0] = x; + q[1] = y; + q[2] = z; + q[3] = w; +} + +/*! + * @brief creates NEW quaternion with axis vector * * @param[out] q quaternion * @param[in] angle angle (radians) - * @param[in] x axis.x - * @param[in] y axis.y - * @param[in] z axis.z + * @param[in] axis axis */ CGLM_INLINE void -glm_quat(versor q, - float angle, - float x, - float y, - float z) { +glm_quatv(versor q, float angle, vec3 axis) { + vec3 k; float a, c, s; a = angle * 0.5f; c = cosf(a); s = sinf(a); - q[0] = c; - q[1] = s * x; - q[2] = s * y; - q[3] = s * z; + glm_normalize_to(axis, k); + + q[0] = s * k[0]; + q[1] = s * k[1]; + q[2] = s * k[2]; + q[3] = c; } /*! - * @brief creates NEW quaternion with axis vector + * @brief creates NEW quaternion with individual axis components * * @param[out] q quaternion * @param[in] angle angle (radians) - * @param[in] v axis + * @param[in] x axis.x + * @param[in] y axis.y + * @param[in] z axis.z */ CGLM_INLINE void -glm_quatv(versor q, - float angle, - vec3 v) { - float a, c, s; - - a = angle * 0.5f; - c = cosf(a); - s = sinf(a); +glm_quat(versor q, float angle, float x, float y, float z) { + vec3 axis = {x, y, z}; + glm_quatv(q, angle, axis); +} - q[0] = c; - q[1] = s * v[0]; - q[2] = s * v[1]; - q[3] = s * v[2]; +/*! + * @brief copy quaternion to another one + * + * @param[in] q quaternion + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_quat_copy(versor q, versor dest) { + glm_vec4_copy(q, dest); } /*! @@ -123,6 +175,43 @@ glm_quat_norm(versor q) { return glm_vec4_norm(q); } +/*! + * @brief normalize quaternion and store result in dest + * + * @param[in] q quaternion to normalze + * @param[out] dest destination quaternion + */ +CGLM_INLINE +void +glm_quat_normalize_to(versor q, versor dest) { +#if defined( __SSE2__ ) || defined( __SSE2__ ) + __m128 xdot, x0; + float dot; + + x0 = _mm_load_ps(q); + xdot = glm_simd_dot(x0, x0); + dot = _mm_cvtss_f32(xdot); + + if (dot <= 0.0f) { + glm_quat_identity(dest); + return; + } + + _mm_store_ps(dest, _mm_div_ps(x0, _mm_sqrt_ps(xdot))); +#else + float dot; + + dot = glm_vec4_norm2(q); + + if (dot <= 0.0f) { + glm_quat_identity(q); + return; + } + + glm_vec4_scale(q, 1.0f / sqrtf(dot), dest); +#endif +} + /*! * @brief normalize quaternion * @@ -131,45 +220,178 @@ glm_quat_norm(versor q) { CGLM_INLINE void glm_quat_normalize(versor q) { - float sum; + glm_quat_normalize_to(q, q); +} - sum = q[0] * q[0] + q[1] * q[1] - + q[2] * q[2] + q[3] * q[3]; +/*! + * @brief dot product of two quaternion + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + */ +CGLM_INLINE +float +glm_quat_dot(versor p, versor q) { + return glm_vec4_dot(p, q); +} - if (fabs(1.0f - sum) < 0.0001f) - return; +/*! + * @brief conjugate of quaternion + * + * @param[in] q quaternion + * @param[out] dest conjugate + */ +CGLM_INLINE +void +glm_quat_conjugate(versor q, versor dest) { + glm_vec4_flipsign_to(q, dest); + dest[3] = -dest[3]; +} - glm_vec4_scale(q, 1.0f / sqrtf(sum), q); +/*! + * @brief inverse of non-zero quaternion + * + * @param[in] q quaternion + * @param[out] dest inverse quaternion + */ +CGLM_INLINE +void +glm_quat_inv(versor q, versor dest) { + versor conj; + glm_quat_conjugate(q, conj); + glm_vec_scale(conj, glm_vec4_norm2(q), dest); } /*! - * @brief dot product of two quaternion + * @brief add (componentwise) two quaternions and store result in dest + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + * @param[out] dest result quaternion + */ +CGLM_INLINE +void +glm_quat_add(versor p, versor q, versor dest) { + glm_vec4_add(p, q, dest); +} + +/*! + * @brief subtract (componentwise) two quaternions and store result in dest * - * @param[in] q quaternion 1 - * @param[in] r quaternion 2 + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + * @param[out] dest result quaternion + */ +CGLM_INLINE +void +glm_quat_sub(versor p, versor q, versor dest) { + glm_vec4_sub(p, q, dest); +} + +/*! + * @brief returns real part of quaternion + * + * @param[in] q quaternion */ CGLM_INLINE float -glm_quat_dot(versor q, versor r) { - return glm_vec4_dot(q, r); +glm_quat_real(versor q) { + return q[3]; +} + +/*! + * @brief returns imaginary part of quaternion + * + * @param[in] q quaternion + * @param[out] dest imag + */ +CGLM_INLINE +void +glm_quat_imag(versor q, vec3 dest) { + dest[0] = q[0]; + dest[1] = q[1]; + dest[2] = q[2]; +} + +/*! + * @brief returns normalized imaginary part of quaternion + * + * @param[in] q quaternion + */ +CGLM_INLINE +void +glm_quat_imagn(versor q, vec3 dest) { + glm_normalize_to(q, dest); +} + +/*! + * @brief returns length of imaginary part of quaternion + * + * @param[in] q quaternion + */ +CGLM_INLINE +float +glm_quat_imaglen(versor q) { + return glm_vec_norm(q); +} + +/*! + * @brief returns angle of quaternion + * + * @param[in] q quaternion + */ +CGLM_INLINE +float +glm_quat_angle(versor q) { + /* + sin(theta / 2) = length(x*x + y*y + z*z) + cos(theta / 2) = w + theta = 2 * atan(sin(theta / 2) / cos(theta / 2)) + */ + return 2.0f * atan2f(glm_quat_imaglen(q), glm_quat_real(q)); +} + +/*! + * @brief axis of quaternion + * + * @param[in] q quaternion + * @param[out] dest axis of quaternion + */ +CGLM_INLINE +void +glm_quat_axis(versor q, versor dest) { + glm_quat_imagn(q, dest); } /*! * @brief multiplies two quaternion and stores result in dest + * this is also called Hamilton Product * - * @param[in] q1 quaternion 1 - * @param[in] q2 quaternion 2 + * According to WikiPedia: + * The product of two rotation quaternions [clarification needed] will be + * equivalent to the rotation q followed by the rotation p + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 * @param[out] dest result quaternion */ CGLM_INLINE void -glm_quat_mulv(versor q1, versor q2, versor dest) { - dest[0] = q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2] - q2[3] * q1[3]; - dest[1] = q2[0] * q1[1] + q2[1] * q1[0] - q2[2] * q1[3] + q2[3] * q1[2]; - dest[2] = q2[0] * q1[2] + q2[1] * q1[3] + q2[2] * q1[0] - q2[3] * q1[1]; - dest[3] = q2[0] * q1[3] - q2[1] * q1[2] + q2[2] * q1[1] + q2[3] * q1[0]; - - glm_quat_normalize(dest); +glm_quat_mul(versor p, versor q, versor dest) { + /* + + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i + + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j + + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k + a1 a2 − b1 b2 − c1 c2 − d1 d2 + */ +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_quat_mul_sse2(p, q, dest); +#else + dest[0] = p[3] * q[0] + p[0] * q[3] + p[1] * q[2] - p[2] * q[1]; + dest[1] = p[3] * q[1] - p[0] * q[2] + p[1] * q[3] + p[2] * q[0]; + dest[2] = p[3] * q[2] + p[0] * q[1] - p[1] * q[0] + p[2] * q[3]; + dest[3] = p[3] * q[3] - p[0] * q[0] - p[1] * q[1] - p[2] * q[2]; +#endif } /*! @@ -181,19 +403,22 @@ glm_quat_mulv(versor q1, versor q2, versor dest) { CGLM_INLINE void glm_quat_mat4(versor q, mat4 dest) { - float w, x, y, z; - float xx, yy, zz; - float xy, yz, xz; - float wx, wy, wz; + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; - w = q[0]; - x = q[1]; - y = q[2]; - z = q[3]; + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; - xx = 2.0f * x * x; xy = 2.0f * x * y; wx = 2.0f * w * x; - yy = 2.0f * y * y; yz = 2.0f * y * z; wy = 2.0f * w * y; - zz = 2.0f * z * z; xz = 2.0f * x * z; wz = 2.0f * w * z; + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; dest[0][0] = 1.0f - yy - zz; dest[1][1] = 1.0f - xx - zz; @@ -207,8 +432,55 @@ glm_quat_mat4(versor q, mat4 dest) { dest[2][1] = yz - wx; dest[0][2] = xz - wy; + dest[0][3] = 0.0f; dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief convert quaternion to mat4 (transposed) + * + * @param[in] q quaternion + * @param[out] dest result matrix as transposed + */ +CGLM_INLINE +void +glm_quat_mat4t(versor q, mat4 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[1][0] = xy + wz; + dest[2][1] = yz + wx; + dest[0][2] = xz + wy; + + dest[0][1] = xy - wz; + dest[1][2] = yz - wx; + dest[2][0] = xz - wy; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; dest[2][3] = 0.0f; dest[3][0] = 0.0f; dest[3][1] = 0.0f; @@ -216,69 +488,256 @@ glm_quat_mat4(versor q, mat4 dest) { dest[3][3] = 1.0f; } +/*! + * @brief convert quaternion to mat3 + * + * @param[in] q quaternion + * @param[out] dest result matrix + */ +CGLM_INLINE +void +glm_quat_mat3(versor q, mat3 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[0][1] = xy + wz; + dest[1][2] = yz + wx; + dest[2][0] = xz + wy; + + dest[1][0] = xy - wz; + dest[2][1] = yz - wx; + dest[0][2] = xz - wy; +} + +/*! + * @brief convert quaternion to mat3 (transposed) + * + * @param[in] q quaternion + * @param[out] dest result matrix + */ +CGLM_INLINE +void +glm_quat_mat3t(versor q, mat3 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[1][0] = xy + wz; + dest[2][1] = yz + wx; + dest[0][2] = xz + wy; + + dest[0][1] = xy - wz; + dest[1][2] = yz - wx; + dest[2][0] = xz - wy; +} + +/*! + * @brief interpolates between two quaternions + * using linear interpolation (LERP) + * + * @param[in] from from + * @param[in] to to + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest result quaternion + */ +CGLM_INLINE +void +glm_quat_lerp(versor from, versor to, float t, versor dest) { + glm_vec4_lerp(from, to, t, dest); +} + /*! * @brief interpolates between two quaternions * using spherical linear interpolation (SLERP) * - * @param[in] q from - * @param[in] r to + * @param[in] from from + * @param[in] to to * @param[in] t amout * @param[out] dest result quaternion */ CGLM_INLINE void -glm_quat_slerp(versor q, - versor r, - float t, - versor dest) { - /* https://en.wikipedia.org/wiki/Slerp */ -#if defined( __SSE__ ) || defined( __SSE2__ ) - glm_quat_slerp_sse2(q, r, t, dest); -#else - float cosTheta, sinTheta, angle, a, b, c; +glm_quat_slerp(versor from, versor to, float t, versor dest) { + vec4 q1, q2; + float cosTheta, sinTheta, angle; - cosTheta = glm_quat_dot(q, r); - if (cosTheta < 0.0f) { - q[0] *= -1.0f; - q[1] *= -1.0f; - q[2] *= -1.0f; - q[3] *= -1.0f; + cosTheta = glm_quat_dot(from, to); + glm_quat_copy(from, q1); + + if (fabsf(cosTheta) >= 1.0f) { + glm_quat_copy(q1, dest); + return; + } + if (cosTheta < 0.0f) { + glm_vec4_flipsign(q1); cosTheta = -cosTheta; } - if (fabs(cosTheta) >= 1.0f) { - dest[0] = q[0]; - dest[1] = q[1]; - dest[2] = q[2]; - dest[3] = q[3]; + sinTheta = sqrtf(1.0f - cosTheta * cosTheta); + + /* LERP to avoid zero division */ + if (fabsf(sinTheta) < 0.001f) { + glm_quat_lerp(from, to, t, dest); return; } - sinTheta = sqrt(1.0f - cosTheta * cosTheta); + /* SLERP */ + angle = acosf(cosTheta); + glm_vec4_scale(q1, sinf((1.0f - t) * angle), q1); + glm_vec4_scale(to, sinf(t * angle), q2); + + glm_vec4_add(q1, q2, q1); + glm_vec4_scale(q1, 1.0f / sinTheta, dest); +} + +/*! + * @brief creates view matrix using quaternion as camera orientation + * + * @param[in] eye eye + * @param[in] ori orientation in world space as quaternion + * @param[out] dest view matrix + */ +CGLM_INLINE +void +glm_quat_look(vec3 eye, versor ori, mat4 dest) { + vec4 t; + + /* orientation */ + glm_quat_mat4t(ori, dest); + + /* translate */ + glm_vec4(eye, 1.0f, t); + glm_mat4_mulv(dest, t, t); + glm_vec_flipsign_to(t, dest[3]); +} + +/*! + * @brief creates look rotation quaternion + * + * @param[in] dir direction to look + * @param[in] fwd forward vector + * @param[in] up up vector + * @param[out] dest destination quaternion + */ +CGLM_INLINE +void +glm_quat_for(vec3 dir, vec3 fwd, vec3 up, versor dest) { + vec3 axis; + float dot, angle; - c = 1.0f - t; + dot = glm_vec_dot(dir, fwd); + if (fabsf(dot + 1.0f) < 0.000001f) { + glm_quat_init(dest, up[0], up[1], up[2], CGLM_PI); + return; + } - /* LERP */ - /* TODO: FLT_EPSILON vs 0.001? */ - if (sinTheta < 0.001f) { - dest[0] = c * q[0] + t * r[0]; - dest[1] = c * q[1] + t * r[1]; - dest[2] = c * q[2] + t * r[2]; - dest[3] = c * q[3] + t * r[3]; + if (fabsf(dot - 1.0f) < 0.000001f) { + glm_quat_identity(dest); return; } - /* SLERP */ - angle = acosf(cosTheta); - a = sinf(c * angle); - b = sinf(t * angle); + angle = acosf(dot); + glm_cross(fwd, dir, axis); + glm_normalize(axis); - dest[0] = (q[0] * a + r[0] * b) / sinTheta; - dest[1] = (q[1] * a + r[1] * b) / sinTheta; - dest[2] = (q[2] * a + r[2] * b) / sinTheta; - dest[3] = (q[3] * a + r[3] * b) / sinTheta; -#endif + glm_quatv(dest, angle, axis); +} + +/*! + * @brief creates look rotation quaternion using source and + * destination positions p suffix stands for position + * + * @param[in] from source point + * @param[in] to destination point + * @param[in] fwd forward vector + * @param[in] up up vector + * @param[out] dest destination quaternion + */ +CGLM_INLINE +void +glm_quat_forp(vec3 from, vec3 to, vec3 fwd, vec3 up, versor dest) { + vec3 dir; + glm_vec_sub(to, from, dir); + glm_quat_for(dir, fwd, up, dest); +} + +/*! + * @brief rotate vector using using quaternion + * + * @param[in] q quaternion + * @param[in] v vector to rotate + * @param[out] dest rotated vector + */ +CGLM_INLINE +void +glm_quat_rotatev(versor q, vec3 v, vec3 dest) { + versor p; + vec3 u, v1, v2; + float s; + + glm_quat_normalize_to(q, p); + glm_quat_imag(p, u); + s = glm_quat_real(p); + + glm_vec_scale(u, 2.0f * glm_vec_dot(u, v), v1); + glm_vec_scale(v, s * s - glm_vec_dot(u, u), v2); + glm_vec_add(v1, v2, v1); + + glm_vec_cross(u, v, v2); + glm_vec_scale(v2, 2.0f * s, v2); + + glm_vec_add(v1, v2, dest); +} + +/*! + * @brief rotate existing transform matrix using quaternion + * + * @param[in] m existing transform matrix + * @param[in] q quaternion + * @param[out] dest rotated matrix/transform + */ +CGLM_INLINE +void +glm_quat_rotate(mat4 m, versor q, mat4 dest) { + mat4 rot; + glm_quat_mat4(q, rot); + glm_mat4_mul(m, rot, dest); } #endif /* cglm_quat_h */ diff --git a/include/cglm/simd/intrin.h b/include/cglm/simd/intrin.h index c0f2e537..4c27d902 100644 --- a/include/cglm/simd/intrin.h +++ b/include/cglm/simd/intrin.h @@ -30,6 +30,16 @@ # define _mm_shuffle2_ps(a, b, z0, y0, x0, w0, z1, y1, x1, w1) \ _mm_shuffle1_ps(_mm_shuffle_ps(a, b, _MM_SHUFFLE(z0, y0, x0, w0)), \ z1, y1, x1, w1) + +CGLM_INLINE +__m128 +glm_simd_dot(__m128 a, __m128 b) { + __m128 x0; + x0 = _mm_mul_ps(a, b); + x0 = _mm_add_ps(x0, _mm_shuffle1_ps(x0, 1, 0, 3, 2)); + return _mm_add_ps(x0, _mm_shuffle1_ps(x0, 0, 1, 0, 1)); +} + #endif /* x86, x64 */ diff --git a/include/cglm/simd/sse2/quat.h b/include/cglm/simd/sse2/quat.h index b3420a70..5dbf7599 100644 --- a/include/cglm/simd/sse2/quat.h +++ b/include/cglm/simd/sse2/quat.h @@ -14,56 +14,33 @@ CGLM_INLINE void -glm_quat_slerp_sse2(versor q, - versor r, - float t, - versor dest) { - /* https://en.wikipedia.org/wiki/Slerp */ - float cosTheta, sinTheta, angle, a, b, c; +glm_quat_mul_sse2(versor p, versor q, versor dest) { + /* + + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i + + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j + + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k + a1 a2 − b1 b2 − c1 c2 − d1 d2 + */ - __m128 xmm_q; + __m128 xp, xq, x0, r; - xmm_q = _mm_load_ps(q); + xp = _mm_load_ps(p); /* 3 2 1 0 */ + xq = _mm_load_ps(q); - cosTheta = glm_vec4_dot(q, r); - if (cosTheta < 0.0f) { - _mm_store_ps(q, - _mm_xor_ps(xmm_q, - _mm_set1_ps(-0.f))) ; + r = _mm_mul_ps(_mm_shuffle1_ps1(xp, 3), xq); - cosTheta = -cosTheta; - } + x0 = _mm_xor_ps(_mm_shuffle1_ps1(xp, 0), _mm_set_ps(-0.f, 0.f, -0.f, 0.f)); + r = _mm_add_ps(r, _mm_mul_ps(x0, _mm_shuffle1_ps(xq, 0, 1, 2, 3))); - if (cosTheta >= 1.0f) { - _mm_store_ps(dest, xmm_q); - return; - } + x0 = _mm_xor_ps(_mm_shuffle1_ps1(xp, 1), _mm_set_ps(-0.f, -0.f, 0.f, 0.f)); + r = _mm_add_ps(r, _mm_mul_ps(x0, _mm_shuffle1_ps(xq, 1, 0, 3, 2))); - sinTheta = sqrtf(1.0f - cosTheta * cosTheta); + x0 = _mm_xor_ps(_mm_shuffle1_ps1(xp, 2), _mm_set_ps(-0.f, 0.f, 0.f, -0.f)); + r = _mm_add_ps(r, _mm_mul_ps(x0, _mm_shuffle1_ps(xq, 2, 3, 0, 1))); - c = 1.0f - t; - - /* LERP */ - if (sinTheta < 0.001f) { - _mm_store_ps(dest, _mm_add_ps(_mm_mul_ps(_mm_set1_ps(c), - xmm_q), - _mm_mul_ps(_mm_set1_ps(t), - _mm_load_ps(r)))); - return; - } - - /* SLERP */ - angle = acosf(cosTheta); - a = sinf(c * angle); - b = sinf(t * angle); - - _mm_store_ps(dest, - _mm_div_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(a), - xmm_q), - _mm_mul_ps(_mm_set1_ps(b), - _mm_load_ps(r))), - _mm_set1_ps(sinTheta))); + _mm_store_ps(dest, r); } + #endif #endif /* cglm_quat_simd_h */ diff --git a/include/cglm/util.h b/include/cglm/util.h index 85fc789f..b272d44f 100644 --- a/include/cglm/util.h +++ b/include/cglm/util.h @@ -143,4 +143,19 @@ glm_clamp(float val, float minVal, float maxVal) { return glm_min(glm_max(val, minVal), maxVal); } +/*! + * @brief linear interpolation between two number + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + */ +CGLM_INLINE +float +glm_lerp(float from, float to, float t) { + return from + glm_clamp(t, 0.0f, 1.0f) * (to - from); +} + #endif /* cglm_util_h */ diff --git a/include/cglm/vec3-ext.h b/include/cglm/vec3-ext.h index 8a9226fc..fb2a6878 100644 --- a/include/cglm/vec3-ext.h +++ b/include/cglm/vec3-ext.h @@ -26,6 +26,7 @@ #define cglm_vec3_ext_h #include "common.h" +#include "util.h" #include #include #include @@ -196,4 +197,33 @@ glm_vec_isvalid(vec3 v) { return !glm_vec_isnan(v) && !glm_vec_isinf(v); } +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param v vector + */ +CGLM_INLINE +void +glm_vec_sign(vec3 v, vec3 dest) { + dest[0] = glm_signf(v[0]); + dest[1] = glm_signf(v[1]); + dest[2] = glm_signf(v[2]); +} + +/*! + * @brief square root of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec_sqrt(vec3 v, vec3 dest) { + dest[0] = sqrtf(v[0]); + dest[1] = sqrtf(v[1]); + dest[2] = sqrtf(v[2]); +} + #endif /* cglm_vec3_ext_h */ diff --git a/include/cglm/vec3.h b/include/cglm/vec3.h index e2108f30..714165b9 100644 --- a/include/cglm/vec3.h +++ b/include/cglm/vec3.h @@ -147,7 +147,7 @@ glm_vec_cross(vec3 a, vec3 b, vec3 d) { CGLM_INLINE float glm_vec_norm2(vec3 v) { - return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + return glm_vec_dot(v, v); } /*! @@ -242,6 +242,20 @@ glm_vec_flipsign(vec3 v) { v[2] = -v[2]; } +/*! + * @brief flip sign of all vec3 members and store result in dest + * + * @param[in] v vector + * @param[out] dest result vector + */ +CGLM_INLINE +void +glm_vec_flipsign_to(vec3 v, vec3 dest) { + dest[0] = -v[0]; + dest[1] = -v[1]; + dest[2] = -v[2]; +} + /*! * @brief make vector as inverse/opposite of itself * @@ -325,12 +339,6 @@ glm_vec_angle(vec3 v1, vec3 v2) { return acosf(glm_vec_dot(v1, v2) * norm); } -CGLM_INLINE -void -glm_quatv(versor q, - float angle, - vec3 v); - /*! * @brief rotate vec3 around axis by angle using Rodrigues' rotation formula * @@ -341,31 +349,26 @@ glm_quatv(versor q, CGLM_INLINE void glm_vec_rotate(vec3 v, float angle, vec3 axis) { - versor q; - vec3 v1, v2, v3; + vec3 v1, v2, k; float c, s; c = cosf(angle); s = sinf(angle); + glm_vec_normalize_to(axis, k); + /* Right Hand, Rodrigues' rotation formula: v = v*cos(t) + (kxv)sin(t) + k*(k.v)(1 - cos(t)) */ - - /* quaternion */ - glm_quatv(q, angle, v); - glm_vec_scale(v, c, v1); - glm_vec_cross(axis, v, v2); + glm_vec_cross(k, v, v2); glm_vec_scale(v2, s, v2); - glm_vec_scale(axis, - glm_vec_dot(axis, v) * (1.0f - c), - v3); - glm_vec_add(v1, v2, v1); - glm_vec_add(v1, v3, v); + + glm_vec_scale(k, glm_vec_dot(k, v) * (1.0f - c), v2); + glm_vec_add(v1, v2, v); } /*! @@ -494,6 +497,28 @@ glm_vec_clamp(vec3 v, float minVal, float maxVal) { v[2] = glm_clamp(v[2], minVal, maxVal); } +/*! + * @brief linear interpolation between two vector + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec_lerp(vec3 from, vec3 to, float t, vec3 dest) { + vec3 s, v; + + /* from + s * (to - from) */ + glm_vec_broadcast(glm_clamp(t, 0.0f, 1.0f), s); + glm_vec_sub(to, from, v); + glm_vec_mulv(s, v, v); + glm_vec_add(from, v, dest); +} + /*! * @brief vec3 cross product * diff --git a/include/cglm/vec4-ext.h b/include/cglm/vec4-ext.h index 28595885..53b02a83 100644 --- a/include/cglm/vec4-ext.h +++ b/include/cglm/vec4-ext.h @@ -175,7 +175,7 @@ glm_vec4_min(vec4 v) { } /*! - * @brief check if all items are NaN (not a number) + * @brief check if one of items is NaN (not a number) * you should only use this in DEBUG mode or very critical asserts * * @param[in] v vector @@ -187,7 +187,7 @@ glm_vec4_isnan(vec4 v) { } /*! - * @brief check if all items are INFINITY + * @brief check if one of items is INFINITY * you should only use this in DEBUG mode or very critical asserts * * @param[in] v vector @@ -210,5 +210,52 @@ glm_vec4_isvalid(vec4 v) { return !glm_vec4_isnan(v) && !glm_vec4_isinf(v); } -#endif /* cglm_vec4_ext_h */ +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param v vector + */ +CGLM_INLINE +void +glm_vec4_sign(vec4 v, vec4 dest) { +#if defined( __SSE2__ ) || defined( __SSE2__ ) + __m128 x0, x1, x2, x3, x4; + + x0 = _mm_load_ps(v); + x1 = _mm_set_ps(0.0f, 0.0f, 1.0f, -1.0f); + x2 = _mm_shuffle1_ps1(x1, 2); + + x3 = _mm_and_ps(_mm_cmpgt_ps(x0, x2), _mm_shuffle1_ps1(x1, 1)); + x4 = _mm_and_ps(_mm_cmplt_ps(x0, x2), _mm_shuffle1_ps1(x1, 0)); + _mm_store_ps(dest, _mm_or_ps(x3, x4)); +#else + dest[0] = glm_signf(v[0]); + dest[1] = glm_signf(v[1]); + dest[2] = glm_signf(v[2]); + dest[3] = glm_signf(v[3]); +#endif +} + +/*! + * @brief square root of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec4_sqrt(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + _mm_store_ps(dest, _mm_sqrt_ps(_mm_load_ps(v))); +#else + dest[0] = sqrtf(v[0]); + dest[1] = sqrtf(v[1]); + dest[2] = sqrtf(v[2]); + dest[3] = sqrtf(v[3]); +#endif +} + +#endif /* cglm_vec4_ext_h */ diff --git a/include/cglm/vec4.h b/include/cglm/vec4.h index 95bab09f..eaa4eafc 100644 --- a/include/cglm/vec4.h +++ b/include/cglm/vec4.h @@ -122,7 +122,14 @@ glm_vec4_copy(vec4 v, vec4 dest) { CGLM_INLINE float glm_vec4_dot(vec4 a, vec4 b) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + __m128 x0; + x0 = _mm_mul_ps(_mm_load_ps(a), _mm_load_ps(b)); + x0 = _mm_add_ps(x0, _mm_shuffle1_ps(x0, 1, 0, 3, 2)); + return _mm_cvtss_f32(_mm_add_ss(x0, _mm_shuffle1_ps(x0, 0, 1, 0, 1))); +#else return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +#endif } /*! @@ -139,7 +146,7 @@ glm_vec4_dot(vec4 a, vec4 b) { CGLM_INLINE float glm_vec4_norm2(vec4 v) { - return v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]; + return glm_vec4_dot(v, v); } /*! @@ -261,6 +268,26 @@ glm_vec4_flipsign(vec4 v) { #endif } +/*! + * @brief flip sign of all vec4 members and store result in dest + * + * @param[in] v vector + * @param[out] dest vector + */ +CGLM_INLINE +void +glm_vec4_flipsign_to(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + _mm_store_ps(dest, _mm_xor_ps(_mm_load_ps(v), + _mm_set1_ps(-0.0f))); +#else + dest[0] = -v[0]; + dest[1] = -v[1]; + dest[2] = -v[2]; + dest[3] = -v[3]; +#endif +} + /*! * @brief make vector as inverse/opposite of itself * @@ -390,4 +417,26 @@ glm_vec4_clamp(vec4 v, float minVal, float maxVal) { v[3] = glm_clamp(v[3], minVal, maxVal); } +/*! + * @brief linear interpolation between two vector + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec4_lerp(vec4 from, vec4 to, float t, vec4 dest) { + vec4 s, v; + + /* from + s * (to - from) */ + glm_vec4_broadcast(glm_clamp(t, 0.0f, 1.0f), s); + glm_vec4_sub(to, from, v); + glm_vec4_mulv(s, v, v); + glm_vec4_add(from, v, dest); +} + #endif /* cglm_vec4_h */ diff --git a/include/cglm/version.h b/include/cglm/version.h index c27a26ee..9a54b03d 100644 --- a/include/cglm/version.h +++ b/include/cglm/version.h @@ -9,7 +9,7 @@ #define cglm_version_h #define CGLM_VERSION_MAJOR 0 -#define CGLM_VERSION_MINOR 3 -#define CGLM_VERSION_PATCH 6 +#define CGLM_VERSION_MINOR 4 +#define CGLM_VERSION_PATCH 0 #endif /* cglm_version_h */ diff --git a/makefile.am b/makefile.am index 217fff3b..2922373f 100644 --- a/makefile.am +++ b/makefile.am @@ -108,7 +108,9 @@ test_tests_SOURCES=\ test/src/test_cam.c \ test/src/test_project.c \ test/src/test_clamp.c \ - test/src/test_euler.c + test/src/test_euler.c \ + test/src/test_quat.c \ + test/src/test_vec4.c all-local: sh ./post-build.sh diff --git a/src/mat4.c b/src/mat4.c index 838b52d2..94076842 100644 --- a/src/mat4.c +++ b/src/mat4.c @@ -52,7 +52,7 @@ glmc_mat4_mul(mat4 m1, mat4 m2, mat4 dest) { CGLM_EXPORT void -glmc_mat4_mulN(mat4 * __restrict matrices[], int len, mat4 dest) { +glmc_mat4_mulN(mat4 * __restrict matrices[], uint32_t len, mat4 dest) { glm_mat4_mulN(matrices, len, dest); } @@ -62,6 +62,12 @@ glmc_mat4_mulv(mat4 m, vec4 v, vec4 dest) { glm_mat4_mulv(m, v, dest); } +CGLM_EXPORT +void +glmc_mat4_quat(mat4 m, versor dest) { + glm_mat4_quat(m, dest); +} + CGLM_EXPORT void glmc_mat4_transpose_to(mat4 m, mat4 dest) { diff --git a/src/quat.c b/src/quat.c index b26d1124..8a9a463b 100644 --- a/src/quat.c +++ b/src/quat.c @@ -8,6 +8,7 @@ #include "../include/cglm/cglm.h" #include "../include/cglm/call.h" + CGLM_EXPORT void glmc_quat_identity(versor q) { @@ -16,20 +17,26 @@ glmc_quat_identity(versor q) { CGLM_EXPORT void -glmc_quat(versor q, - float angle, - float x, - float y, - float z) { +glmc_quat_init(versor q, float x, float y, float z, float w) { + glm_quat_init(q, x, y, z, w); +} + +CGLM_EXPORT +void +glmc_quat(versor q, float angle, float x, float y, float z) { glm_quat(q, angle, x, y, z); } CGLM_EXPORT void -glmc_quatv(versor q, - float angle, - vec3 v) { - glm_quatv(q, angle, v); +glmc_quatv(versor q, float angle, vec3 axis) { + glm_quatv(q, angle, axis); +} + +CGLM_EXPORT +void +glmc_quat_copy(versor q, versor dest) { + glm_quat_copy(q, dest); } CGLM_EXPORT @@ -38,22 +45,88 @@ glmc_quat_norm(versor q) { return glm_quat_norm(q); } +CGLM_EXPORT +void +glmc_quat_normalize_to(versor q, versor dest) { + glm_quat_normalize_to(q, dest); +} + CGLM_EXPORT void glmc_quat_normalize(versor q) { - glm_quat_normalize(q); + glm_quat_norm(q); } CGLM_EXPORT float -glmc_quat_dot(versor q, versor r) { - return glm_quat_dot(q, r); +glmc_quat_dot(versor p, versor q) { + return glm_quat_dot(p, q); +} + +CGLM_EXPORT +void +glmc_quat_conjugate(versor q, versor dest) { + glm_quat_conjugate(q, dest); } CGLM_EXPORT void -glmc_quat_mulv(versor q1, versor q2, versor dest) { - glm_quat_mulv(q1, q2, dest); +glmc_quat_inv(versor q, versor dest) { + glm_quat_inv(q, dest); +} + +CGLM_EXPORT +void +glmc_quat_add(versor p, versor q, versor dest) { + glm_quat_add(p, q, dest); +} + +CGLM_EXPORT +void +glmc_quat_sub(versor p, versor q, versor dest) { + glm_quat_sub(p, q, dest); +} + +CGLM_EXPORT +float +glmc_quat_real(versor q) { + return glm_quat_real(q); +} + +CGLM_EXPORT +void +glmc_quat_imag(versor q, vec3 dest) { + glm_quat_imag(q, dest); +} + +CGLM_EXPORT +void +glmc_quat_imagn(versor q, vec3 dest) { + glm_quat_imagn(q, dest); +} + +CGLM_EXPORT +float +glmc_quat_imaglen(versor q) { + return glm_quat_imaglen(q); +} + +CGLM_EXPORT +float +glmc_quat_angle(versor q) { + return glm_quat_angle(q); +} + +CGLM_EXPORT +void +glmc_quat_axis(versor q, versor dest) { + glm_quat_axis(q, dest); +} + +CGLM_EXPORT +void +glmc_quat_mul(versor p, versor q, versor dest) { + glm_quat_mul(p, q, dest); } CGLM_EXPORT @@ -64,9 +137,60 @@ glmc_quat_mat4(versor q, mat4 dest) { CGLM_EXPORT void -glmc_quat_slerp(versor q, - versor r, - float t, - versor dest) { - glm_quat_slerp(q, r, t, dest); +glmc_quat_mat4t(versor q, mat4 dest) { + glm_quat_mat4t(q, dest); +} + +CGLM_EXPORT +void +glmc_quat_mat3(versor q, mat3 dest) { + glm_quat_mat3(q, dest); +} + +CGLM_EXPORT +void +glmc_quat_mat3t(versor q, mat3 dest) { + glm_quat_mat3t(q, dest); +} + +CGLM_EXPORT +void +glmc_quat_lerp(versor from, versor to, float t, versor dest) { + glm_quat_lerp(from, to, t, dest); +} + +CGLM_EXPORT +void +glmc_quat_slerp(versor from, versor to, float t, versor dest) { + glm_quat_slerp(from, to, t, dest); +} + +CGLM_EXPORT +void +glmc_quat_look(vec3 eye, versor ori, mat4 dest) { + glm_quat_look(eye, ori, dest); +} + +CGLM_EXPORT +void +glmc_quat_for(vec3 dir, vec3 fwd, vec3 up, versor dest) { + glm_quat_for(dir, fwd, up, dest); +} + +CGLM_EXPORT +void +glmc_quat_forp(vec3 from, vec3 to, vec3 fwd, vec3 up, versor dest) { + glm_quat_forp(from, to, fwd, up, dest); +} + +CGLM_EXPORT +void +glmc_quat_rotatev(versor q, vec3 v, vec3 dest) { + glm_quat_rotatev(q, v, dest); +} + +CGLM_EXPORT +void +glmc_quat_rotate(mat4 m, versor q, mat4 dest) { + glm_quat_rotate(m, q, dest); } diff --git a/src/vec3.c b/src/vec3.c index ebc677d5..f7e26928 100644 --- a/src/vec3.c +++ b/src/vec3.c @@ -8,6 +8,12 @@ #include "../include/cglm/cglm.h" #include "../include/cglm/call.h" +CGLM_EXPORT +void +glmc_vec3(vec4 v4, vec3 dest) { + glm_vec3(v4, dest); +} + CGLM_EXPORT void glmc_vec_copy(vec3 a, vec3 dest) { @@ -80,6 +86,12 @@ glmc_vec_flipsign(vec3 v) { glm_vec_flipsign(v); } +CGLM_EXPORT +void +glmc_vec_flipsign_to(vec3 v, vec3 dest) { + glm_vec_flipsign_to(v, dest); +} + CGLM_EXPORT void glmc_vec_inv(vec3 v) { @@ -145,3 +157,101 @@ void glmc_vec_clamp(vec3 v, float minVal, float maxVal) { glm_vec_clamp(v, minVal, maxVal); } + +CGLM_EXPORT +void +glmc_vec_ortho(vec3 v, vec3 dest) { + glm_vec_ortho(v, dest); +} + +CGLM_EXPORT +void +glmc_vec_lerp(vec3 from, vec3 to, float t, vec3 dest) { + glm_vec_lerp(from, to, t, dest); +} + +/* ext */ + +CGLM_EXPORT +void +glmc_vec_mulv(vec3 a, vec3 b, vec3 d) { + glm_vec_mulv(a, b, d); +} + +CGLM_EXPORT +void +glmc_vec_broadcast(float val, vec3 d) { + glm_vec_broadcast(val, d); +} + +CGLM_EXPORT +bool +glmc_vec_eq(vec3 v, float val) { + return glm_vec_eq(v, val); +} + +CGLM_EXPORT +bool +glmc_vec_eq_eps(vec3 v, float val) { + return glm_vec_eq_eps(v, val); +} + +CGLM_EXPORT +bool +glmc_vec_eq_all(vec3 v) { + return glm_vec_eq_all(v); +} + +CGLM_EXPORT +bool +glmc_vec_eqv(vec3 v1, vec3 v2) { + return glm_vec_eqv(v1, v2); +} + +CGLM_EXPORT +bool +glmc_vec_eqv_eps(vec3 v1, vec3 v2) { + return glm_vec_eqv_eps(v1, v2); +} + +CGLM_EXPORT +float +glmc_vec_max(vec3 v) { + return glm_vec_max(v); +} + +CGLM_EXPORT +float +glmc_vec_min(vec3 v) { + return glm_vec_min(v); +} + +CGLM_EXPORT +bool +glmc_vec_isnan(vec3 v) { + return glm_vec_isnan(v); +} + +CGLM_EXPORT +bool +glmc_vec_isinf(vec3 v) { + return glm_vec_isinf(v); +} + +CGLM_EXPORT +bool +glmc_vec_isvalid(vec3 v) { + return glm_vec_isvalid(v); +} + +CGLM_EXPORT +void +glmc_vec_sign(vec3 v, vec3 dest) { + glm_vec_sign(v, dest); +} + +CGLM_EXPORT +void +glmc_vec_sqrt(vec3 v, vec3 dest) { + glm_vec_sqrt(v, dest); +} diff --git a/src/vec4.c b/src/vec4.c index f5f6a066..72280708 100644 --- a/src/vec4.c +++ b/src/vec4.c @@ -8,6 +8,12 @@ #include "../include/cglm/cglm.h" #include "../include/cglm/call.h" +CGLM_EXPORT +void +glmc_vec4(vec3 v3, float last, vec4 dest) { + glm_vec4(v3, last, dest); +} + CGLM_EXPORT void glmc_vec4_copy3(vec4 a, vec3 dest) { @@ -80,6 +86,12 @@ glmc_vec4_flipsign(vec4 v) { glm_vec4_flipsign(v); } +CGLM_EXPORT +void +glmc_vec4_flipsign_to(vec4 v, vec4 dest) { + glm_vec4_flipsign_to(v, dest); +} + CGLM_EXPORT void glmc_vec4_inv(vec4 v) { @@ -115,3 +127,95 @@ void glmc_vec4_clamp(vec4 v, float minVal, float maxVal) { glm_vec4_clamp(v, minVal, maxVal); } + +CGLM_EXPORT +void +glmc_vec4_lerp(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_lerp(from, to, t, dest); +} + +/* ext */ + +CGLM_EXPORT +void +glmc_vec4_mulv(vec4 a, vec4 b, vec4 d) { + glm_vec4_mulv(a, b, d); +} + +CGLM_EXPORT +void +glmc_vec4_broadcast(float val, vec4 d) { + glm_vec4_broadcast(val, d); +} + +CGLM_EXPORT +bool +glmc_vec4_eq(vec4 v, float val) { + return glm_vec4_eq(v, val); +} + +CGLM_EXPORT +bool +glmc_vec4_eq_eps(vec4 v, float val) { + return glm_vec4_eq_eps(v, val); +} + +CGLM_EXPORT +bool +glmc_vec4_eq_all(vec4 v) { + return glm_vec4_eq_all(v); +} + +CGLM_EXPORT +bool +glmc_vec4_eqv(vec4 v1, vec4 v2) { + return glm_vec4_eqv(v1, v2); +} + +CGLM_EXPORT +bool +glmc_vec4_eqv_eps(vec4 v1, vec4 v2) { + return glm_vec4_eqv_eps(v1, v2); +} + +CGLM_EXPORT +float +glmc_vec4_max(vec4 v) { + return glm_vec4_max(v); +} + +CGLM_EXPORT +float +glmc_vec4_min(vec4 v) { + return glm_vec4_min(v); +} + +CGLM_EXPORT +bool +glmc_vec4_isnan(vec4 v) { + return glm_vec4_isnan(v); +} + +CGLM_EXPORT +bool +glmc_vec4_isinf(vec4 v) { + return glm_vec4_isinf(v); +} + +CGLM_EXPORT +bool +glmc_vec4_isvalid(vec4 v) { + return glm_vec4_isvalid(v); +} + +CGLM_EXPORT +void +glmc_vec4_sign(vec4 v, vec4 dest) { + glm_vec4_sign(v, dest); +} + +CGLM_EXPORT +void +glmc_vec4_sqrt(vec4 v, vec4 dest) { + glm_vec4_sqrt(v, dest); +} diff --git a/test/src/test_common.c b/test/src/test_common.c index 23f2b502..514c0063 100644 --- a/test/src/test_common.c +++ b/test/src/test_common.c @@ -27,6 +27,39 @@ test_rand_mat4(mat4 dest) { /* glm_scale(dest, (vec3){drand48(), drand48(), drand48()}); */ } +void +test_rand_vec3(vec3 dest) { + srand((unsigned int)time(NULL)); + + dest[0] = drand48(); + dest[1] = drand48(); + dest[2] = drand48(); +} + +void +test_rand_vec4(vec4 dest) { + srand((unsigned int)time(NULL)); + + dest[0] = drand48(); + dest[1] = drand48(); + dest[2] = drand48(); + dest[3] = drand48(); +} + +float +test_rand_angle(void) { + srand((unsigned int)time(NULL)); + return drand48(); +} + +void +test_rand_quat(versor q) { + srand((unsigned int)time(NULL)); + + glm_quat(q, drand48(), drand48(), drand48(), drand48()); + glm_quat_normalize(q); +} + void test_assert_mat4_eq(mat4 m1, mat4 m2) { int i, j, k; @@ -53,7 +86,24 @@ test_assert_mat4_eq2(mat4 m1, mat4 m2, float eps) { void test_assert_vec3_eq(vec3 v1, vec3 v2) { - assert_true(fabsf(v1[0] - v2[0]) <= 0.0000009); - assert_true(fabsf(v1[1] - v2[1]) <= 0.0000009); - assert_true(fabsf(v1[2] - v2[2]) <= 0.0000009); + assert_true(fabsf(v1[0] - v2[0]) <= 0.000009); /* rounding errors */ + assert_true(fabsf(v1[1] - v2[1]) <= 0.000009); + assert_true(fabsf(v1[2] - v2[2]) <= 0.000009); +} + +void +test_assert_quat_eq_abs(versor v1, versor v2) { + assert_true(fabsf(fabsf(v1[0]) - fabsf(v2[0])) <= 0.0009); /* rounding errors */ + assert_true(fabsf(fabsf(v1[1]) - fabsf(v2[1])) <= 0.0009); + assert_true(fabsf(fabsf(v1[2]) - fabsf(v2[2])) <= 0.0009); + assert_true(fabsf(fabsf(v1[3]) - fabsf(v2[3])) <= 0.0009); +} + +void +test_assert_quat_eq(versor v1, versor v2) { + assert_true(fabsf(v1[0] - v2[0]) <= 0.000009); /* rounding errors */ + assert_true(fabsf(v1[1] - v2[1]) <= 0.000009); + assert_true(fabsf(v1[2] - v2[2]) <= 0.000009); + assert_true(fabsf(v1[3] - v2[3]) <= 0.000009); } + diff --git a/test/src/test_common.h b/test/src/test_common.h index aeea4d6a..c95405e2 100644 --- a/test/src/test_common.h +++ b/test/src/test_common.h @@ -34,4 +34,22 @@ test_assert_mat4_eq2(mat4 m1, mat4 m2, float eps); void test_assert_vec3_eq(vec3 v1, vec3 v2); +void +test_assert_quat_eq(versor v1, versor v2); + +void +test_assert_quat_eq_abs(versor v1, versor v2); + +void +test_rand_vec3(vec3 dest); + +void +test_rand_vec4(vec4 dest) ; + +float +test_rand_angle(void); + +void +test_rand_quat(versor q); + #endif /* test_common_h */ diff --git a/test/src/test_main.c b/test/src/test_main.c index 5c1a647c..7995a5a3 100644 --- a/test/src/test_main.c +++ b/test/src/test_main.c @@ -23,7 +23,13 @@ main(int argc, const char * argv[]) { cmocka_unit_test(test_clamp), /* euler */ - cmocka_unit_test(test_euler) + cmocka_unit_test(test_euler), + + /* quaternion */ + cmocka_unit_test(test_quat), + + /* vec4 */ + cmocka_unit_test(test_vec4) }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/test/src/test_quat.c b/test/src/test_quat.c new file mode 100644 index 00000000..fec8daed --- /dev/null +++ b/test/src/test_quat.c @@ -0,0 +1,188 @@ +/* + * 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" + +CGLM_INLINE +void +test_quat_mul_raw(versor p, versor q, versor dest) { + dest[0] = p[3] * q[0] + p[0] * q[3] + p[1] * q[2] - p[2] * q[1]; + dest[1] = p[3] * q[1] - p[0] * q[2] + p[1] * q[3] + p[2] * q[0]; + dest[2] = p[3] * q[2] + p[0] * q[1] - p[1] * q[0] + p[2] * q[3]; + dest[3] = p[3] * q[3] - p[0] * q[0] - p[1] * q[1] - p[2] * q[2]; +} + +void +test_quat(void **state) { + mat4 inRot, outRot, view1, view2, rot1, rot2; + versor inQuat, outQuat, q3, q4, q5; + vec3 eye, axis, imag, v1, v2; + int i; + + /* 0. test identiy quat */ + glm_quat_identity(q4); + assert_true(glm_quat_real(q4) == cosf(glm_rad(0.0f) * 0.5f)); + + /* 1. test quat to mat and mat to quat */ + for (i = 0; i < 1000; i++) { + test_rand_quat(inQuat); + + glmc_quat_mat4(inQuat, inRot); + glmc_mat4_quat(inRot, outQuat); + glmc_quat_mat4(outQuat, outRot); + + /* 2. test first quat and generated one equality */ + test_assert_quat_eq_abs(inQuat, outQuat); + + /* 3. test first rot and second rotation */ + test_assert_mat4_eq2(inRot, outRot, 0.000009); /* almost equal */ + + /* 4. test SSE mul and raw mul */ + test_quat_mul_raw(inQuat, outQuat, q3); + glm_quat_mul_sse2(inQuat, outQuat, q4); + test_assert_quat_eq(q3, q4); + } + + /* 5. test lookat */ + test_rand_vec3(eye); + glm_quatv(q3, glm_rad(-90.0f), GLM_YUP); + + /* now X axis must be forward axis, Z must be right axis */ + glm_look(eye, GLM_XUP, GLM_YUP, view1); + + /* create view matrix with quaternion */ + glm_quat_look(eye, q3, view2); + + test_assert_mat4_eq2(view1, view2, 0.000009); + + /* 6. test quaternion rotation matrix result */ + test_rand_quat(q3); + glm_quat_mat4(q3, rot1); + + /* 6.1 test axis and angle of quat */ + glm_quat_axis(q3, axis); + glm_rotate_make(rot2, glm_quat_angle(q3), axis); + + test_assert_mat4_eq2(rot1, rot2, 0.000009); + + /* 7. test quaternion multiplication (hamilton product), + final rotation = first rotation + second = quat1 * quat2 + */ + test_rand_quat(q3); + test_rand_quat(q4); + + glm_quat_mul(q3, q4, q5); + + glm_quat_axis(q3, axis); + glm_rotate_make(rot1, glm_quat_angle(q3), axis); + + glm_quat_axis(q4, axis); + glm_rotate(rot1, glm_quat_angle(q4), axis); + + /* rot2 is combine of two rotation now test with quaternion result */ + glm_quat_mat4(q5, rot2); + + /* result must be same (almost) */ + test_assert_mat4_eq2(rot1, rot2, 0.000009); + + /* 8. test quaternion for look rotation */ + + /* 8.1 same direction */ + /* look at from 0, 0, 1 to zero, direction = 0, 0, -1 */ + glm_quat_for((vec3){0, 0, -1}, (vec3){0, 0, -1}, GLM_YUP, q3); + + /* result must be identity */ + glm_quat_identity(q4); + test_assert_quat_eq(q3, q4); + + /* look at from 0, 0, 1 to zero, direction = 0, 0, -1 */ + glm_quat_forp(GLM_ZUP, GLM_VEC3_ZERO, (vec3){0, 0, -1}, GLM_YUP, q3); + + /* result must be identity */ + glm_quat_identity(q4); + test_assert_quat_eq(q3, q4); + + /* 8.2 perpendicular */ + glm_quat_for(GLM_XUP, (vec3){0, 0, -1}, GLM_YUP, q3); + + /* result must be -90 */ + glm_quatv(q4, glm_rad(-90.0f), GLM_YUP); + test_assert_quat_eq(q3, q4); + + /* 9. test imag, real */ + + /* 9.1 real */ + assert_true(glm_quat_real(q4) == cosf(glm_rad(-90.0f) * 0.5f)); + + /* 9.1 imag */ + glm_quat_imag(q4, imag); + + /* axis = Y_UP * sinf(angle * 0.5), YUP = 0, 1, 0 */ + axis[0] = 0.0f; + axis[1] = sinf(glm_rad(-90.0f) * 0.5f) * 1.0f; + axis[2] = 0.0f; + + assert_true(glm_vec_eqv_eps(imag, axis)); + + /* 9.2 axis */ + glm_quat_axis(q4, axis); + imag[0] = 0.0f; + imag[1] = -1.0f; + imag[2] = 0.0f; + + test_assert_vec3_eq(imag, axis); + + /* 10. test rotate vector using quat */ + /* (0,0,-1) around (1,0,0) must give (0,1,0) */ + v1[0] = 0.0f; v1[1] = 0.0f; v1[2] = -1.0f; + v2[0] = 0.0f; v2[1] = 0.0f; v2[2] = -1.0f; + + glm_vec_rotate(v1, glm_rad(90.0f), (vec3){1.0f, 0.0f, 0.0f}); + glm_quatv(q3, glm_rad(90.0f), (vec3){1.0f, 0.0f, 0.0f}); + + glm_vec4_scale(q3, 1.5, q3); + glm_quat_rotatev(q3, v2, v2); + + /* result must be : (0,1,0) */ + assert_true(fabsf(v1[0]) <= 0.00009f + && fabsf(v1[1] - 1.0f) <= 0.00009f + && fabsf(v1[2]) <= 0.00009f); + + test_assert_vec3_eq(v1, v2); + + /* 11. test rotate transform */ + glm_translate_make(rot1, (vec3){-10.0, 45.0f, 8.0f}); + glm_rotate(rot1, glm_rad(-90), GLM_ZUP); + + glm_quatv(q3, glm_rad(-90.0f), GLM_ZUP); + glm_translate_make(rot2, (vec3){-10.0, 45.0f, 8.0f}); + glm_quat_rotate(rot2, q3, rot2); + + /* result must be same (almost) */ + test_assert_mat4_eq2(rot1, rot2, 0.000009); + + glm_rotate_make(rot1, glm_rad(-90), GLM_ZUP); + glm_translate(rot1, (vec3){-10.0, 45.0f, 8.0f}); + + glm_quatv(q3, glm_rad(-90.0f), GLM_ZUP); + glm_mat4_identity(rot2); + glm_quat_rotate(rot2, q3, rot2); + glm_translate(rot2, (vec3){-10.0, 45.0f, 8.0f}); + + /* result must be same (almost) */ + test_assert_mat4_eq2(rot1, rot2, 0.000009); + + /* reverse */ + glm_rotate_make(rot1, glm_rad(-90), GLM_ZUP); + glm_quatv(q3, glm_rad(90.0f), GLM_ZUP); + glm_quat_rotate(rot1, q3, rot1); + + /* result must be identity */ + test_assert_mat4_eq2(rot1, GLM_MAT4_IDENTITY, 0.000009); + + /* TODO: add tests for slerp, lerp */ +} diff --git a/test/src/test_tests.h b/test/src/test_tests.h index 7234782e..1dfbb5f6 100644 --- a/test/src/test_tests.h +++ b/test/src/test_tests.h @@ -25,4 +25,10 @@ test_clamp(void **state); void test_euler(void **state); +void +test_quat(void **state); + +void +test_vec4(void **state); + #endif /* test_tests_h */ diff --git a/test/src/test_vec4.c b/test/src/test_vec4.c new file mode 100644 index 00000000..a45a700c --- /dev/null +++ b/test/src/test_vec4.c @@ -0,0 +1,30 @@ +/* + * 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" + +CGLM_INLINE +float +test_vec4_dot(vec4 a, vec4 b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +} + +void +test_vec4(void **state) { + vec4 v; + int i; + float d1, d2; + + /* test SSE/SIMD dot product */ + for (i = 0; i < 100; i++) { + test_rand_vec4(v); + d1 = glm_vec4_dot(v, v); + d2 = test_vec4_dot(v, v); + + assert_true(fabsf(d1 - d2) <= 0.000009); + } +}