diff --git a/CMakeLists.txt b/CMakeLists.txt index f16c4cf..c532330 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,9 @@ add_shaders(shaders ) add_executable(demo - main.c gpu.c + linear_alg.c + main.c ) configure_target(demo) target_include_directories(demo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/gpu.h b/gpu.h index e1e5a30..16d39a2 100644 --- a/gpu.h +++ b/gpu.h @@ -1,23 +1,13 @@ #ifndef GPU_H #define GPU_H +#include "linear_alg.h" + #define GLFW_INCLUDE_VULKAN #include #define SWAPCHAIN_IMG_BUFFER_SIZE 4 -typedef struct { - float x, y, z; -} vec3_t; - -typedef struct { - float x, y, z, w; -} vec4_t; - -typedef struct { - vec4_t x, y, z, w; -} mat4_t; - typedef struct { vec3_t position; vec3_t colour; diff --git a/linear_alg.c b/linear_alg.c new file mode 100644 index 0000000..2aa141f --- /dev/null +++ b/linear_alg.c @@ -0,0 +1,76 @@ +#include "linear_alg.h" + +#include + +vec3_t vec3_sub(vec3_t v, vec3_t u) +{ + return (vec3_t) { v.x - u.x, v.y - u.y, v.z - u.z }; +} + +float vec3_len(vec3_t v) +{ + return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); +} + +vec3_t vec3_norm(vec3_t v) +{ + const float l = vec3_len(v); + return (vec3_t) { v.x / l, v.y / l, v.z / l }; +} + +float vec3_dot(vec3_t v, vec3_t u) +{ + return v.x * u.x + v.y * u.y + v.z * u.z; +} + +vec3_t vec3_cross(vec3_t v, vec3_t u) +{ + return (vec3_t) { + v.y * u.z - v.z * u.y, + v.z * u.x - v.x * u.z, + v.x * u.y - v.y * u.x, + }; +} + +mat4_t mat4_mul(mat4_t m, mat4_t n) +{ +#define ELEM(i, j) m.x.i *n.j.x + m.y.i *n.j.y + m.z.i *n.j.z + m.w.i *n.j.w + return (mat4_t) { + { ELEM(x, x), ELEM(y, x), ELEM(z, x), ELEM(w, x) }, + { ELEM(x, y), ELEM(y, y), ELEM(z, y), ELEM(w, y) }, + { ELEM(x, z), ELEM(y, z), ELEM(z, z), ELEM(w, z) }, + { ELEM(x, w), ELEM(y, w), ELEM(z, w), ELEM(w, w) }, + }; +#undef ELEM +} + +mat4_t mat4_lookat(vec3_t camera, vec3_t target, vec3_t up) +{ + const vec3_t s = vec3_sub(target, camera); + const vec3_t s_hat = vec3_norm(s); + const vec3_t u_hat = vec3_norm(vec3_cross(up, s_hat)); + const vec3_t v_hat = vec3_cross(s_hat, u_hat); + return (mat4_t) { + { u_hat.x, v_hat.x, s_hat.x, 0 }, + { u_hat.y, v_hat.y, s_hat.y, 0 }, + { u_hat.z, v_hat.z, s_hat.z, 0 }, + { + -vec3_dot(u_hat, camera), + -vec3_dot(v_hat, camera), + -vec3_dot(s_hat, camera), + 1, + }, + }; +} + +mat4_t mat4_perspective(float aspect, float fov, float near, float far) +{ + const float r = 2 * near * tanf(fov / 2); + const float t = r * aspect; + return (mat4_t) { + { near / t, 0, 0, 0 }, + { 0, -near / r, 0, 0 }, + { 0, 0, far / (far - near), 1 }, + { 0, 0, -far * near / (far - near), 0 }, + }; +} diff --git a/linear_alg.h b/linear_alg.h new file mode 100644 index 0000000..9c92d3f --- /dev/null +++ b/linear_alg.h @@ -0,0 +1,76 @@ +#ifndef LINEAR_ALG_H +#define LINEAR_ALG_H + +#define PI 3.14159265358979323846264 + +typedef struct { + float x, y, z; +} vec3_t; + +typedef struct { + float x, y, z, w; +} vec4_t; + +typedef struct { + vec4_t x, y, z, w; +} mat4_t; + +#define MAT4_ID \ + { \ + { 1, 0, 0, 0 }, \ + { 0, 1, 0, 0 }, \ + { 0, 0, 1, 0 }, \ + { 0, 0, 0, 1 }, \ + } + +#define MAT4_SCALE(sf) \ + { \ + { sf, 0, 0, 0 }, \ + { 0, sf, 0, 0 }, \ + { 0, 0, sf, 0 }, \ + { 0, 0, 0, 1 }, \ + } + +#define MAT4_TRANSLATE(x, y, z) \ + { \ + { 1, 0, 0, 0 }, \ + { 0, 1, 0, 0 }, \ + { 0, 0, 1, 0 }, \ + { x, y, z, 1 }, \ + } + +#define MAT4_ROTATE_X(a) \ + { \ + { 1, 0, 0, 0 }, \ + { 0, cosf(a), sinf(a), 0 }, \ + { 0, -sinf(a), cosf(a), 0 }, \ + { 0, 0, 0, 1 }, \ + } + +#define MAT4_ROTATE_Y(a) \ + { \ + { cosf(a), 0, -sinf(a), 0 }, \ + { 0, 1, 0, 0 }, \ + { sinf(a), 0, cosf(a), 0 }, \ + { 0, 0, 0, 1 }, \ + } + +#define MAT4_ROTATE_Z(a) \ + { \ + { cosf(a), sinf(a), 0, 0 }, \ + { -sinf(a), cosf(a), 0, 0 }, \ + { 0, 0, 1, 0 }, \ + { 0, 0, 0, 1 }, \ + } + +vec3_t vec3_sub(vec3_t v, vec3_t u); +float vec3_len(vec3_t v); +vec3_t vec3_norm(vec3_t v); +float vec3_dot(vec3_t v, vec3_t u); +vec3_t vec3_cross(vec3_t v, vec3_t u); + +mat4_t mat4_mul(mat4_t m, mat4_t n); +mat4_t mat4_lookat(vec3_t camera, vec3_t target, vec3_t up); +mat4_t mat4_perspective(float aspect, float fov, float near, float far); + +#endif diff --git a/main.c b/main.c index d7fbf21..5c57657 100644 --- a/main.c +++ b/main.c @@ -5,10 +5,6 @@ #include #include -#ifndef M_PI -#define M_PI 3.14159265358979324 -#endif - #define W 800 #define H 600 @@ -31,54 +27,6 @@ typedef enum { static axis_t rot_axis = AXIS_Y; -#define ID \ - { \ - { 1, 0, 0, 0 }, \ - { 0, 1, 0, 0 }, \ - { 0, 0, 1, 0 }, \ - { 0, 0, 0, 1 }, \ - } - -#define SCALE(sf) \ - { \ - { sf, 0, 0, 0 }, \ - { 0, sf, 0, 0 }, \ - { 0, 0, sf, 0 }, \ - { 0, 0, 0, 1 }, \ - } - -#define TRANSLATE(x, y, z) \ - { \ - { 1, 0, 0, 0 }, \ - { 0, 1, 0, 0 }, \ - { 0, 0, 1, 0 }, \ - { x, y, z, 1 }, \ - } - -#define ROT_X(a) \ - { \ - { 1, 0, 0, 0 }, \ - { 0, cosf(a), sinf(a), 0 }, \ - { 0, -sinf(a), cosf(a), 0 }, \ - { 0, 0, 0, 1 }, \ - } - -#define ROT_Y(a) \ - { \ - { cosf(a), 0, -sinf(a), 0 }, \ - { 0, 1, 0, 0 }, \ - { sinf(a), 0, cosf(a), 0 }, \ - { 0, 0, 0, 1 }, \ - } - -#define ROT_Z(a) \ - { \ - { cosf(a), sinf(a), 0, 0 }, \ - { -sinf(a), cosf(a), 0, 0 }, \ - { 0, 0, 1, 0 }, \ - { 0, 0, 0, 1 }, \ - } - static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { @@ -106,76 +54,6 @@ key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) } } -static vec3_t sub(vec3_t v, vec3_t u) -{ - return (vec3_t) { v.x - u.x, v.y - u.y, v.z - u.z }; -} - -static float len(vec3_t v) -{ - return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); -} - -static vec3_t norm(vec3_t v) -{ - const float s = len(v); - return (vec3_t) { v.x / s, v.y / s, v.z / s }; -} - -static float dot(vec3_t v, vec3_t u) -{ - return v.x * u.x + v.y * u.y + v.z * u.z; -} - -static vec3_t cross(vec3_t v, vec3_t u) -{ - return (vec3_t) { - v.y * u.z - v.z * u.y, - v.z * u.x - v.x * u.z, - v.x * u.y - v.y * u.x, - }; -} - -static mat4_t mul(mat4_t m, mat4_t n) -{ -#define ELEM(i, j) m.x.i *n.j.x + m.y.i *n.j.y + m.z.i *n.j.z + m.w.i *n.j.w - return (mat4_t) { - { ELEM(x, x), ELEM(y, x), ELEM(z, x), ELEM(w, x) }, - { ELEM(x, y), ELEM(y, y), ELEM(z, y), ELEM(w, y) }, - { ELEM(x, z), ELEM(y, z), ELEM(z, z), ELEM(w, z) }, - { ELEM(x, w), ELEM(y, w), ELEM(z, w), ELEM(w, w) }, - }; -#undef MUL_ELEM -} - -static mat4_t lookat(vec3_t cam, vec3_t tgt, vec3_t up) -{ - const vec3_t s = sub(tgt, cam); - - const vec3_t s_hat = norm(s); - const vec3_t u_hat = norm(cross(up, s_hat)); - const vec3_t v_hat = cross(s_hat, u_hat); - - return (mat4_t) { - { u_hat.x, v_hat.x, s_hat.x, 0 }, - { u_hat.y, v_hat.y, s_hat.y, 0 }, - { u_hat.z, v_hat.z, s_hat.z, 0 }, - { -dot(u_hat, cam), -dot(v_hat, cam), -dot(s_hat, cam), 1 }, - }; -} - -static mat4_t persp(float aspect, float fov, float ncp, float fcp) -{ - const float r = 2 * ncp * tanf(fov / 2); - const float t = r * aspect; - return (mat4_t) { - { ncp / t, 0, 0, 0 }, - { 0, -ncp / r, 0, 0 }, - { 0, 0, fcp / (fcp - ncp), 1 }, - { 0, 0, -fcp * ncp / (fcp - ncp), 0 }, - }; -} - int main() { // Set up window @@ -203,27 +81,27 @@ int main() const vec3_t cam = { 2, 2, -2 }; const vec3_t tgt = { 0, 0, 0 }; mvp_t mvp = { - .model = SCALE(2), - .view = lookat(cam, tgt, (vec3_t) { 0, 1, 0 }), - .proj = persp((float)W / H, M_PI / 6, 0.1, 10.0), + .model = MAT4_SCALE(2), + .view = mat4_lookat(cam, tgt, (vec3_t) { 0, 1, 0 }), + .proj = mat4_perspective((float)W / H, PI / 6, 0.1, 10.0), }; - const mat4_t rotx = ROT_X(M_PI / 128); - const mat4_t roty = ROT_Y(M_PI / 128); - const mat4_t rotz = ROT_Z(M_PI / 128); + const mat4_t rotx = MAT4_ROTATE_X(PI / 128); + const mat4_t roty = MAT4_ROTATE_Y(PI / 128); + const mat4_t rotz = MAT4_ROTATE_Z(PI / 128); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); switch (rot_axis) { case AXIS_X: - mvp.model = mul(rotx, mvp.model); + mvp.model = mat4_mul(rotx, mvp.model); break; case AXIS_Y: - mvp.model = mul(roty, mvp.model); + mvp.model = mat4_mul(roty, mvp.model); break; case AXIS_Z: - mvp.model = mul(rotz, mvp.model); + mvp.model = mat4_mul(rotz, mvp.model); break; }