diff --git a/README.md b/README.md index 84b321e..4e7c4ef 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # 🎥 OpenGL Mathematics (glm) for `C` [![Build Status](https://travis-ci.org/recp/cglm.svg?branch=master)](https://travis-ci.org/recp/cglm) -[![Build status](https://ci.appveyor.com/api/projects/status/av7l3gc0yhfex8y4/branch/master?svg=true)](https://ci.appveyor.com/project/recp/cglm/branch/master) + [![Build status](https://ci.appveyor.com/api/projects/status/av7l3gc0yhfex8y4/branch/master?svg=true)](https://ci.appveyor.com/project/recp/cglm/branch/master) [![Documentation Status](https://readthedocs.org/projects/cglm/badge/?version=latest)](http://cglm.readthedocs.io/en/latest/?badge=latest) [![Coverage Status](https://coveralls.io/repos/github/recp/cglm/badge.svg?branch=master)](https://coveralls.io/github/recp/cglm?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/6a62b37d5f214f178ebef269dc4a6bf1)](https://www.codacy.com/app/recp/cglm?utm_source=github.com&utm_medium=referral&utm_content=recp/cglm&utm_campaign=Badge_Grade) +[![Backers on Open Collective](https://opencollective.com/cglm/backers/badge.svg)](#backers) +[![Sponsors on Open Collective](https://opencollective.com/cglm/sponsors/badge.svg)](#sponsors) The original glm library is for C++ only (templates, namespaces, classes...), this library targeted to C99 but currently you can use it for C89 safely by language extensions e.g `__restrict` @@ -17,6 +19,7 @@ Complete documentation: http://cglm.readthedocs.io - _dup (duplicate) is changed to _copy. For instance `glm_vec_dup -> glm_vec_copy` - OpenGL related functions are dropped to make this lib platform/third-party independent - make sure you have latest version and feel free to report bugs, troubles +- **[bugfix]** euler angles was implemented in reverse order (extrinsic) it was fixed, now they are intrinsic. Make sure that you have the latest version #### Note for C++ developers: If you don't aware about original GLM library yet, you may also want to look at: @@ -114,6 +117,36 @@ glm_mul(T, R, modelMat); glm_inv_tr(modelMat); ``` +## Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + + + +## Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/cglm#backer)] + + + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/cglm#sponsor)] + + + + + + + + + + + + + + ## License MIT. check the LICENSE file @@ -161,7 +194,7 @@ If you want to use inline versions of funcstions then; include main header ```C #include ``` -the haeder will include all headers. Then call func you want e.g. rotate vector by axis: +the header will include all headers. Then call func you want e.g. rotate vector by axis: ```C glm_vec_rotate(v1, glm_rad(45), (vec3){1.0f, 0.0f, 0.0f}); ``` @@ -180,7 +213,7 @@ to call pre-compiled versions include header with `c` postfix, c means call. Pre ```C #include ``` -this header will include all heaers with c postfix. You need to call functions with c posfix: +this header will include all headers with c postfix. You need to call functions with c posfix: ```C glmc_vec_normalize(vec); ``` diff --git a/build-deps.sh b/build-deps.sh index 23bb9e9..74b1cfa 100644 --- a/build-deps.sh +++ b/build-deps.sh @@ -16,14 +16,18 @@ cd $(dirname "$0") if [ "$(uname)" = "Darwin" ]; then libtoolBin=$(which glibtoolize) libtoolBinDir=$(dirname "${libtoolBin}") - ln -s $libtoolBin "${libtoolBinDir}/libtoolize" + + if [ ! -f "${libtoolBinDir}/libtoolize" ]; then + ln -s $libtoolBin "${libtoolBinDir}/libtoolize" + fi fi # general deps: gcc make autoconf automake libtool cmake # test - cmocka cd ./test/lib/cmocka -mkdir build +rm -rf build +mkdir -p build cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug .. make -j8 diff --git a/configure.ac b/configure.ac index 23e8a15..afb694b 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ #***************************************************************************** AC_PREREQ([2.69]) -AC_INIT([cglm], [0.3.5], [info@recp.me]) +AC_INIT([cglm], [0.3.6], [info@recp.me]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/docs/source/box.rst b/docs/source/box.rst index 84ba145..6aa4d62 100644 --- a/docs/source/box.rst +++ b/docs/source/box.rst @@ -24,6 +24,10 @@ Functions: #. :c:func:`glm_aabb_crop` #. :c:func:`glm_aabb_crop_until` #. :c:func:`glm_aabb_frustum` +#. :c:func:`glm_aabb_invalidate` +#. :c:func:`glm_aabb_isvalid` +#. :c:func:`glm_aabb_size` +#. :c:func:`glm_aabb_radius` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -91,3 +95,39 @@ Functions documentation Parameters: | *[in]* **box** bounding box | *[out]* **planes** frustum planes + +.. c:function:: void glm_aabb_invalidate(vec3 box[2]) + + | invalidate AABB min and max values + + | It fills *max* values with -FLT_MAX and *min* values with +FLT_MAX + + Parameters: + | *[in, out]* **box** bounding box + +.. c:function:: bool glm_aabb_isvalid(vec3 box[2]) + + | check if AABB is valid or not + + Parameters: + | *[in]* **box** bounding box + + Returns: + returns true if aabb is valid otherwise false + +.. c:function:: float glm_aabb_size(vec3 box[2]) + + | distance between of min and max + + Parameters: + | *[in]* **box** bounding box + + Returns: + distance between min - max + +.. c:function:: float glm_aabb_radius(vec3 box[2]) + + | radius of sphere which surrounds AABB + + Parameters: + | *[in]* **box** bounding box diff --git a/docs/source/io.rst b/docs/source/io.rst index 9cf8900..34fe696 100644 --- a/docs/source/io.rst +++ b/docs/source/io.rst @@ -39,6 +39,7 @@ Functions: #. :c:func:`glm_vec3_print` #. :c:func:`glm_ivec3_print` #. :c:func:`glm_versor_print` +#. :c:func:`glm_aabb_print` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -90,3 +91,12 @@ Functions documentation Parameters: | *[in]* **vec** quaternion | *[in]* **ostream** FILE to write + +.. c:function:: void glm_aabb_print(versor vec, const char * __restrict tag, FILE * __restrict ostream) + + | print aabb to given stream + + Parameters: + | *[in]* **vec** aabb (axis-aligned bounding box) + | *[in]* **tag** tag to find it more easly in logs + | *[in]* **ostream** FILE to write diff --git a/docs/source/util.rst b/docs/source/util.rst index 048be70..a9f4066 100644 --- a/docs/source/util.rst +++ b/docs/source/util.rst @@ -13,18 +13,24 @@ Table of contents (click to go): Functions: 1. :c:func:`glm_sign` +#. :c:func:`glm_signf` #. :c:func:`glm_rad` #. :c:func:`glm_deg` #. :c:func:`glm_make_rad` #. :c:func:`glm_make_deg` #. :c:func:`glm_pow2` +#. :c:func:`glm_min` +#. :c:func:`glm_max` +#. :c:func:`glm_clamp` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ .. c:function:: int glm_sign(int val) - | returns sign of 32 bit integer as +1 or -1 + | returns sign of 32 bit integer as +1, -1, 0 + + | **Important**: It returns 0 for zero input Parameters: | *[in]* **val** an integer @@ -32,6 +38,18 @@ Functions documentation Returns: sign of given number +.. c:function:: float glm_signf(float val) + + | returns sign of 32 bit integer as +1.0, -1.0, 0.0 + + | **Important**: It returns 0.0f for zero input + + Parameters: + | *[in]* **val** a float + + Returns: + sign of given number + .. c:function:: float glm_rad(float deg) | convert degree to radians @@ -91,3 +109,15 @@ Functions documentation Returns: maximum value + +.. c:function:: void glm_clamp(float val, float minVal, float maxVal) + + constrain a value to lie between two further values + + Parameters: + | *[in]* **val** input value + | *[in]* **minVal** minimum value + | *[in]* **maxVal** maximum value + + Returns: + clamped value diff --git a/docs/source/vec3.rst b/docs/source/vec3.rst index 2ae95f0..355178d 100644 --- a/docs/source/vec3.rst +++ b/docs/source/vec3.rst @@ -53,6 +53,7 @@ Functions: #. :c:func:`glm_vec_maxv` #. :c:func:`glm_vec_minv` #. :c:func:`glm_vec_ortho` +#. :c:func:`glm_vec_clamp` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -271,3 +272,12 @@ Functions documentation Parameters: | *[in]* **mat** vector | *[out]* **dest** orthogonal/perpendicular vector + +.. c:function:: void glm_vec_clamp(vec3 v, float minVal, float maxVal) + + constrain a value to lie between two further values + + Parameters: + | *[in, out]* **v** vector + | *[in]* **minVal** minimum value + | *[in]* **maxVal** maximum value diff --git a/docs/source/vec4.rst b/docs/source/vec4.rst index 60be7fa..ac1b9c5 100644 --- a/docs/source/vec4.rst +++ b/docs/source/vec4.rst @@ -39,6 +39,7 @@ Functions: #. :c:func:`glm_vec4_distance` #. :c:func:`glm_vec4_maxv` #. :c:func:`glm_vec4_minv` +#. :c:func:`glm_vec4_clamp` Functions documentation ~~~~~~~~~~~~~~~~~~~~~~~ @@ -203,3 +204,12 @@ Functions documentation | *[in]* **v1** vector1 | *[in]* **v2** vector2 | *[out]* **dest** destination + +.. c:function:: void glm_vec4_clamp(vec4 v, float minVal, float maxVal) + + constrain a value to lie between two further values + + Parameters: + | *[in, out]* **v** vector + | *[in]* **minVal** minimum value + | *[in]* **maxVal** maximum value diff --git a/include/cglm/box.h b/include/cglm/box.h index d7184c5..7032339 100644 --- a/include/cglm/box.h +++ b/include/cglm/box.h @@ -153,4 +153,50 @@ glm_aabb_frustum(vec3 box[2], vec4 planes[6]) { return true; } +/*! + * @brief invalidate AABB min and max values + * + * @param[in, out] box bounding box + */ +CGLM_INLINE +void +glm_aabb_invalidate(vec3 box[2]) { + glm_vec_broadcast(FLT_MAX, box[0]); + glm_vec_broadcast(-FLT_MAX, box[1]); +} + +/*! + * @brief check if AABB is valid or not + * + * @param[in] box bounding box + */ +CGLM_INLINE +bool +glm_aabb_isvalid(vec3 box[2]) { + return glm_vec_max(box[0]) != FLT_MAX + && glm_vec_min(box[1]) != -FLT_MAX; +} + +/*! + * @brief distance between of min and max + * + * @param[in] box bounding box + */ +CGLM_INLINE +float +glm_aabb_size(vec3 box[2]) { + return glm_vec_distance(box[0], box[1]); +} + +/*! + * @brief radius of sphere which surrounds AABB + * + * @param[in] box bounding box + */ +CGLM_INLINE +float +glm_aabb_radius(vec3 box[2]) { + return glm_aabb_size(box) * 0.5f; +} + #endif /* cglm_box_h */ diff --git a/include/cglm/call/euler.h b/include/cglm/call/euler.h index 8cbea2b..9b85485 100644 --- a/include/cglm/call/euler.h +++ b/include/cglm/call/euler.h @@ -21,6 +21,10 @@ CGLM_EXPORT void glmc_euler(vec3 angles, mat4 dest); +CGLM_EXPORT +void +glmc_euler_xyz(vec3 angles, mat4 dest); + CGLM_EXPORT void glmc_euler_zyx(vec3 angles, mat4 dest); diff --git a/include/cglm/call/vec3.h b/include/cglm/call/vec3.h index a637d7e..461de0b 100644 --- a/include/cglm/call/vec3.h +++ b/include/cglm/call/vec3.h @@ -104,6 +104,10 @@ CGLM_EXPORT void glmc_vec_minv(vec3 v1, vec3 v2, vec3 dest); +CGLM_EXPORT +void +glmc_vec_clamp(vec3 v, float minVal, float maxVal); + #ifdef __cplusplus } #endif diff --git a/include/cglm/call/vec4.h b/include/cglm/call/vec4.h index 084fce2..b63af80 100644 --- a/include/cglm/call/vec4.h +++ b/include/cglm/call/vec4.h @@ -85,6 +85,10 @@ CGLM_EXPORT void glmc_vec4_minv(vec4 v1, vec4 v2, vec4 dest); +CGLM_EXPORT +void +glmc_vec4_clamp(vec4 v, float minVal, float maxVal); + #ifdef __cplusplus } #endif diff --git a/include/cglm/euler.h b/include/cglm/euler.h index 2289b04..5db7e66 100644 --- a/include/cglm/euler.h +++ b/include/cglm/euler.h @@ -5,6 +5,14 @@ * Full license can be found in the LICENSE file */ +/* + NOTE: + angles must be passed as [X-Angle, Y-Angle, Z-angle] order + For instance you don't pass angles as [Z-Angle, X-Angle, Y-angle] to + glm_euler_zxy funciton, All RELATED functions accept angles same order + which is [X, Y, Z]. + */ + /* Types: enum glm_euler_sq @@ -61,253 +69,283 @@ glm_euler_order(int ord[3]) { CGLM_INLINE void glm_euler_angles(mat4 m, vec3 dest) { - if (m[0][2] < 1.0f) { - if (m[0][2] > -1.0f) { - vec3 a[2]; - float cy1, cy2; - int path; + float m00, m01, m10, m11, m20, m21, m22; + float thetaX, thetaY, thetaZ; - a[0][1] = asinf(-m[0][2]); - a[1][1] = CGLM_PI - a[0][1]; + m00 = m[0][0]; m10 = m[1][0]; m20 = m[2][0]; + m01 = m[0][1]; m11 = m[1][1]; m21 = m[2][1]; + m22 = m[2][2]; - cy1 = cosf(a[0][1]); - cy2 = cosf(a[1][1]); - - a[0][0] = atan2f(m[1][2] / cy1, m[2][2] / cy1); - a[1][0] = atan2f(m[1][2] / cy2, m[2][2] / cy2); - - a[0][2] = atan2f(m[0][1] / cy1, m[0][0] / cy1); - a[1][2] = atan2f(m[0][1] / cy2, m[0][0] / cy2); - - path = (fabsf(a[0][0]) + fabsf(a[0][1]) + fabsf(a[0][2])) >= - (fabsf(a[1][0]) + fabsf(a[1][1]) + fabsf(a[1][2])); - - glm_vec_copy(a[path], dest); - } else { - dest[0] = atan2f(m[1][0], m[2][0]); - dest[1] = CGLM_PI_2; - dest[2] = 0.0f; + if (m20 < 1.0f) { + if (m20 > -1.0f) { + thetaY = asinf(m20); + thetaX = atan2f(-m21, m22); + thetaZ = atan2f(-m10, m00); + } else { /* m20 == -1 */ + /* Not a unique solution */ + thetaY = -CGLM_PI_2; + thetaX = -atan2f(m01, m11); + thetaZ = 0.0f; } - } else { - dest[0] = atan2f(-m[1][0], -m[2][0]); - dest[1] =-CGLM_PI_2; - dest[2] = 0.0f; + } else { /* m20 == +1 */ + thetaY = CGLM_PI_2; + thetaX = atan2f(m01, m11); + thetaZ = 0.0f; } + + dest[0] = thetaX; + dest[1] = thetaY; + dest[2] = thetaZ; } /*! * @brief build rotation matrix from euler angles * - * @param[in] angles angles as vector [Ex, Ey, Ez] + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +CGLM_INLINE +void +glm_euler_xyz(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, czsx, cxcz, sysz; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + czsx = cz * sx; + cxcz = cx * cz; + sysz = sy * sz; + + dest[0][0] = cy * cz; + dest[0][1] = czsx * sy + cx * sz; + dest[0][2] = -cxcz * sy + sx * sz; + dest[1][0] = -cy * sz; + dest[1][1] = cxcz - sx * sysz; + dest[1][2] = czsx + cx * sysz; + dest[2][0] = sy; + dest[2][1] = -cy * sx; + dest[2][2] = cx * cy; + 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 build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[out] dest rotation matrix */ CGLM_INLINE void glm_euler(vec3 angles, mat4 dest) { - float cx, cy, cz, - sx, sy, sz; - - sx = sinf(angles[0]); cx = cosf(angles[0]); - sy = sinf(angles[1]); cy = cosf(angles[1]); - sz = sinf(angles[2]); cz = cosf(angles[2]); - - dest[0][0] = cy * cz; - dest[0][1] = cy * sz; - dest[0][2] =-sy; - dest[1][0] = cz * sx * sy - cx * sz; - dest[1][1] = cx * cz + sx * sy * sz; - dest[1][2] = cy * sx; - dest[2][0] = cx * cz * sy + sx * sz; - dest[2][1] =-cz * sx + cx * sy * sz; - dest[2][2] = cx * cy; - 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; + glm_euler_xyz(angles, dest); } /*! * @brief build rotation matrix from euler angles * - * @param[in] angles angles as vector [Ez, Ey, Ex] + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[out] dest rotation matrix */ CGLM_INLINE void -glm_euler_zyx(vec3 angles, - mat4 dest) { +glm_euler_xzy(vec3 angles, mat4 dest) { float cx, cy, cz, - sx, sy, sz; + sx, sy, sz, sxsy, cysx, cxsy, cxcy; - sx = sinf(angles[0]); cx = cosf(angles[0]); - sy = sinf(angles[1]); cy = cosf(angles[1]); - sz = sinf(angles[2]); cz = cosf(angles[2]); + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); - dest[0][0] = cy * cz; - dest[0][1] = cz * sx * sy + cx * sz; - dest[0][2] =-cx * cz * sy + sx * sz; - dest[1][0] =-cy * sz; - dest[1][1] = cx * cz - sx * sy * sz; - dest[1][2] = cz * sx + cx * sy * sz; - dest[2][0] = sy; - dest[2][1] =-cy * sx; - dest[2][2] = cx * cy; - 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; + sxsy = sx * sy; + cysx = cy * sx; + cxsy = cx * sy; + cxcy = cx * cy; + + dest[0][0] = cy * cz; + dest[0][1] = sxsy + cxcy * sz; + dest[0][2] = -cxsy + cysx * sz; + dest[1][0] = -sz; + dest[1][1] = cx * cz; + dest[1][2] = cz * sx; + dest[2][0] = cz * sy; + dest[2][1] = -cysx + cxsy * sz; + dest[2][2] = cxcy + sxsy * sz; + 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 build rotation matrix from euler angles * - * @param[in] angles angles as vector [Ez, Ex, Ey] + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[out] dest rotation matrix */ CGLM_INLINE void -glm_euler_zxy(vec3 angles, - mat4 dest) { +glm_euler_yxz(vec3 angles, mat4 dest) { float cx, cy, cz, - sx, sy, sz; + sx, sy, sz, cycz, sysz, czsy, cysz; - sx = sinf(angles[0]); cx = cosf(angles[0]); - sy = sinf(angles[1]); cy = cosf(angles[1]); - sz = sinf(angles[2]); cz = cosf(angles[2]); + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); - dest[0][0] = cy * cz + sx * sy * sz; - dest[0][1] = cx * sz; - dest[0][2] =-cz * sy + cy * sx * sz; - dest[1][0] = cz * sx * sy - cy * sz; - dest[1][1] = cx * cz; - dest[1][2] = cy * cz * sx + sy * sz; - dest[2][0] = cx * sy; - dest[2][1] =-sx; - dest[2][2] = cx * cy; - 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; + cycz = cy * cz; + sysz = sy * sz; + czsy = cz * sy; + cysz = cy * sz; + + dest[0][0] = cycz + sx * sysz; + dest[0][1] = cx * sz; + dest[0][2] = -czsy + cysz * sx; + dest[1][0] = -cysz + czsy * sx; + dest[1][1] = cx * cz; + dest[1][2] = cycz * sx + sysz; + dest[2][0] = cx * sy; + dest[2][1] = -sx; + dest[2][2] = cx * cy; + 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 build rotation matrix from euler angles * - * @param[in] angles angles as vector [Ex, Ez, Ey] + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[out] dest rotation matrix */ CGLM_INLINE void -glm_euler_xzy(vec3 angles, - mat4 dest) { +glm_euler_yzx(vec3 angles, mat4 dest) { float cx, cy, cz, - sx, sy, sz; + sx, sy, sz, sxsy, cxcy, cysx, cxsy; - sx = sinf(angles[0]); cx = cosf(angles[0]); - sy = sinf(angles[1]); cy = cosf(angles[1]); - sz = sinf(angles[2]); cz = cosf(angles[2]); + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); - dest[0][0] = cy * cz; - dest[0][1] = sz; - dest[0][2] =-cz * sy; - dest[1][0] = sx * sy - cx * cy * sz; - dest[1][1] = cx * cz; - dest[1][2] = cy * sx + cx * sy * sz; - dest[2][0] = cx * sy + cy * sx * sz; - dest[2][1] =-cz * sx; - dest[2][2] = cx * cy - sx * sy * sz; - 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; + sxsy = sx * sy; + cxcy = cx * cy; + cysx = cy * sx; + cxsy = cx * sy; + + dest[0][0] = cy * cz; + dest[0][1] = sz; + dest[0][2] = -cz * sy; + dest[1][0] = sxsy - cxcy * sz; + dest[1][1] = cx * cz; + dest[1][2] = cysx + cxsy * sz; + dest[2][0] = cxsy + cysx * sz; + dest[2][1] = -cz * sx; + dest[2][2] = cxcy - sxsy * sz; + 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 build rotation matrix from euler angles * - * @param[in] angles angles as vector [Ey, Ez, Ex] + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[out] dest rotation matrix */ CGLM_INLINE void -glm_euler_yzx(vec3 angles, - mat4 dest) { +glm_euler_zxy(vec3 angles, mat4 dest) { float cx, cy, cz, - sx, sy, sz; + sx, sy, sz, cycz, sxsy, cysz; - sx = sinf(angles[0]); cx = cosf(angles[0]); - sy = sinf(angles[1]); cy = cosf(angles[1]); - sz = sinf(angles[2]); cz = cosf(angles[2]); + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); - dest[0][0] = cy * cz; - dest[0][1] = sx * sy + cx * cy * sz; - dest[0][2] =-cx * sy + cy * sx * sz; - dest[1][0] =-sz; - dest[1][1] = cx * cz; - dest[1][2] = cz * sx; - dest[2][0] = cz * sy; - dest[2][1] =-cy * sx + cx * sy * sz; - dest[2][2] = cx * cy + sx * sy * sz; - 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; + cycz = cy * cz; + sxsy = sx * sy; + cysz = cy * sz; + + dest[0][0] = cycz - sxsy * sz; + dest[0][1] = cz * sxsy + cysz; + dest[0][2] = -cx * sy; + dest[1][0] = -cx * sz; + dest[1][1] = cx * cz; + dest[1][2] = sx; + dest[2][0] = cz * sy + cysz * sx; + dest[2][1] = -cycz * sx + sy * sz; + dest[2][2] = cx * cy; + 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 build rotation matrix from euler angles * - * @param[in] angles angles as vector [Ey, Ex, Ez] + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[out] dest rotation matrix */ CGLM_INLINE void -glm_euler_yxz(vec3 angles, - mat4 dest) { +glm_euler_zyx(vec3 angles, mat4 dest) { float cx, cy, cz, - sx, sy, sz; + sx, sy, sz, czsx, cxcz, sysz; - sx = sinf(angles[0]); cx = cosf(angles[0]); - sy = sinf(angles[1]); cy = cosf(angles[1]); - sz = sinf(angles[2]); cz = cosf(angles[2]); + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); - dest[0][0] = cy * cz - sx * sy * sz; - dest[0][1] = cz * sx * sy + cy * sz; - dest[0][2] =-cx * sy; - dest[1][0] =-cx * sz; - dest[1][1] = cx * cz; - dest[1][2] = sx; - dest[2][0] = cz * sy + cy * sx * sz; - dest[2][1] =-cy * cz * sx + sy * sz; - dest[2][2] = cx * cy; - 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; + czsx = cz * sx; + cxcz = cx * cz; + sysz = sy * sz; + + dest[0][0] = cy * cz; + dest[0][1] = cy * sz; + dest[0][2] = -sy; + dest[1][0] = czsx * sy - cx * sz; + dest[1][1] = cxcz + sx * sysz; + dest[1][2] = cy * sx; + dest[2][0] = cxcz * sy + sx * sz; + dest[2][1] = -czsx + cx * sysz; + dest[2][2] = cx * cy; + 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 build rotation matrix from euler angles * - * @param[in] angles angles as vector (ord parameter spceifies angles order) + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] * @param[in] ord euler order * @param[out] dest rotation matrix */ @@ -332,71 +370,71 @@ glm_euler_by_order(vec3 angles, glm_euler_sq ord, mat4 dest) { sysz = sy * sz; switch (ord) { - case GLM_EULER_XYZ: - dest[0][0] = cycz; - dest[0][1] = cysz; - dest[0][2] =-sy; - dest[1][0] = czsx * sy - cxsz; - dest[1][1] = cxcz + sx * sysz; - dest[1][2] = cysx; - dest[2][0] = cx * czsy + sx * sz; - dest[2][1] =-czsx + cx * sysz; - dest[2][2] = cxcy; - break; case GLM_EULER_XZY: - dest[0][0] = cycz; - dest[0][1] = sz; - dest[0][2] =-czsy; - dest[1][0] = sx * sy - cx * cysz; - dest[1][1] = cxcz; - dest[1][2] = cysx + cx * sysz; - dest[2][0] = cx * sy + cysx * sz; - dest[2][1] =-czsx; - dest[2][2] = cxcy - sx * sysz; + dest[0][0] = cycz; + dest[0][1] = sx * sy + cx * cysz; + dest[0][2] = -cx * sy + cysx * sz; + dest[1][0] = -sz; + dest[1][1] = cxcz; + dest[1][2] = czsx; + dest[2][0] = czsy; + dest[2][1] = -cysx + cx * sysz; + dest[2][2] = cxcy + sx * sysz; break; - case GLM_EULER_ZXY: - dest[0][0] = cycz + sx * sysz; - dest[0][1] = cxsz; - dest[0][2] =-czsy + cysx * sz; - dest[1][0] = czsx * sy - cysz; - dest[1][1] = cxcz; - dest[1][2] = cycz * sx + sysz; - dest[2][0] = cx * sy; - dest[2][1] =-sx; - dest[2][2] = cxcy; - break; - case GLM_EULER_ZYX: - dest[0][0] = cycz; - dest[0][1] = czsx * sy + cxsz; - dest[0][2] =-cx * czsy + sx * sz; - dest[1][0] =-cysz; - dest[1][1] = cxcz - sx * sysz; - dest[1][2] = czsx + cx * sysz; - dest[2][0] = sy; - dest[2][1] =-cysx; - dest[2][2] = cxcy; + case GLM_EULER_XYZ: + dest[0][0] = cycz; + dest[0][1] = czsx * sy + cxsz; + dest[0][2] = -cx * czsy + sx * sz; + dest[1][0] = -cysz; + dest[1][1] = cxcz - sx * sysz; + dest[1][2] = czsx + cx * sysz; + dest[2][0] = sy; + dest[2][1] = -cysx; + dest[2][2] = cxcy; break; case GLM_EULER_YXZ: - dest[0][0] = cycz - sx * sysz; - dest[0][1] = czsx * sy + cysz; - dest[0][2] =-cx * sy; - dest[1][0] =-cxsz; - dest[1][1] = cxcz; - dest[1][2] = sx; - dest[2][0] = czsy + cysx * sz; - dest[2][1] =-cycz * sx + sysz; - dest[2][2] = cxcy; + dest[0][0] = cycz + sx * sysz; + dest[0][1] = cxsz; + dest[0][2] = -czsy + cysx * sz; + dest[1][0] = czsx * sy - cysz; + dest[1][1] = cxcz; + dest[1][2] = cycz * sx + sysz; + dest[2][0] = cx * sy; + dest[2][1] = -sx; + dest[2][2] = cxcy; break; case GLM_EULER_YZX: - dest[0][0] = cycz; - dest[0][1] = sx * sy + cx * cysz; - dest[0][2] =-cx * sy + cysx * sz; - dest[1][0] =-sz; - dest[1][1] = cxcz; - dest[1][2] = czsx; - dest[2][0] = czsy; - dest[2][1] =-cysx + cx * sysz; - dest[2][2] = cxcy + sx * sysz; + dest[0][0] = cycz; + dest[0][1] = sz; + dest[0][2] = -czsy; + dest[1][0] = sx * sy - cx * cysz; + dest[1][1] = cxcz; + dest[1][2] = cysx + cx * sysz; + dest[2][0] = cx * sy + cysx * sz; + dest[2][1] = -czsx; + dest[2][2] = cxcy - sx * sysz; + break; + case GLM_EULER_ZXY: + dest[0][0] = cycz - sx * sysz; + dest[0][1] = czsx * sy + cysz; + dest[0][2] = -cx * sy; + dest[1][0] = -cxsz; + dest[1][1] = cxcz; + dest[1][2] = sx; + dest[2][0] = czsy + cysx * sz; + dest[2][1] = -cycz * sx + sysz; + dest[2][2] = cxcy; + break; + case GLM_EULER_ZYX: + dest[0][0] = cycz; + dest[0][1] = cysz; + dest[0][2] = -sy; + dest[1][0] = czsx * sy - cxsz; + dest[1][1] = cxcz + sx * sysz; + dest[1][2] = cysx; + dest[2][0] = cx * czsy + sx * sz; + dest[2][1] = -czsx + cx * sysz; + dest[2][2] = cxcy; break; } diff --git a/include/cglm/io.h b/include/cglm/io.h index 2ee8ee8..f38fad7 100644 --- a/include/cglm/io.h +++ b/include/cglm/io.h @@ -171,4 +171,33 @@ glm_versor_print(versor vec, #undef m } +CGLM_INLINE +void +glm_aabb_print(vec3 bbox[2], + const char * __restrict tag, + FILE * __restrict ostream) { + int i, j; + +#define m 3 + + fprintf(ostream, "AABB (%s):\n", tag ? tag: "float"); + + for (i = 0; i < 2; i++) { + fprintf(ostream, "\t|"); + + for (j = 0; j < m; j++) { + fprintf(ostream, "%0.4f", bbox[i][j]); + + if (j != m - 1) + fprintf(ostream, "\t"); + } + + fprintf(ostream, "|\n"); + } + + fprintf(ostream, "\n"); + +#undef m +} + #endif /* cglm_io_h */ diff --git a/include/cglm/util.h b/include/cglm/util.h index 12db1a2..2a9ece7 100644 --- a/include/cglm/util.h +++ b/include/cglm/util.h @@ -21,7 +21,9 @@ #include "common.h" /*! - * @brief get sign of 32 bit integer as +1 or -1 + * @brief get sign of 32 bit integer as +1, -1, 0 + * + * Important: It returns 0 for zero input * * @param val integer value */ @@ -31,6 +33,19 @@ glm_sign(int val) { return ((val >> 31) - (-val >> 31)); } +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param val float value + */ +CGLM_INLINE +float +glm_signf(float val) { + return (val > 0.0f) - (val < 0.0f); +} + /*! * @brief convert degree to radians * @@ -115,4 +130,17 @@ glm_max(float a, float b) { return b; } +/*! + * @brief clamp a number between min and max + * + * @param[in] val value to clamp + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +CGLM_INLINE +float +glm_clamp(float val, float minVal, float maxVal) { + return glm_min(glm_max(val, minVal), maxVal); +} + #endif /* cglm_util_h */ diff --git a/include/cglm/vec3.h b/include/cglm/vec3.h index 26adf7d..e2108f3 100644 --- a/include/cglm/vec3.h +++ b/include/cglm/vec3.h @@ -46,6 +46,7 @@ CGLM_INLINE void glm_vec_maxv(vec3 v1, vec3 v2, vec3 dest); CGLM_INLINE void glm_vec_minv(vec3 v1, vec3 v2, vec3 dest); CGLM_INLINE void glm_vec_ortho(vec3 v, vec3 dest); + CGLM_INLINE void glm_vec_clamp(vec3 v, float minVal, float maxVal); Convenient: CGLM_INLINE void glm_cross(vec3 a, vec3 b, vec3 d); @@ -478,6 +479,21 @@ glm_vec_ortho(vec3 v, vec3 dest) { dest[2] = v[0] - v[1]; } +/*! + * @brief clamp vector's individual members between min and max values + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +CGLM_INLINE +void +glm_vec_clamp(vec3 v, float minVal, float maxVal) { + v[0] = glm_clamp(v[0], minVal, maxVal); + v[1] = glm_clamp(v[1], minVal, maxVal); + v[2] = glm_clamp(v[2], minVal, maxVal); +} + /*! * @brief vec3 cross product * diff --git a/include/cglm/vec4.h b/include/cglm/vec4.h index a656166..95bab09 100644 --- a/include/cglm/vec4.h +++ b/include/cglm/vec4.h @@ -40,6 +40,7 @@ CGLM_INLINE float glm_vec4_distance(vec4 v1, vec4 v2); CGLM_INLINE void glm_vec4_maxv(vec4 v1, vec4 v2, vec4 dest); CGLM_INLINE void glm_vec4_minv(vec4 v1, vec4 v2, vec4 dest); + CGLM_INLINE void glm_vec4_clamp(vec4 v, float minVal, float maxVal); */ #ifndef cglm_vec4_h @@ -373,4 +374,20 @@ glm_vec4_minv(vec4 v1, vec4 v2, vec4 dest) { dest[3] = glm_min(v1[3], v2[3]); } +/*! + * @brief clamp vector's individual members between min and max values + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +CGLM_INLINE +void +glm_vec4_clamp(vec4 v, float minVal, float maxVal) { + v[0] = glm_clamp(v[0], minVal, maxVal); + v[1] = glm_clamp(v[1], minVal, maxVal); + v[2] = glm_clamp(v[2], minVal, maxVal); + v[3] = glm_clamp(v[3], minVal, maxVal); +} + #endif /* cglm_vec4_h */ diff --git a/include/cglm/version.h b/include/cglm/version.h index a025ccb..c27a26e 100644 --- a/include/cglm/version.h +++ b/include/cglm/version.h @@ -10,6 +10,6 @@ #define CGLM_VERSION_MAJOR 0 #define CGLM_VERSION_MINOR 3 -#define CGLM_VERSION_PATCH 5 +#define CGLM_VERSION_PATCH 6 #endif /* cglm_version_h */ diff --git a/makefile.am b/makefile.am index ff951c8..a81bdd5 100644 --- a/makefile.am +++ b/makefile.am @@ -16,7 +16,7 @@ AM_CFLAGS = -Wall \ -pedantic lib_LTLIBRARIES = libcglm.la -libcglm_la_LDFLAGS = -no-undefined -version-info 0:3:5 +libcglm_la_LDFLAGS = -no-undefined -version-info 0:1:0 checkLDFLAGS = -L./.libs \ -L./test/lib/cmocka/build/src \ @@ -104,7 +104,9 @@ test_tests_SOURCES=\ test/src/test_common.c \ test/src/test_main.c \ test/src/test_mat4.c \ - test/src/test_cam.c + test/src/test_cam.c \ + test/src/test_clamp.c \ + test/src/test_euler.c all-local: sh ./post-build.sh diff --git a/src/euler.c b/src/euler.c index 5c945e2..44d6edb 100644 --- a/src/euler.c +++ b/src/euler.c @@ -20,6 +20,12 @@ glmc_euler(vec3 angles, mat4 dest) { glm_euler(angles, dest); } +CGLM_EXPORT +void +glmc_euler_xyz(vec3 angles, mat4 dest) { + glm_euler_xyz(angles, dest); +} + CGLM_EXPORT void glmc_euler_zyx(vec3 angles, mat4 dest) { diff --git a/src/vec3.c b/src/vec3.c index f377d88..ebc677d 100644 --- a/src/vec3.c +++ b/src/vec3.c @@ -139,3 +139,9 @@ void glmc_vec_minv(vec3 v1, vec3 v2, vec3 dest) { glm_vec_maxv(v1, v2, dest); } + +CGLM_EXPORT +void +glmc_vec_clamp(vec3 v, float minVal, float maxVal) { + glm_vec_clamp(v, minVal, maxVal); +} diff --git a/src/vec4.c b/src/vec4.c index 279652d..f5f6a06 100644 --- a/src/vec4.c +++ b/src/vec4.c @@ -109,3 +109,9 @@ void glmc_vec4_minv(vec4 v1, vec4 v2, vec4 dest) { glm_vec4_maxv(v1, v2, dest); } + +CGLM_EXPORT +void +glmc_vec4_clamp(vec4 v, float minVal, float maxVal) { + glm_vec4_clamp(v, minVal, maxVal); +} diff --git a/test/src/test_clamp.c b/test/src/test_clamp.c new file mode 100644 index 0000000..ee98958 --- /dev/null +++ b/test/src/test_clamp.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" + +void +test_clamp(void **state) { + vec3 v3 = {15.07, 0.4, 17.3}; + vec4 v4 = {5.07, 2.3, 1.3, 1.4}; + + assert_true(glm_clamp(1.6f, 0.0f, 1.0f) == 1.0f); + assert_true(glm_clamp(-1.6f, 0.0f, 1.0f) == 0.0f); + assert_true(glm_clamp(0.6f, 0.0f, 1.0f) == 0.6f); + + glm_vec_clamp(v3, 0.0, 1.0); + glm_vec4_clamp(v4, 1.5, 3.0); + + assert_true(v3[0] == 1.0f); + assert_true(v3[1] == 0.4f); + assert_true(v3[2] == 1.0f); + + assert_true(v4[0] == 3.0f); + assert_true(v4[1] == 2.3f); + assert_true(v4[2] == 1.5f); + assert_true(v4[3] == 1.5f); +} diff --git a/test/src/test_common.c b/test/src/test_common.c index e6f36ab..23f2b50 100644 --- a/test/src/test_common.c +++ b/test/src/test_common.c @@ -50,3 +50,10 @@ 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); +} diff --git a/test/src/test_common.h b/test/src/test_common.h index 5fff3d7..aeea4d6 100644 --- a/test/src/test_common.h +++ b/test/src/test_common.h @@ -31,4 +31,7 @@ test_assert_mat4_eq(mat4 m1, mat4 m2); void test_assert_mat4_eq2(mat4 m1, mat4 m2, float eps); +void +test_assert_vec3_eq(vec3 v1, vec3 v2); + #endif /* test_common_h */ diff --git a/test/src/test_euler.c b/test/src/test_euler.c new file mode 100644 index 0000000..b71c4cb --- /dev/null +++ b/test/src/test_euler.c @@ -0,0 +1,44 @@ +/* + * 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" + +void +test_euler(void **state) { + mat4 rot1, rot2; + vec3 inAngles, outAngles; + + inAngles[0] = glm_rad(-45.0f); /* X angle */ + inAngles[1] = glm_rad(88.0f); /* Y angle */ + inAngles[2] = glm_rad(18.0f); /* Z angle */ + + glm_euler_xyz(inAngles, rot1); + + /* extract angles */ + glmc_euler_angles(rot1, outAngles); + + /* angles must be equal in that range */ + test_assert_vec3_eq(inAngles, outAngles); + + /* matrices must be equal */ + glmc_euler_xyz(outAngles, rot2); + test_assert_mat4_eq(rot1, rot2); + + /* change range */ + inAngles[0] = glm_rad(-145.0f); /* X angle */ + inAngles[1] = glm_rad(818.0f); /* Y angle */ + inAngles[2] = glm_rad(181.0f); /* Z angle */ + + glm_euler_xyz(inAngles, rot1); + glmc_euler_angles(rot1, outAngles); + + /* angles may not be equal but matrices MUST! */ + + /* matrices must be equal */ + glmc_euler_xyz(outAngles, rot2); + test_assert_mat4_eq(rot1, rot2); +} diff --git a/test/src/test_main.c b/test/src/test_main.c index 61fa7d5..dad20f9 100644 --- a/test/src/test_main.c +++ b/test/src/test_main.c @@ -18,6 +18,12 @@ main(int argc, const char * argv[]) { /* project */ cmocka_unit_test(test_project) + + /* vector */ + cmocka_unit_test(test_clamp), + + /* euler */ + cmocka_unit_test(test_euler) }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/test/src/test_tests.h b/test/src/test_tests.h index 26eb91d..7234782 100644 --- a/test/src/test_tests.h +++ b/test/src/test_tests.h @@ -19,4 +19,10 @@ test_camera_decomp(void **state); void test_project(void **state); +void +test_clamp(void **state); + +void +test_euler(void **state); + #endif /* test_tests_h */