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