diff --git a/CMakeLists.txt b/CMakeLists.txt index df0b5e4..3e31ba7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,16 +36,16 @@ set(SOURCE_FILES third-party/stb/stb_vorbis.c third-party/glad/src/glad.c - src/config.h - src/context.h - src/context.c src/main.c - src/audio.c + src/config.h + src/context.c src/context.h + src/audio.c src/audio.h src/util.c src/util.h src/rendering.c src/rendering.h src/textures.c src/textures.h src/input.c src/input.h src/text.c src/text.h + src/camera.c src/camera.h src/game_api.h src/game/game.c src/game/game.h diff --git a/src/camera.c b/src/camera.c new file mode 100644 index 0000000..f15a522 --- /dev/null +++ b/src/camera.c @@ -0,0 +1,27 @@ +#include "camera.h" + +t_matrix4 camera_look_at(const t_camera *const camera) { + /* from cglm */ + const t_fvec3 f = m_vec_norm(m_vec_sub(camera->pos, camera->target)); + const t_fvec3 r = m_vec_norm(m_vec_cross(f, camera->up)); + const t_fvec3 u = m_vec_cross(f, r); + + t_matrix4 result; + + result.row[0].x = r.x; + result.row[0].y = u.x; + result.row[0].z =-f.x; + result.row[1].x = r.y; + result.row[1].y = u.y; + result.row[1].z =-f.y; + result.row[2].x = r.z; + result.row[2].y = u.z; + result.row[2].z =-f.z; + result.row[3].x =-m_vec_dot(r, camera->pos); + result.row[3].y =-m_vec_dot(u, camera->pos); + result.row[3].z = m_vec_dot(f, camera->pos); + result.row[0].w = result.row[1].w = result.row[2].w = 0.0f; + result.row[3].w = 1.0f; + + return result; +} diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 0000000..128db62 --- /dev/null +++ b/src/camera.h @@ -0,0 +1,15 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "util.h" + +/* first person camera class */ +typedef struct camera { + t_fvec3 pos; /* eye position */ + t_fvec3 target; /* target point */ + t_fvec3 up; /* normalized up vector */ +} t_camera; + +t_matrix4 camera_look_at(const t_camera *camera); + +#endif diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index ac0d281..0e5b268 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,6 +11,15 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); + static t_camera cam = { .pos = { 0 }, .target = { 0, 0, -1 }, .up = { 0, -1, 0 } }; + + if (input_is_action_pressed(&ctx.input, "player_left")) + cam.pos.x -= 0.01f; + if (input_is_action_pressed(&ctx.input, "player_right")) + cam.pos.x += 0.01f; + + push_camera(&cam); + push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/light.png", .color = (t_color){255, 0, 0, 255}, }); diff --git a/src/rendering.c b/src/rendering.c index 231a670..887b23b 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -13,6 +13,10 @@ #include +/* TODO: have a default initialized one */ +static t_matrix4 camera_projection; + + void render_queue_clear(void) { /* since i don't intend to free the queues, */ /* it's faster and simpler to just "start over" */ @@ -160,7 +164,7 @@ void render(void) { { glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + glLoadMatrixf(&camera_projection.row[0].x); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -168,6 +172,9 @@ void render(void) { render_space(); } + /* TODO: only do it when transition between spaces is needed */ + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + { glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -181,3 +188,8 @@ void render(void) { SDL_GL_SwapWindow(ctx.window); } + +void push_camera(const t_camera *const camera) { + /* TODO: skip recaulculating if it's the same? */ + camera_projection = camera_look_at(camera); +} diff --git a/src/rendering.h b/src/rendering.h index 5d31cb4..059aaa5 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -2,6 +2,7 @@ #define RENDERING_H #include "util.h" +#include "camera.h" #include @@ -63,6 +64,9 @@ void unfurl_triangle(const char *path, // t_fvec2 scaling, // t_frect uvs); +/* pushes a camera state to be used for all future unfurl_* commands */ +void push_camera(const t_camera *camera); + /* renders the background, then the primitives in all render queues */ void render(void); diff --git a/src/rendering/circles.h b/src/rendering/circles.h index d8f9bf5..67ce710 100644 --- a/src/rendering/circles.h +++ b/src/rendering/circles.h @@ -3,8 +3,8 @@ #define CIRCLES_H #include "../util.h" -#include "internal_api.h" #include "../context.h" +#include "internal_api.h" #include #include diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 9c3e24e..44ca2a3 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -7,6 +7,7 @@ #include "../context.h" #include "../util.h" #include "quad_element_buffer.h" +#include "internal_api.h" #include diff --git a/src/rendering/triangles.h b/src/rendering/triangles.h index 8e88b11..f48da2a 100644 --- a/src/rendering/triangles.h +++ b/src/rendering/triangles.h @@ -4,6 +4,7 @@ #include "../textures.h" #include "../context.h" +#include "internal_api.h" #include @@ -66,6 +67,8 @@ static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); t_rect dims = textures_get_dims(&ctx.texture_cache, texture_key); + /* TODO: fast path for uvs mapped directly on srcrect corners? */ + const float wr = (float)srcrect.w / (float)dims.w; const float hr = (float)srcrect.h / (float)dims.h; const float xr = (float)srcrect.x / (float)dims.w; diff --git a/src/util.c b/src/util.c index b368957..5643e30 100644 --- a/src/util.c +++ b/src/util.c @@ -194,21 +194,6 @@ t_fvec2 frect_center(t_frect rect) { } -t_fvec2 fvec2_from_vec2(t_vec2 vec) { - return (t_fvec2) { - .x = (float)vec.x, - .y = (float)vec.y, - }; -} - -t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { - return (t_fvec2) { - .x = (float)vec.x, - .y = (float)vec.y, - }; -} - - void tick_timer(int *value) { *value = MAX(*value - 1, 0); } diff --git a/src/util.h b/src/util.h index 44ea51f..f4b64fd 100644 --- a/src/util.h +++ b/src/util.h @@ -1,6 +1,7 @@ #ifndef UTIL_H #define UTIL_H +#include "vec.h" #include #include @@ -78,40 +79,13 @@ _Alignas(4) } t_color; -/* a point in some space (integer) */ -typedef struct vec2 { - int x, y; -} t_vec2; - - -/* a point in some space (floating point) */ -typedef struct fvec2 { -_Alignas(8) - float x; - float y; -} t_fvec2; - - -/* a point in some three dimension space (floating point) */ -/* y goes up, x goes to the right */ -typedef struct fvec3 { -_Alignas(8) - float x; - float y; - float z; -} t_fvec3; - - -/* a point in some space (short) */ -typedef struct shvec2 { - short x, y; -} t_shvec2; - - /* a rectangle with the origin at the upper left (integer) */ typedef struct rect { - int x, y; - int w, h; +_Alignas(16) + int32_t x; + int32_t y; + int32_t w; + int32_t h; } t_rect; @@ -135,15 +109,9 @@ t_frect to_frect(t_rect rect); t_fvec2 frect_center(t_frect rect); -/* aren't macros to prevent double evaluation with side effects */ -/* maybe could be inlined? i hope LTO will resolve this */ -t_fvec2 fvec2_from_vec2(t_vec2 vec); -t_fvec2 fvec2_from_shvec2(t_shvec2 vec); - -#define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2), \ - t_vec2: fvec2_from_vec2, \ - t_shvec2: fvec2_from_shvec2 \ - )(p_any_vec2)) +typedef struct matrix4 { + t_fvec4 row[4]; +} t_matrix4; /* decrements an lvalue (which should be an int), stopping at 0 */ diff --git a/src/vec.h b/src/vec.h new file mode 100644 index 0000000..2aa5ba2 --- /dev/null +++ b/src/vec.h @@ -0,0 +1,121 @@ +#ifndef VEC_H +#define VEC_H + +#include +#include + + +/* a point in some space (integer) */ +typedef struct vec2 { +_Alignas(8) + int32_t x; + int32_t y; +} t_vec2; + + +/* a point in some space (floating point) */ +typedef struct fvec2 { +_Alignas(8) + float x; + float y; +} t_fvec2; + + +/* a point in some three dimension space (floating point) */ +/* y goes up, x goes to the right */ +typedef struct fvec3 { +_Alignas(16) + float x; + float y; + float z; +} t_fvec3; + + +/* a point in some three dimension space (floating point) */ +/* y goes up, x goes to the right */ +typedef struct fvec4 { +_Alignas(16) + float x; + float y; + float z; + float w; +} t_fvec4; + + +/* a point in some space (short) */ +typedef struct shvec2 { +_Alignas(4) + int16_t x; + int16_t y; +} t_shvec2; + + +/* aren't macros to prevent double evaluation with side effects */ +/* maybe could be inlined? i hope LTO will resolve this */ +static inline t_fvec2 fvec2_from_vec2(t_vec2 vec) { + return (t_fvec2) { + .x = (float)vec.x, + .y = (float)vec.y, + }; +} + +static inline t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { + return (t_fvec2) { + .x = (float)vec.x, + .y = (float)vec.y, + }; +} + +static inline t_fvec3 fvec3_sub(t_fvec3 a, t_fvec3 b) { + return (t_fvec3) { a.x - b.x, a.y - b.y, a.z - b.z }; +} + +static inline t_fvec3 fvec3_scale(t_fvec3 a, float s) { + return (t_fvec3) { a.x * s, a.y * s, a.z * s }; +} + +static inline float fvec3_dot(t_fvec3 a, t_fvec3 b) { + return a.x * b.x + a.x * b.x + a.z * b.z; +} + +static inline t_fvec3 fvec3_cross(t_fvec3 a, t_fvec3 b) { + return (t_fvec3) { + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x, + }; +} + +/* TODO: fast_sqrt version? */ +static inline t_fvec3 fvec3_norm(t_fvec3 a) { + const float n = sqrtf(fvec3_dot(a, a)); + /* TODO: do we need truncating over epsilon as cglm does? */ + return fvec3_scale(a, 1.0f / n); +} + +#define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2), \ + t_vec2: fvec2_from_vec2, \ + t_shvec2: fvec2_from_shvec2 \ + )(p_any_vec2)) + +#define m_vec_sub(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ + t_fvec3: fvec3_sub \ + )(p_any_vec0, p_any_vec1)) + +#define m_vec_scale(p_any_vec, p_any_scalar) (_Generic((p_any_vec), \ + t_fvec3: fvec3_scale \ + )(p_any_vec, p_any_scalar)) + +#define m_vec_dot(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ + t_fvec3: fvec3_dot \ + )(p_any_vec0, p_any_vec1)) + +#define m_vec_cross(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ + t_fvec3: fvec3_cross \ + )(p_any_vec0, p_any_vec1)) + +#define m_vec_norm(p_any_vec) (_Generic((p_any_vec), \ + t_fvec3: fvec3_norm \ + )(p_any_vec)) + +#endif