diff --git a/docs/source/api.rst b/docs/source/api.rst index e2c03d4..c61365b 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -41,6 +41,7 @@ Follow the :doc:`build` documentation for this vec4-ext color plane + project util io call diff --git a/docs/source/project.rst b/docs/source/project.rst new file mode 100644 index 0000000..b31a86a --- /dev/null +++ b/docs/source/project.rst @@ -0,0 +1,102 @@ +.. default-domain:: C + +Project / UnProject +================================================================================ + +Header: cglm/project.h + +Viewport is required as *vec4* **[X, Y, Width, Height]** but this doesn't mean +that you should store it as **vec4**. You can convert your data representation +to vec4 before passing it to related functions. + +Table of contents (click to go): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Functions: + +1. :c:func:`glm_unprojecti` +#. :c:func:`glm_unproject` +#. :c:func:`glm_project` + +Functions documentation +~~~~~~~~~~~~~~~~~~~~~~~ + +.. c:function:: void glm_unprojecti(vec3 pos, mat4 invMat, vec4 vp, vec3 dest) + + | maps the specified viewport coordinates into specified space [1] + the matrix should contain projection matrix. + + if you don't have ( and don't want to have ) an inverse matrix then use + glm_unproject version. You may use existing inverse of matrix in somewhere + else, this is why glm_unprojecti exists to save save inversion cost + + [1] space: + - if m = invProj: View Space + - if m = invViewProj: World Space + - if m = invMVP: Object Space + + You probably want to map the coordinates into object space + so use invMVP as m + + Computing viewProj: + + .. code-block:: c + + glm_mat4_mul(proj, view, viewProj); + glm_mat4_mul(viewProj, model, MVP); + glm_mat4_inv(viewProj, invMVP); + + Parameters: + | *[in]* **pos** point/position in viewport coordinates + | *[in]* **invMat** matrix (see brief) + | *[in]* **vp** viewport as [x, y, width, height] + | *[out]* **dest** unprojected coordinates + +.. c:function:: void glm_unproject(vec3 pos, mat4 m, vec4 vp, vec3 dest) + + | maps the specified viewport coordinates into specified space [1] + the matrix should contain projection matrix. + + this is same as glm_unprojecti except this function get inverse matrix for + you. + + [1] space: + - if m = proj: View Space + - if m = viewProj: World Space + - if m = MVP: Object Space + + You probably want to map the coordinates into object space so use MVP as m + + Computing viewProj and MVP: + + .. code-block:: c + + glm_mat4_mul(proj, view, viewProj); + glm_mat4_mul(viewProj, model, MVP); + + Parameters: + | *[in]* **pos** point/position in viewport coordinates + | *[in]* **m** matrix (see brief) + | *[in]* **vp** viewport as [x, y, width, height] + | *[out]* **dest** unprojected coordinates + +.. c:function:: void glm_project(vec3 pos, mat4 m, vec4 vp, vec3 dest) + + | map object coordinates to window coordinates + + Computing MVP: + + .. code-block:: c + + glm_mat4_mul(proj, view, viewProj); + glm_mat4_mul(viewProj, model, MVP); + + this could be useful for gettng a bbox which fits with view frustum and + object bounding boxes. In this case you crop view frustum box with objects + box + + Parameters: + | *[in]* **pos** object coordinates + | *[in]* **m** MVP matrix + | *[in]* **vp** viewport as [x, y, width, height] + | *[out]* **dest** projected coordinates diff --git a/include/cglm/call.h b/include/cglm/call.h index 3f7bf14..ce08b78 100644 --- a/include/cglm/call.h +++ b/include/cglm/call.h @@ -24,6 +24,7 @@ extern "C" { #include "call/frustum.h" #include "call/box.h" #include "call/io.h" +#include "call/project.h" #ifdef __cplusplus } diff --git a/include/cglm/call/project.h b/include/cglm/call/project.h new file mode 100644 index 0000000..35ac087 --- /dev/null +++ b/include/cglm/call/project.h @@ -0,0 +1,33 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +#ifndef cglmc_project_h +#define cglmc_project_h +#ifdef __cplusplus +extern "C" { +#endif + +#include "../cglm.h" + +CGLM_EXPORT +void +glmc_unprojecti(vec3 pos, mat4 invMat, vec4 vp, vec3 dest); + +CGLM_EXPORT +void +glmc_unproject(vec3 pos, mat4 m, vec4 vp, vec3 dest); + +CGLM_EXPORT +void +glmc_project(vec3 pos, mat4 m, vec4 vp, vec3 dest); + +#ifdef __cplusplus +} +#endif +#endif /* cglmc_project_h */ + + diff --git a/include/cglm/cglm.h b/include/cglm/cglm.h index e99bb09..52c7e97 100644 --- a/include/cglm/cglm.h +++ b/include/cglm/cglm.h @@ -23,5 +23,6 @@ #include "color.h" #include "util.h" #include "io.h" +#include "project.h" #endif /* cglm_h */ diff --git a/include/cglm/project.h b/include/cglm/project.h new file mode 100644 index 0000000..fea44b0 --- /dev/null +++ b/include/cglm/project.h @@ -0,0 +1,117 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +#ifndef cglm_project_h +#define cglm_project_h + +#include "mat4.h" +#include "vec3.h" +#include "vec4.h" + +/*! + * @brief maps the specified viewport coordinates into specified space [1] + * the matrix should contain projection matrix. + * + * if you don't have ( and don't want to have ) an inverse matrix then use + * glm_unproject version. You may use existing inverse of matrix in somewhere + * else, this is why glm_unprojecti exists to save save inversion cost + * + * [1] space: + * 1- if m = invProj: View Space + * 2- if m = invViewProj: World Space + * 3- if m = invMVP: Object Space + * + * You probably want to map the coordinates into object space + * so use invMVP as m + * + * Computing viewProj: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * glm_mat4_inv(viewProj, invMVP); + * + * @param[in] pos point/position in viewport coordinates + * @param[in] invMat matrix (see brief) + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest unprojected coordinates + */ +CGLM_INLINE +void +glm_unprojecti(vec3 pos, mat4 invMat, vec4 vp, vec3 dest) { + vec4 v; + + v[0] = 2.0f * (pos[0] - vp[0]) / vp[2] - 1.0f; + v[1] = 2.0f * (pos[1] - vp[1]) / vp[3] - 1.0f; + v[2] = 2.0f * pos[2] - 1.0f; + v[3] = 1.0f; + + glm_mat4_mulv(invMat, v, v); + glm_vec4_scale(v, 1.0f / v[3], v); + glm_vec3(v, dest); +} + +/*! + * @brief maps the specified viewport coordinates into specified space [1] + * the matrix should contain projection matrix. + * + * this is same as glm_unprojecti except this function get inverse matrix for + * you. + * + * [1] space: + * 1- if m = proj: View Space + * 2- if m = viewProj: World Space + * 3- if m = MVP: Object Space + * + * You probably want to map the coordinates into object space + * so use MVP as m + * + * Computing viewProj and MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] pos point/position in viewport coordinates + * @param[in] m matrix (see brief) + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest unprojected coordinates + */ +CGLM_INLINE +void +glm_unproject(vec3 pos, mat4 m, vec4 vp, vec3 dest) { + mat4 inv; + glm_mat4_inv(m, inv); + glm_unprojecti(pos, inv, vp, dest); +} + +/*! + * @brief map object coordinates to window coordinates + * + * Computing MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] pos object coordinates + * @param[in] m MVP matrix + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest projected coordinates + */ +CGLM_INLINE +void +glm_project(vec3 pos, mat4 m, vec4 vp, vec3 dest) { + vec4 pos4, vone = GLM_VEC4_ONE_INIT; + + glm_vec4(pos, 1.0f, pos4); + + glm_mat4_mulv(m, pos4, pos4); + glm_vec4_scale(pos4, 1.0f / pos4[3], pos4); /* pos = pos / pos.w */ + glm_vec4_add(pos4, vone, pos4); + glm_vec4_scale(pos4, 0.5f, pos4); + + dest[0] = pos4[0] * vp[2] + vp[0]; + dest[1] = pos4[1] * vp[3] + vp[1]; + dest[2] = pos4[2]; +} + +#endif /* cglm_project_h */ diff --git a/include/cglm/types.h b/include/cglm/types.h index a4170c4..c411d8b 100644 --- a/include/cglm/types.h +++ b/include/cglm/types.h @@ -14,6 +14,7 @@ # define CGLM_ALIGN(X) __attribute((aligned(X))) #endif +typedef float vec2[2]; typedef float vec3[3]; typedef int ivec3[3]; typedef CGLM_ALIGN(16) float vec4[4]; diff --git a/include/cglm/util.h b/include/cglm/util.h index 2a9ece7..85fc789 100644 --- a/include/cglm/util.h +++ b/include/cglm/util.h @@ -43,7 +43,7 @@ glm_sign(int val) { CGLM_INLINE float glm_signf(float val) { - return (val > 0.0f) - (val < 0.0f); + return (float)((val > 0.0f) - (val < 0.0f)); } /*! diff --git a/makefile.am b/makefile.am index a81bdd5..217fff3 100644 --- a/makefile.am +++ b/makefile.am @@ -98,13 +98,15 @@ libcglm_la_SOURCES=\ src/mat4.c \ src/plane.c \ src/frustum.c \ - src/box.c + src/box.c \ + src/project.c 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_project.c \ test/src/test_clamp.c \ test/src/test_euler.c diff --git a/src/project.c b/src/project.c new file mode 100644 index 0000000..91c7128 --- /dev/null +++ b/src/project.c @@ -0,0 +1,27 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +#include "../include/cglm/cglm.h" +#include "../include/cglm/call.h" + +CGLM_EXPORT +void +glmc_unprojecti(vec3 pos, mat4 invMat, vec4 vp, vec3 dest) { + glm_unprojecti(pos, invMat, vp, dest); +} + +CGLM_EXPORT +void +glmc_unproject(vec3 pos, mat4 m, vec4 vp, vec3 dest) { + glm_unproject(pos, m, vp, dest); +} + +CGLM_EXPORT +void +glmc_project(vec3 pos, mat4 m, vec4 vp, vec3 dest) { + glm_project(pos, m, vp, dest); +} diff --git a/test/src/test_main.c b/test/src/test_main.c index 38d2fe4..5c1a647 100644 --- a/test/src/test_main.c +++ b/test/src/test_main.c @@ -16,6 +16,9 @@ main(int argc, const char * argv[]) { cmocka_unit_test(test_camera_lookat), cmocka_unit_test(test_camera_decomp), + /* project */ + cmocka_unit_test(test_project), + /* vector */ cmocka_unit_test(test_clamp), diff --git a/test/src/test_project.c b/test/src/test_project.c new file mode 100644 index 0000000..4cdac9e --- /dev/null +++ b/test/src/test_project.c @@ -0,0 +1,31 @@ +/* + * 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_project(void **state) { + mat4 model, view, proj, mvp; + vec4 viewport = {0.0f, 0.0f, 800.0f, 600.0f}; + vec3 pos = {13.0f, 45.0f, 0.74f}; + vec3 projected, unprojected; + + glm_translate_make(model, (vec3){0.0f, 0.0f, -10.0f}); + glm_lookat((vec3){0.0f, 0.0f, 0.0f}, pos, GLM_YUP, view); + + glm_perspective_default(0.5f, proj); + glm_mat4_mulN((mat4 *[]){&proj, &view, &model}, 3, mvp); + + glmc_project(pos, mvp, viewport, projected); + glmc_unproject(projected, mvp, viewport, unprojected); + + /* unprojected of projected vector must be same as original one */ + /* we used 0.01 because of projection floating point errors */ + assert_true(fabsf(pos[0] - unprojected[0]) < 0.01); + assert_true(fabsf(pos[1] - unprojected[1]) < 0.01); + assert_true(fabsf(pos[2] - unprojected[2]) < 0.01); +} diff --git a/test/src/test_tests.h b/test/src/test_tests.h index b1288f7..7234782 100644 --- a/test/src/test_tests.h +++ b/test/src/test_tests.h @@ -16,6 +16,9 @@ test_camera_lookat(void **state); void test_camera_decomp(void **state); +void +test_project(void **state); + void test_clamp(void **state); diff --git a/win/cglm.vcxproj b/win/cglm.vcxproj index 4a0463d..415a87d 100644 --- a/win/cglm.vcxproj +++ b/win/cglm.vcxproj @@ -29,6 +29,7 @@ + @@ -47,6 +48,7 @@ + @@ -60,6 +62,7 @@ + diff --git a/win/cglm.vcxproj.filters b/win/cglm.vcxproj.filters index 506f3d8..3090d72 100644 --- a/win/cglm.vcxproj.filters +++ b/win/cglm.vcxproj.filters @@ -75,6 +75,9 @@ src + + src + @@ -206,5 +209,11 @@ include\cglm + + include\cglm + + + include\cglm\call + \ No newline at end of file