typedef & PascalCase for ALL structs and enums
This commit is contained in:
		| @@ -13,9 +13,9 @@ | ||||
| void game_tick(void) { | ||||
|     if (ctx.initialization_needed) { | ||||
|         if (!ctx.udata) { | ||||
|             ctx.udata = ccalloc(1, sizeof (struct state)); | ||||
|             ctx.udata = ccalloc(1, sizeof (State)); | ||||
|  | ||||
|             struct state *state = ctx.udata; | ||||
|             State *state = ctx.udata; | ||||
|             state->ctx = &ctx; | ||||
|             state->scene = title_scene(state); | ||||
|         } | ||||
| @@ -48,7 +48,7 @@ void game_tick(void) { | ||||
|         input_bind_action_scancode(&ctx.input, "mouse_capture_toggle", SDL_SCANCODE_ESCAPE); | ||||
|     } | ||||
|  | ||||
|     struct state *state = ctx.udata; | ||||
|     State *state = ctx.udata; | ||||
|  | ||||
|     if (input_is_action_just_pressed(&ctx.input, "debug_toggle")) { | ||||
|         ctx.debug = !ctx.debug; | ||||
| @@ -67,7 +67,7 @@ void game_tick(void) { | ||||
|  | ||||
|  | ||||
| void game_end(void) { | ||||
|     struct state *state = ctx.udata; | ||||
|     State *state = ctx.udata; | ||||
|     state->scene->end(state); | ||||
|     free(state); | ||||
| } | ||||
|   | ||||
| @@ -10,14 +10,14 @@ | ||||
| #include <tgmath.h> | ||||
|  | ||||
|  | ||||
| static void update_timers(struct player *player) { | ||||
| static void update_timers(Player *player) { | ||||
|      tick_timer(&player->jump_air_timer); | ||||
|      tick_timer(&player->jump_coyote_timer); | ||||
|      tick_timer(&player->jump_buffer_timer); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void input_move(struct input_state *input, struct player *player) { | ||||
| static void input_move(InputState *input, Player *player) { | ||||
|     /* apply horizontal damping when the player stops moving */ | ||||
|     /* in other words, make it decelerate to a standstill */ | ||||
|     if (!input_is_action_pressed(input, "player_left") && | ||||
| @@ -44,7 +44,7 @@ static void input_move(struct input_state *input, struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void jump(struct player *player) { | ||||
| static void jump(Player *player) { | ||||
|     player->jump_coyote_timer = 0; | ||||
|     player->jump_buffer_timer = 0; | ||||
|     player->dy = player->jump_force_initial; | ||||
| @@ -53,7 +53,7 @@ static void jump(struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void input_jump(struct input_state *input, struct player *player) { | ||||
| static void input_jump(InputState *input, Player *player) { | ||||
|     player->current_gravity_multiplier = player->jump_default_multiplier; | ||||
|  | ||||
|     if (input_is_action_just_pressed(input, "player_jump")) { | ||||
| @@ -74,7 +74,7 @@ static void input_jump(struct input_state *input, struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void update_collider_x(struct player *player) { | ||||
| static void update_collider_x(Player *player) { | ||||
|     player->collider_x.w = player->rect.w; | ||||
|     player->collider_x.h = player->rect.h - 8; | ||||
|     player->collider_x.x = player->rect.x; | ||||
| @@ -82,7 +82,7 @@ static void update_collider_x(struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void update_collider_y(struct player *player) { | ||||
| static void update_collider_y(Player *player) { | ||||
|     player->collider_y.w = player->rect.w; | ||||
|     player->collider_y.h = player->rect.h; | ||||
|     player->collider_y.x = player->rect.x + ((player->rect.w - player->collider_y.w) / 2); | ||||
| @@ -90,7 +90,7 @@ static void update_collider_y(struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void apply_gravity(struct player *player, float gravity) { | ||||
| static void apply_gravity(Player *player, float gravity) { | ||||
|     player->dy -= gravity * player->current_gravity_multiplier; | ||||
|     player->dy = fmax(player->dy, -player->terminal_velocity); | ||||
|  | ||||
| @@ -106,7 +106,7 @@ static void apply_gravity(struct player *player, float gravity) { | ||||
|  | ||||
|  | ||||
| /* returns whether or not a correction was applied */ | ||||
| static bool corner_correct(struct player *player, t_frect collision) { | ||||
| static bool corner_correct(Player *player, Rect collision) { | ||||
|     /* | ||||
|      * somewhat of a hack here. we only want to do corner correction | ||||
|      * if the corner in question really is the corner of a "platform," | ||||
| @@ -145,16 +145,16 @@ static bool corner_correct(struct player *player, t_frect collision) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void calc_collisions_x(struct player *player) { | ||||
|     t_frect collision; | ||||
| static void calc_collisions_x(Player *player) { | ||||
|     Rect collision; | ||||
|     bool is_colliding = world_find_intersect_frect(player->world, player->collider_x, &collision); | ||||
|     if (!is_colliding) return; | ||||
|  | ||||
|     float player_center_x = player->collider_x.x + (player->collider_x.w / 2); | ||||
|     float collision_center_x = collision.x + (collision.w / 2);  | ||||
|  | ||||
|     enum collision_direction { COLLISION_LEFT = -1, COLLISION_RIGHT = 1 }; | ||||
|     enum collision_direction dir_x = | ||||
|     typedef enum CollisionDirection { COLLISION_LEFT = -1, COLLISION_RIGHT = 1 } CollisionDirection; | ||||
|     CollisionDirection dir_x = | ||||
|         player_center_x > collision_center_x ? COLLISION_LEFT : COLLISION_RIGHT; | ||||
|  | ||||
|     player->rect.x -= collision.w * (float)dir_x; | ||||
| @@ -162,16 +162,16 @@ static void calc_collisions_x(struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void calc_collisions_y(struct player *player) { | ||||
|     t_frect collision; | ||||
| static void calc_collisions_y(Player *player) { | ||||
|     Rect collision; | ||||
|     bool is_colliding = world_find_intersect_frect(player->world, player->collider_y, &collision); | ||||
|     if (!is_colliding) return; | ||||
|  | ||||
|     float player_center_y = player->collider_y.y + (player->collider_y.h / 2); | ||||
|     float collision_center_y = collision.y + (collision.h / 2);  | ||||
|  | ||||
|     enum collision_direction { COLLISION_ABOVE = -1, COLLISION_BELOW = 1 }; | ||||
|     enum collision_direction dir_y = | ||||
|     typedef enum CollisionDirection { COLLISION_ABOVE = -1, COLLISION_BELOW = 1 } CollisionDirection; | ||||
|     CollisionDirection dir_y = | ||||
|         player_center_y > collision_center_y ? COLLISION_ABOVE : COLLISION_BELOW; | ||||
|  | ||||
|     /* before the resolution */ | ||||
| @@ -202,16 +202,16 @@ static void calc_collisions_y(struct player *player) { | ||||
| } | ||||
|  | ||||
|  | ||||
| struct player *player_create(struct world *world) { | ||||
|     struct player *player = cmalloc(sizeof *player); | ||||
| Player *player_create(World *world) { | ||||
|     Player *player = cmalloc(sizeof *player); | ||||
|  | ||||
|     *player = (struct player) { | ||||
|     *player = (Player) { | ||||
|         .world = world, | ||||
|  | ||||
|         .sprite_w = 48, | ||||
|         .sprite_h = 48, | ||||
|  | ||||
|         .rect = (t_frect) { | ||||
|         .rect = (Rect) { | ||||
|             .x = 92, | ||||
|             .y = 200, | ||||
|             .w = 16, | ||||
| @@ -243,22 +243,22 @@ struct player *player_create(struct world *world) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void drawdef(struct player *player) { | ||||
| static void drawdef(Player *player) { | ||||
|     m_sprite("/assets/player/baron-walk.png", | ||||
|             (t_frect) { | ||||
|             (Rect) { | ||||
|                 .x = player->rect.x + ((player->rect.w - player->sprite_w) / 2), | ||||
|                 .y = player->rect.y - 8, | ||||
|                 .w = player->sprite_w, | ||||
|                 .h = player->sprite_h, | ||||
|             }); | ||||
|  | ||||
|     push_circle((t_fvec2) { 256, 128 }, | ||||
|     push_circle((Vec2) { 256, 128 }, | ||||
|                 24, | ||||
|                 (t_color) { 255, 0, 0, 255 }); | ||||
|                 (Color) { 255, 0, 0, 255 }); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void drawdef_debug(struct player *player) { | ||||
| static void drawdef_debug(Player *player) { | ||||
|     if (!ctx.debug) | ||||
|         return; | ||||
|  | ||||
| @@ -269,19 +269,19 @@ static void drawdef_debug(struct player *player) { | ||||
|     /* }; */ | ||||
|  | ||||
|     push_rectangle(player->collider_x, | ||||
|                    (t_color){ 0, 0, 255, 128 }); | ||||
|                    (Color){ 0, 0, 255, 128 }); | ||||
|  | ||||
|     push_rectangle(player->collider_y, | ||||
|                    (t_color){ 0, 0, 255, 128 }); | ||||
|                    (Color){ 0, 0, 255, 128 }); | ||||
| } | ||||
|  | ||||
|  | ||||
| void player_destroy(struct player *player) { | ||||
| void player_destroy(Player *player) { | ||||
|     free(player); | ||||
| } | ||||
|  | ||||
|  | ||||
| void player_calc(struct player *player) { | ||||
| void player_calc(Player *player) { | ||||
|     update_timers(player); | ||||
|  | ||||
|     input_move(&ctx.input, player); | ||||
|   | ||||
| @@ -4,32 +4,32 @@ | ||||
| #include "twn_game_api.h" | ||||
|  | ||||
|  | ||||
| struct world; | ||||
| typedef struct World World; | ||||
|  | ||||
|  | ||||
| enum player_action { | ||||
| typedef enum PlayerAction { | ||||
|     PLAYER_ACTION_GROUND, | ||||
|     PLAYER_ACTION_FALL, | ||||
|     PLAYER_ACTION_JUMP, | ||||
| }; | ||||
| } PlayerAction; | ||||
|  | ||||
|  | ||||
| struct player { | ||||
|     struct world *world; | ||||
| typedef struct Player { | ||||
|     World *world; | ||||
|  | ||||
|     /* visual */ | ||||
|     float sprite_w; | ||||
|     float sprite_h; | ||||
|  | ||||
|     /* body */ | ||||
|     t_frect rect; | ||||
|     Rect rect; | ||||
|  | ||||
|     /* state */ | ||||
|     enum player_action action; | ||||
|     PlayerAction action; | ||||
|  | ||||
|     /* physics */ | ||||
|     t_frect collider_x; | ||||
|     t_frect collider_y; | ||||
|     Rect collider_x; | ||||
|     Rect collider_y; | ||||
|     int collider_thickness; | ||||
|     float dx; | ||||
|     float dy; | ||||
| @@ -54,12 +54,12 @@ struct player { | ||||
|     float jump_boosted_multiplier; | ||||
|  | ||||
|     float jump_corner_correction_offset; /* from center */ | ||||
| }; | ||||
| } Player; | ||||
|  | ||||
|  | ||||
| struct player *player_create(struct world *world); | ||||
| void player_destroy(struct player *player); | ||||
| void player_calc(struct player *player); | ||||
| Player *player_create(World *world); | ||||
| void player_destroy(Player *player); | ||||
| void player_calc(Player *player); | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,8 +8,8 @@ | ||||
| #include <stb_perlin.h> | ||||
|  | ||||
|  | ||||
| static void ingame_tick(struct state *state) { | ||||
|     struct scene_ingame *scn = (struct scene_ingame *)state->scene; | ||||
| static void ingame_tick(State *state) { | ||||
|     SceneIngame *scn = (SceneIngame *)state->scene; | ||||
|  | ||||
|     world_drawdef(scn->world); | ||||
|     player_calc(scn->player); | ||||
| @@ -23,26 +23,26 @@ static void ingame_tick(struct state *state) { | ||||
|         const float yaw_rad = scn->yaw * (float)DEG2RAD; | ||||
|         const float pitch_rad = scn->pitch * (float)DEG2RAD; | ||||
|  | ||||
|         scn->cam.target = m_vec_norm(((t_fvec3){ | ||||
|         scn->cam.target = m_vec_norm(((Vec3){ | ||||
|             cosf(yaw_rad) * cosf(pitch_rad), | ||||
|             sinf(pitch_rad), | ||||
|             sinf(yaw_rad) * cosf(pitch_rad) | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     const t_fvec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up)); | ||||
|     const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up)); | ||||
|     const float speed = 0.04f; /* TODO: put this in a better place */ | ||||
|     if (input_is_action_pressed(&ctx.input, "player_left")) | ||||
|         scn->cam.pos = fvec3_sub(scn->cam.pos, m_vec_scale(right, speed)); | ||||
|         scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed)); | ||||
|  | ||||
|     if (input_is_action_pressed(&ctx.input, "player_right")) | ||||
|         scn->cam.pos = fvec3_add(scn->cam.pos, m_vec_scale(right, speed)); | ||||
|         scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed)); | ||||
|  | ||||
|     if (input_is_action_pressed(&ctx.input, "player_forward")) | ||||
|         scn->cam.pos = fvec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed)); | ||||
|         scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed)); | ||||
|  | ||||
|     if (input_is_action_pressed(&ctx.input, "player_backward")) | ||||
|         scn->cam.pos = fvec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed)); | ||||
|         scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed)); | ||||
|  | ||||
|     if (input_is_action_pressed(&ctx.input, "player_jump")) | ||||
|         scn->cam.pos.y += speed; | ||||
| @@ -56,27 +56,27 @@ static void ingame_tick(struct state *state) { | ||||
|     } | ||||
|  | ||||
|     m_sprite(m_set(path,  "/assets/9slice.png"), | ||||
|              m_set(rect,  ((t_frect){ 16, 16, 128, 128 })), | ||||
|              m_opt(texture_region, ((t_frect){ 0, 0, (float)(ctx.tick_count % 48), (float)(ctx.tick_count % 48) }))); | ||||
|              m_set(rect,  ((Rect){ 16, 16, 128, 128 })), | ||||
|              m_opt(texture_region, ((Rect){ 0, 0, (float)(ctx.tick_count % 48), (float)(ctx.tick_count % 48) }))); | ||||
|  | ||||
|     m_sprite(m_set(path,  "/assets/light.png"), | ||||
|              m_set(rect,  ((t_frect){ 48, 64, 64, 64 })), | ||||
|              m_opt(color, ((t_color){ 255, 0, 0, 255 }))); | ||||
|              m_set(rect,  ((Rect){ 48, 64, 64, 64 })), | ||||
|              m_opt(color, ((Color){ 255, 0, 0, 255 }))); | ||||
|  | ||||
|     m_sprite(m_set(path,  "/assets/light.png"), | ||||
|              m_set(rect,  ((t_frect){ 64, 64, 64, 64 })), | ||||
|              m_opt(color, ((t_color){ 0, 255, 0, 255 }))); | ||||
|              m_set(rect,  ((Rect){ 64, 64, 64, 64 })), | ||||
|              m_opt(color, ((Color){ 0, 255, 0, 255 }))); | ||||
|  | ||||
|     m_sprite(m_set(path,  "/assets/light.png"), | ||||
|              m_set(rect,  ((t_frect){ 80, 64, 64, 64 })), | ||||
|              m_opt(color, ((t_color){ 0, 0, 255, 255 }))); | ||||
|              m_set(rect,  ((Rect){ 80, 64, 64, 64 })), | ||||
|              m_opt(color, ((Color){ 0, 0, 255, 255 }))); | ||||
|  | ||||
|     m_sprite(m_set(path,     "/assets/player/baron-walk.png"), | ||||
|              m_set(rect,     ((t_frect){ 32, 32, 64, 64 })), | ||||
|              m_set(rect,     ((Rect){ 32, 32, 64, 64 })), | ||||
|              m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 )); | ||||
|  | ||||
|     m_sprite(m_set(path,     "/assets/player/baron-walk.png"), | ||||
|              m_set(rect,     ((t_frect){ 128, 32, 128, 64 })), | ||||
|              m_set(rect,     ((Rect){ 128, 32, 128, 64 })), | ||||
|              m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 )); | ||||
|  | ||||
|     set_camera(&scn->cam); | ||||
| @@ -91,51 +91,51 @@ static void ingame_tick(struct state *state) { | ||||
|             float d3 = stb_perlin_noise3((float)x       * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 20 - 6; | ||||
|  | ||||
|             unfurl_triangle("/assets/grass.gif", | ||||
|                             (t_fvec3){ (float)x,     d0, (float)y }, | ||||
|                             (t_fvec3){ (float)x + 1, d1, (float)y }, | ||||
|                             (t_fvec3){ (float)x,     d3, (float)y - 1 }, | ||||
|                             (t_shvec2){ 1024, 768 }, | ||||
|                             (t_shvec2){ 1024, 0 }, | ||||
|                             (t_shvec2){ 0, 768 }); | ||||
|                             (Vec3){ (float)x,     d0, (float)y }, | ||||
|                             (Vec3){ (float)x + 1, d1, (float)y }, | ||||
|                             (Vec3){ (float)x,     d3, (float)y - 1 }, | ||||
|                             (Vec2sh){ 1024, 768 }, | ||||
|                             (Vec2sh){ 1024, 0 }, | ||||
|                             (Vec2sh){ 0, 768 }); | ||||
|  | ||||
|             unfurl_triangle("/assets/grass.gif", | ||||
|                             (t_fvec3){ (float)x + 1, d1, (float)y }, | ||||
|                             (t_fvec3){ (float)x + 1, d2, (float)y - 1 }, | ||||
|                             (t_fvec3){ (float)x,     d3, (float)y - 1 }, | ||||
|                             (t_shvec2){ 1024, 0 }, | ||||
|                             (t_shvec2){ 0, 0 }, | ||||
|                             (t_shvec2){ 0, 768 }); | ||||
|                             (Vec3){ (float)x + 1, d1, (float)y }, | ||||
|                             (Vec3){ (float)x + 1, d2, (float)y - 1 }, | ||||
|                             (Vec3){ (float)x,     d3, (float)y - 1 }, | ||||
|                             (Vec2sh){ 1024, 0 }, | ||||
|                             (Vec2sh){ 0, 0 }, | ||||
|                             (Vec2sh){ 0, 768 }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| static void ingame_end(struct state *state) { | ||||
|     struct scene_ingame *scn = (struct scene_ingame *)state->scene; | ||||
| static void ingame_end(State *state) { | ||||
|     SceneIngame *scn = (SceneIngame *)state->scene; | ||||
|     player_destroy(scn->player); | ||||
|     world_destroy(scn->world); | ||||
|     free(state->scene); | ||||
| } | ||||
|  | ||||
|  | ||||
| struct scene *ingame_scene(struct state *state) { | ||||
| Scene *ingame_scene(State *state) { | ||||
|     (void)state; | ||||
|  | ||||
|     struct scene_ingame *new_scene = ccalloc(1, sizeof *new_scene); | ||||
|     SceneIngame *new_scene = ccalloc(1, sizeof *new_scene); | ||||
|     new_scene->base.tick = ingame_tick; | ||||
|     new_scene->base.end = ingame_end; | ||||
|  | ||||
|     new_scene->world = world_create(); | ||||
|     new_scene->player = player_create(new_scene->world); | ||||
|  | ||||
|     new_scene->cam = (t_camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 }; | ||||
|     new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 }; | ||||
|  | ||||
|     play_audio_ex("music/mod65.xm", "soundtrack", (t_play_audio_args){ | ||||
|     audio_play_ex("music/mod65.xm", "soundtrack", (PlayAudioArgs){ | ||||
|         .repeat = true, | ||||
|         .volume = 1.0f | ||||
|     }); | ||||
|  | ||||
|     input_set_mouse_captured(&ctx.input, true); | ||||
|  | ||||
|     return (struct scene *)new_scene; | ||||
|     return (Scene *)new_scene; | ||||
| } | ||||
|   | ||||
| @@ -9,22 +9,22 @@ | ||||
| #include "../world.h" | ||||
|  | ||||
|  | ||||
| struct scene_ingame { | ||||
|     struct scene base; | ||||
| typedef struct SceneIngame { | ||||
|     Scene base; | ||||
|  | ||||
|     struct world *world; | ||||
|     struct player *player; | ||||
|     World *world; | ||||
|     Player *player; | ||||
|  | ||||
|     t_camera cam; | ||||
|     Camera cam; | ||||
|  | ||||
|     /* TODO: put this in a better place */ | ||||
|     float yaw; | ||||
|     float pitch; | ||||
|     float roll; | ||||
| }; | ||||
| } SceneIngame; | ||||
|  | ||||
|  | ||||
| struct scene *ingame_scene(struct state *state); | ||||
| Scene *ingame_scene(State *state); | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "../state.h" | ||||
|  | ||||
|  | ||||
| void switch_to(struct state *state, struct scene *(*scene_func)(struct state *)) { | ||||
| void switch_to(State *state, Scene *(*scene_func)(State *)) { | ||||
|     state->next_scene = scene_func(state); | ||||
|     state->is_scene_switching = true; | ||||
| } | ||||
|   | ||||
| @@ -2,15 +2,15 @@ | ||||
| #define SCENE_H | ||||
|  | ||||
|  | ||||
| struct state; | ||||
| struct scene { | ||||
| typedef struct State State; | ||||
| typedef struct Scene { | ||||
|     char *id; | ||||
|     void (*tick)(struct state *); | ||||
|     void (*end)(struct state *); | ||||
| }; | ||||
|     void (*tick)(State *); | ||||
|     void (*end)(State *); | ||||
| } Scene; | ||||
|  | ||||
|  | ||||
| void switch_to(struct state *state, struct scene *(*scene_func)(struct state *)); | ||||
| void switch_to(State *state, Scene *(*scene_func)(State *)); | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,8 +8,8 @@ | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
| static void title_tick(struct state *state) { | ||||
|     struct scene_title *scn = (struct scene_title *)state->scene; | ||||
| static void title_tick(State *state) { | ||||
|     SceneTitle *scn = (SceneTitle *)state->scene; | ||||
|     (void)scn; | ||||
|  | ||||
|     if (input_is_action_just_pressed(&state->ctx->input, "ui_accept")) { | ||||
| @@ -17,7 +17,7 @@ static void title_tick(struct state *state) { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     m_sprite("/assets/title.png", ((t_frect) { | ||||
|     m_sprite("/assets/title.png", ((Rect) { | ||||
|             ((float)RENDER_BASE_WIDTH / 2) - ((float)320 / 2), 64, 320, 128 })); | ||||
|      | ||||
|      | ||||
| @@ -31,42 +31,42 @@ static void title_tick(struct state *state) { | ||||
|     int text_w = get_text_width(text_str, text_h, font); | ||||
|  | ||||
|     push_rectangle( | ||||
|         (t_frect) { | ||||
|         (Rect) { | ||||
|             .x = 0, | ||||
|             .y = 0, | ||||
|             .w = (float)text_w, | ||||
|             .h = (float)text_h, | ||||
|         }, | ||||
|         (t_color) { 0, 0, 0, 255 } | ||||
|         (Color) { 0, 0, 0, 255 } | ||||
|     ); | ||||
|     push_text( | ||||
|         text_str, | ||||
|         (t_fvec2){ 0, 0 }, | ||||
|         (Vec2){ 0, 0 }, | ||||
|         text_h, | ||||
|         (t_color) { 255, 255, 255, 255 }, | ||||
|         (Color) { 255, 255, 255, 255 }, | ||||
|         font | ||||
|     ); | ||||
|     free(text_str); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void title_end(struct state *state) { | ||||
|     struct scene_title *scn = (struct scene_title *)state->scene; | ||||
| static void title_end(State *state) { | ||||
|     SceneTitle *scn = (SceneTitle *)state->scene; | ||||
|     player_destroy(scn->player); | ||||
|     world_destroy(scn->world); | ||||
|     free(state->scene); | ||||
| } | ||||
|  | ||||
|  | ||||
| struct scene *title_scene(struct state *state) { | ||||
| Scene *title_scene(State *state) { | ||||
|     (void)state; | ||||
|  | ||||
|     struct scene_title *new_scene = ccalloc(1, sizeof *new_scene); | ||||
|     SceneTitle *new_scene = ccalloc(1, sizeof *new_scene); | ||||
|     new_scene->base.tick = title_tick; | ||||
|     new_scene->base.end = title_end; | ||||
|  | ||||
|     new_scene->world = world_create(); | ||||
|     new_scene->player = player_create(new_scene->world); | ||||
|  | ||||
|     return (struct scene *)new_scene; | ||||
|     return (Scene *)new_scene; | ||||
| } | ||||
|   | ||||
| @@ -7,15 +7,15 @@ | ||||
| #include "../world.h" | ||||
|  | ||||
|  | ||||
| struct scene_title { | ||||
|     struct scene base; | ||||
| typedef struct SceneTitle { | ||||
|     Scene base; | ||||
|  | ||||
|     struct world *world; | ||||
|     struct player *player; | ||||
| }; | ||||
|     World *world; | ||||
|     Player *player; | ||||
| } SceneTitle; | ||||
|  | ||||
|  | ||||
| struct scene *title_scene(struct state *state); | ||||
| Scene *title_scene(State *state); | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -6,12 +6,15 @@ | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| struct state { | ||||
|     t_ctx *ctx; | ||||
|     struct scene *scene; | ||||
|     struct scene *next_scene; | ||||
|  | ||||
| typedef struct Scene Scene; | ||||
|  | ||||
| typedef struct State { | ||||
|     Context *ctx; | ||||
|     Scene *scene; | ||||
|     Scene *next_scene; | ||||
|     bool is_scene_switching; | ||||
| }; | ||||
| } State; | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,11 +8,11 @@ | ||||
| #include <tgmath.h> | ||||
|  | ||||
|  | ||||
| static void update_tiles(struct world *world) { | ||||
| static void update_tiles(struct World *world) { | ||||
|     for (size_t row = 0; row < world->tilemap_height; ++row) { | ||||
|         for (size_t col = 0; col < world->tilemap_width; ++col) { | ||||
|             world->tiles[(row * world->tilemap_width) + col] = (struct tile) { | ||||
|                 .rect = (t_rect) { | ||||
|             world->tiles[(row * world->tilemap_width) + col] = (struct Tile) { | ||||
|                 .rect = (Recti) { | ||||
|                     .x = (int)col * world->tile_size, | ||||
|                     .y = (int)row * world->tile_size, | ||||
|                     .w = world->tile_size, | ||||
| @@ -25,30 +25,30 @@ static void update_tiles(struct world *world) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static t_vec2 to_grid_location(struct world *world, float x, float y) { | ||||
|     return (t_vec2) { | ||||
| static Vec2i to_grid_location(struct World *world, float x, float y) { | ||||
|     return (Vec2i) { | ||||
|         .x = (int)floor(x / (float)world->tile_size), | ||||
|         .y = (int)floor(y / (float)world->tile_size), | ||||
|     }; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void drawdef_debug(struct world *world) { | ||||
| static void drawdef_debug(struct World *world) { | ||||
|     if (!ctx.debug) return; | ||||
|  | ||||
|     for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) { | ||||
|         if (world->tiles[i].type == TILE_TYPE_VOID) continue; | ||||
|  | ||||
|         push_rectangle(to_frect(world->tiles[i].rect), | ||||
|                         (t_color) { 255, 0, 255, 128 }); | ||||
|                         (Color) { 255, 0, 255, 128 }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| struct world *world_create(void) { | ||||
|     struct world *world = cmalloc(sizeof *world); | ||||
| struct World *world_create(void) { | ||||
|     struct World *world = cmalloc(sizeof *world); | ||||
|  | ||||
|     *world = (struct world) { | ||||
|     *world = (struct World) { | ||||
|         .tiles = NULL, | ||||
|         .tile_size = 42, | ||||
|         .tilemap_width = 20, | ||||
| @@ -89,7 +89,7 @@ struct world *world_create(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void world_destroy(struct world *world) { | ||||
| void world_destroy(struct World *world) { | ||||
|     free(world->tiles); | ||||
|  | ||||
|     for (size_t i = 0; i < world->tilemap_height; ++i) { | ||||
| @@ -101,7 +101,7 @@ void world_destroy(struct world *world) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void world_drawdef(struct world *world) { | ||||
| void world_drawdef(struct World *world) { | ||||
|     for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) { | ||||
|         if (world->tiles[i].type == TILE_TYPE_VOID) | ||||
|             continue; | ||||
| @@ -113,7 +113,7 @@ void world_drawdef(struct world *world) { | ||||
| } | ||||
|  | ||||
|  | ||||
| bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *intersection) { | ||||
| bool world_find_intersect_frect(struct World *world, Rect rect, Rect *intersection) { | ||||
|     bool is_intersecting = false; | ||||
|  | ||||
|     const size_t tile_count = world->tilemap_height * world->tilemap_width; | ||||
| @@ -121,7 +121,7 @@ bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *inte | ||||
|         if (world->tiles[i].type == TILE_TYPE_VOID) | ||||
|             continue; | ||||
|  | ||||
|         t_frect tile_frect = { | ||||
|         Rect tile_frect = { | ||||
|             .x = (float)(world->tiles[i].rect.x), | ||||
|             .y = (float)(world->tiles[i].rect.y), | ||||
|             .w = (float)(world->tiles[i].rect.w), | ||||
| @@ -129,7 +129,7 @@ bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *inte | ||||
|         }; | ||||
|  | ||||
|         if (intersection == NULL) { | ||||
|             t_frect temp; | ||||
|             Rect temp; | ||||
|             is_intersecting = overlap_frect(&rect, &tile_frect, &temp); | ||||
|         } else { | ||||
|             is_intersecting = overlap_frect(&rect, &tile_frect, intersection); | ||||
| @@ -143,7 +143,7 @@ bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *inte | ||||
| } | ||||
|  | ||||
|  | ||||
| bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *intersection) { | ||||
| bool world_find_intersect_rect(struct World *world, Recti rect, Recti *intersection) { | ||||
|     bool is_intersecting = false; | ||||
|  | ||||
|     const size_t tile_count = world->tilemap_height * world->tilemap_width; | ||||
| @@ -151,10 +151,10 @@ bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *interse | ||||
|         if (world->tiles[i].type == TILE_TYPE_VOID) | ||||
|             continue; | ||||
|  | ||||
|         t_rect *tile_rect = &world->tiles[i].rect; | ||||
|         Recti *tile_rect = &world->tiles[i].rect; | ||||
|  | ||||
|         if (intersection == NULL) { | ||||
|             t_rect temp; | ||||
|             Recti temp; | ||||
|             is_intersecting = overlap_rect(&rect, tile_rect, &temp); | ||||
|         } else { | ||||
|             is_intersecting = overlap_rect(&rect, tile_rect, intersection); | ||||
| @@ -168,21 +168,21 @@ bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *interse | ||||
| } | ||||
|  | ||||
|  | ||||
| bool world_is_tile_at(struct world *world, float x, float y) { | ||||
|     t_vec2 position_in_grid = to_grid_location(world, x, y); | ||||
| bool world_is_tile_at(struct World *world, float x, float y) { | ||||
|     Vec2i position_in_grid = to_grid_location(world, x, y); | ||||
|     return world->tilemap[position_in_grid.y][position_in_grid.x] != TILE_TYPE_VOID; | ||||
| } | ||||
|  | ||||
|  | ||||
| void world_place_tile(struct world *world, float x, float y) { | ||||
|     t_vec2 position_in_grid = to_grid_location(world, x, y); | ||||
| void world_place_tile(struct World *world, float x, float y) { | ||||
|     Vec2i position_in_grid = to_grid_location(world, x, y); | ||||
|     world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_SOLID; | ||||
|     update_tiles(world); | ||||
| } | ||||
|  | ||||
|  | ||||
| void world_remove_tile(struct world *world, float x, float y) { | ||||
|     t_vec2 position_in_grid = to_grid_location(world, x, y); | ||||
| void world_remove_tile(struct World *world, float x, float y) { | ||||
|     Vec2i position_in_grid = to_grid_location(world, x, y); | ||||
|     world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_VOID; | ||||
|     update_tiles(world); | ||||
| } | ||||
|   | ||||
| @@ -1,42 +1,44 @@ | ||||
| #ifndef WORLD_H | ||||
| #define WORLD_H | ||||
|  | ||||
| #include "twn_game_api.h" | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
|  | ||||
| enum tile_type { | ||||
| typedef enum TileType { | ||||
|     TILE_TYPE_VOID, | ||||
|     TILE_TYPE_SOLID, | ||||
| }; | ||||
| } TileType; | ||||
|  | ||||
|  | ||||
| struct tile { | ||||
|     t_rect rect; | ||||
|     enum tile_type type; | ||||
| }; | ||||
| typedef struct Tile { | ||||
|     Recti rect; | ||||
|     TileType type; | ||||
| } Tile; | ||||
|  | ||||
|  | ||||
| struct world { | ||||
|     enum tile_type **tilemap; | ||||
|     struct tile *tiles; | ||||
| typedef struct World { | ||||
|     TileType **tilemap; | ||||
|     Tile *tiles; | ||||
|      | ||||
|     int tile_size; | ||||
|     unsigned int tilemap_width; | ||||
|     unsigned int tilemap_height; | ||||
|     size_t tile_nonvoid_count; | ||||
|     float gravity; | ||||
| }; | ||||
| } World; | ||||
|  | ||||
|  | ||||
| struct world *world_create(void); | ||||
| void world_destroy(struct world *world); | ||||
| void world_drawdef(struct world *world); | ||||
| bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *intersection); | ||||
| bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *intersection); | ||||
| bool world_is_tile_at(struct world *world, float x, float y); | ||||
| void world_place_tile(struct world *world, float x, float y); | ||||
| void world_remove_tile(struct world *world, float x, float y); | ||||
| World *world_create(void); | ||||
| void world_destroy(World *world); | ||||
| void world_drawdef(World *world); | ||||
| bool world_find_intersect_frect(World *world, Rect rect, Rect *intersection); | ||||
| bool world_find_intersect_rect(World *world, Recti rect, Recti *intersection); | ||||
| bool world_is_tile_at(World *world, float x, float y); | ||||
| void world_place_tile(World *world, float x, float y); | ||||
| void world_remove_tile(World *world, float x, float y); | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/9slice.png
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/9slice.png
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -6,7 +6,7 @@ | ||||
| #include <stdbool.h> | ||||
|  | ||||
|  | ||||
| typedef struct play_audio_args { | ||||
| typedef struct PlayAudioArgs { | ||||
|     /* default: false */ | ||||
|     bool repeat; | ||||
|     /* crossfade between already playing audio on a given channel, if any */ | ||||
| @@ -18,21 +18,21 @@ typedef struct play_audio_args { | ||||
|     /* range: -1.0 to 1.0f */ | ||||
|     /* default: 0.0f */ | ||||
|     float panning; | ||||
| } t_play_audio_args; | ||||
| } PlayAudioArgs; | ||||
|  | ||||
|  | ||||
| /* plays audio file at specified channel or anywhere if NULL is passed */ | ||||
| /* path must contain valid file extension to infer which file format it is */ | ||||
| /* supported formats: .ogg, .xm */ | ||||
| /* preserves args that are already specified on the channel */ | ||||
| TWN_API void play_audio(const char *path, const char *channel); | ||||
| TWN_API void audio_play(const char *path, const char *channel); | ||||
|  | ||||
| TWN_API void play_audio_ex(const char *path, const char *channel, t_play_audio_args args); | ||||
| TWN_API void audio_play_ex(const char *path, const char *channel, PlayAudioArgs args); | ||||
|  | ||||
| /* could be used for modifying args */ | ||||
| /* warn: is only valid if no other calls to audio are made */ | ||||
| TWN_API t_play_audio_args *get_audio_args(const char *channel); | ||||
| TWN_API PlayAudioArgs *audio_get_args(const char *channel); | ||||
|  | ||||
| TWN_API t_play_audio_args get_default_audio_args(void); | ||||
| TWN_API PlayAudioArgs audio_get_default_args(void); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,15 +8,15 @@ | ||||
| /*       for example, perspective matrix only needs recaluclation on FOV change */ | ||||
|  | ||||
| /* first person camera class */ | ||||
| typedef struct camera { | ||||
|     t_fvec3 pos;        /* eye position */ | ||||
|     t_fvec3 target;     /* normalized target vector */ | ||||
|     t_fvec3 up;         /* normalized up vector */ | ||||
| typedef struct Camera { | ||||
|     Vec3 pos;        /* eye position */ | ||||
|     Vec3 target;     /* normalized target vector */ | ||||
|     Vec3 up;         /* normalized up vector */ | ||||
|     float fov;          /* field of view, in radians */ | ||||
| } t_camera; | ||||
| } Camera; | ||||
|  | ||||
| TWN_API t_matrix4 camera_look_at(const t_camera *camera); | ||||
| TWN_API Matrix4 camera_look_at(const Camera *camera); | ||||
|  | ||||
| TWN_API t_matrix4 camera_perspective(const t_camera *const camera); | ||||
| TWN_API Matrix4 camera_perspective(const Camera *const camera); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,8 +8,8 @@ | ||||
| #include <stdint.h> | ||||
|  | ||||
|  | ||||
| typedef struct context { | ||||
|     struct input_state input; | ||||
| typedef struct Context { | ||||
|     struct InputState input; | ||||
|  | ||||
|     int64_t delta_time; /* preserves real time frame delta with no manipilation */ | ||||
|     uint64_t tick_count; | ||||
| @@ -31,10 +31,10 @@ typedef struct context { | ||||
|     bool is_running; | ||||
|     bool window_size_has_changed; | ||||
|     bool initialization_needed; | ||||
| } t_ctx; | ||||
| } Context; | ||||
|  | ||||
| #ifndef TWN_ENGINE_CONTEXT_C_H | ||||
| TWN_API extern t_ctx ctx; | ||||
| TWN_API extern Context ctx; | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -12,16 +12,16 @@ | ||||
| #include <stdbool.h> | ||||
|  | ||||
|  | ||||
| enum button_source { | ||||
| typedef enum ButtonSource { | ||||
|     BUTTON_SOURCE_NOT_SET, | ||||
|     BUTTON_SOURCE_KEYBOARD_PHYSICAL, | ||||
|     BUTTON_SOURCE_KEYBOARD_CHARACTER, | ||||
|     BUTTON_SOURCE_GAMEPAD, | ||||
|     BUTTON_SOURCE_MOUSE, | ||||
| }; | ||||
| } ButtonSource; | ||||
|  | ||||
|  | ||||
| union button_code { | ||||
| union ButtonCode { | ||||
|     SDL_Scancode scancode; | ||||
|     SDL_Keycode keycode; | ||||
|     SDL_GameControllerButton gamepad_button; | ||||
| @@ -31,71 +31,71 @@ union button_code { | ||||
|  | ||||
| /* an input to which an action is bound */ | ||||
| /* it is not limited to literal buttons */ | ||||
| struct button { | ||||
|     enum button_source source; | ||||
|     union button_code code; | ||||
| }; | ||||
| typedef struct Button { | ||||
|     enum ButtonSource source; | ||||
|     union ButtonCode code; | ||||
| } Button; | ||||
|  | ||||
|  | ||||
| /* represents the collective state of a group of buttons */ | ||||
| /* that is, changes in the states of any of the bound buttons will affect it */ | ||||
| struct action { | ||||
| typedef struct Action { | ||||
|     size_t num_bindings; | ||||
|  | ||||
|     /* if you bind more than NUM_KEYBIND_SLOTS (set in config.h) */ | ||||
|     /* it forgets the first button to add the new one at the end */ | ||||
|     struct button bindings[NUM_KEYBIND_SLOTS]; | ||||
|     /* it forgets the first Button to add the new one at the end */ | ||||
|     Button bindings[NUM_KEYBIND_SLOTS]; | ||||
|  | ||||
|     t_fvec2 position;        /* set if applicable, e.g. mouse click */ | ||||
|     Vec2 position;        /* set if applicable, e.g. mouse click */ | ||||
|     bool is_pressed; | ||||
|     bool just_changed; | ||||
| }; | ||||
| } Action; | ||||
|  | ||||
|  | ||||
| struct action_hash_item { | ||||
| typedef struct ActionHashItem { | ||||
|     char *key; | ||||
|     struct action value; | ||||
| }; | ||||
|     Action value; | ||||
| } ActionHashItem; | ||||
|  | ||||
|  | ||||
| struct input_state { | ||||
|     struct action_hash_item *action_hash; | ||||
| typedef struct InputState { | ||||
|     ActionHashItem *action_hash; | ||||
|     const uint8_t *keyboard_state; /* array of booleans indexed by scancode */ | ||||
|     uint32_t mouse_state;          /* SDL mouse button bitmask */ | ||||
|     t_vec2 mouse_window_position; | ||||
|     t_vec2 mouse_relative_position; | ||||
|     enum button_source last_active_source; | ||||
|     Vec2i mouse_window_position; | ||||
|     Vec2i mouse_relative_position; | ||||
|     ButtonSource last_active_source; | ||||
|     bool is_anything_just_pressed; | ||||
| }; | ||||
| } InputState; | ||||
|  | ||||
|  | ||||
| TWN_API void input_state_init(struct input_state *input); | ||||
| TWN_API void input_state_deinit(struct input_state *input); | ||||
| TWN_API void input_state_update(struct input_state *input); | ||||
| TWN_API void input_state_init(InputState *input); | ||||
| TWN_API void input_state_deinit(InputState *input); | ||||
| TWN_API void input_state_update(InputState *input); | ||||
|  | ||||
| TWN_API void input_bind_action_scancode(struct input_state *input, | ||||
| TWN_API void input_bind_action_scancode(InputState *input, | ||||
|                                 char *action_name, | ||||
|                                 SDL_Scancode scancode); | ||||
| TWN_API void input_unbind_action_scancode(struct input_state *input, | ||||
| TWN_API void input_unbind_action_scancode(InputState *input, | ||||
|                                   char *action_name, | ||||
|                                   SDL_Scancode scancode); | ||||
| TWN_API void input_bind_action_mouse(struct input_state *input, | ||||
| TWN_API void input_bind_action_mouse(InputState *input, | ||||
|                              char *action_name, | ||||
|                              uint8_t mouse_button); | ||||
| TWN_API void input_unbind_action_mouse(struct input_state *input, | ||||
| TWN_API void input_unbind_action_mouse(InputState *input, | ||||
|                                char *action_name, | ||||
|                                uint8_t mouse_button); | ||||
|  | ||||
| TWN_API void input_add_action(struct input_state *input, char *action_name); | ||||
| TWN_API void input_delete_action(struct input_state *input, char *action_name); | ||||
| TWN_API void input_add_action(InputState *input, char *action_name); | ||||
| TWN_API void input_delete_action(InputState *input, char *action_name); | ||||
|  | ||||
| TWN_API bool input_is_action_pressed(struct input_state *input, char *action_name); | ||||
| TWN_API bool input_is_action_just_pressed(struct input_state *input, char *action_name); | ||||
| TWN_API bool input_is_action_just_released(struct input_state *input, char *action_name); | ||||
| TWN_API bool input_is_action_pressed(InputState *input, char *action_name); | ||||
| TWN_API bool input_is_action_just_pressed(InputState *input, char *action_name); | ||||
| TWN_API bool input_is_action_just_released(InputState *input, char *action_name); | ||||
|  | ||||
| TWN_API t_fvec2 input_get_action_position(struct input_state *input, char *action_name); | ||||
| TWN_API Vec2 input_get_action_position(InputState *input, char *action_name); | ||||
|  | ||||
| TWN_API void input_set_mouse_captured(struct input_state *input, bool value); | ||||
| TWN_API bool input_is_mouse_captured(struct input_state *input); | ||||
| TWN_API void input_set_mouse_captured(InputState *input, bool value); | ||||
| TWN_API bool input_is_mouse_captured(InputState *input); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -10,67 +10,67 @@ | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| typedef struct push_sprite_args { | ||||
| typedef struct PushSpriteArgs { | ||||
|     char *path; | ||||
|     t_frect rect; | ||||
|     Rect rect; | ||||
|  | ||||
|     m_option_list( | ||||
|         t_frect, texture_region, | ||||
|         t_color, color, | ||||
|         Rect, texture_region, | ||||
|         Color, color, | ||||
|         float,   rotation, | ||||
|         bool,    flip_x, | ||||
|         bool,    flip_y, | ||||
|         bool,    stretch ) | ||||
| } t_push_sprite_args; | ||||
| } PushSpriteArgs; | ||||
|  | ||||
| /* pushes a sprite onto the sprite render queue */ | ||||
| /* this is a simplified version of push_sprite_ex for the most common case. */ | ||||
| /* it assumes you want no color modulation, no rotation, no flip */ | ||||
| TWN_API void push_sprite(t_push_sprite_args args); | ||||
| #define m_sprite(...) (push_sprite((t_push_sprite_args){__VA_ARGS__})) | ||||
| TWN_API void push_sprite(PushSpriteArgs args); | ||||
| #define m_sprite(...) (push_sprite((PushSpriteArgs){__VA_ARGS__})) | ||||
|  | ||||
| /* pushes a filled rectangle onto the rectangle render queue */ | ||||
| TWN_API void push_rectangle(t_frect rect, t_color color); | ||||
| TWN_API void push_rectangle(Rect rect, Color color); | ||||
|  | ||||
| /* pushes a filled circle onto the circle render queue */ | ||||
| TWN_API void push_circle(t_fvec2 position, float radius, t_color color); | ||||
| TWN_API void push_circle(Vec2 position, float radius, Color color); | ||||
|  | ||||
| TWN_API void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path); | ||||
| TWN_API void push_text(char *string, Vec2 position, int height_px, Color color, const char *font_path); | ||||
| TWN_API int get_text_width(char *string, int height_px, const char *font_path); | ||||
|  | ||||
| /* pushes a textured 3d triangle onto the render queue */ | ||||
| /* vertices are in absolute coordinates, relative to world origin */ | ||||
| /* texture coordinates are in pixels */ | ||||
| TWN_API void unfurl_triangle(const char *path, | ||||
|                             t_fvec3 v0, | ||||
|                             t_fvec3 v1, | ||||
|                             t_fvec3 v2, | ||||
|                             t_shvec2 uv0, | ||||
|                             t_shvec2 uv1, | ||||
|                             t_shvec2 uv2); | ||||
|                             Vec3 v0, | ||||
|                             Vec3 v1, | ||||
|                             Vec3 v2, | ||||
|                             Vec2sh uv0, | ||||
|                             Vec2sh uv1, | ||||
|                             Vec2sh uv2); | ||||
|  | ||||
| // TODO: decide whether it's needed to begin with? | ||||
| //       intended usage for it is baked lighting, i would think. | ||||
| /* pushes a colored textured 3d triangle onto the render queue */ | ||||
| // void unfurl_colored_triangle(const char *path, | ||||
| //                              t_fvec3 v0, | ||||
| //                              t_fvec3 v1, | ||||
| //                              t_fvec3 v2, | ||||
| //                              t_shvec2 uv0, | ||||
| //                              t_shvec2 uv1, | ||||
| //                              t_shvec2 uv2, | ||||
| //                              t_color c0, | ||||
| //                              t_color c1, | ||||
| //                              t_color c2); | ||||
| //                              Vec3 v0, | ||||
| //                              Vec3 v1, | ||||
| //                              Vec3 v2, | ||||
| //                              Vec2sh uv0, | ||||
| //                              Vec2sh uv1, | ||||
| //                              Vec2sh uv2, | ||||
| //                              Color c0, | ||||
| //                              Color c1, | ||||
| //                              Color c2); | ||||
|  | ||||
| // TODO: | ||||
| // http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2 | ||||
| // void unfurl_billboard(const char *path, | ||||
| //                       t_fvec3 position, | ||||
| //                       t_fvec2 scaling, | ||||
| //                       t_frect uvs); | ||||
| //                       Vec2 position, | ||||
| //                       Vec2 scaling, | ||||
| //                       Rect uvs); | ||||
|  | ||||
| /* pushes a camera state to be used for all future unfurl_* commands */ | ||||
| TWN_API void set_camera(const t_camera *camera); | ||||
| TWN_API void set_camera(const Camera *camera); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -2,10 +2,10 @@ | ||||
| #define TWN_TEXTURES_MODES_H | ||||
|  | ||||
| /* alpha channel information */ | ||||
| enum texture_mode { | ||||
| typedef enum TextureMode { | ||||
|     TEXTURE_MODE_OPAQUE,        /* all pixels are solid */ | ||||
|     TEXTURE_MODE_SEETHROUGH,  	/* some pixels are alpha zero */ | ||||
|     TEXTURE_MODE_GHOSTLY,       /* arbitrary alpha values */ | ||||
| }; | ||||
| } TextureMode; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -80,54 +80,54 @@ TWN_API TWN_API bool strends(const char *str, const char *suffix); | ||||
| /*                      */ | ||||
|  | ||||
| /* 32-bit color data */ | ||||
| typedef struct color { | ||||
| typedef struct Color { | ||||
| _Alignas(4) | ||||
|     uint8_t r; | ||||
|     uint8_t g; | ||||
|     uint8_t b; | ||||
|     uint8_t a; | ||||
| } t_color; | ||||
| } Color; | ||||
|  | ||||
|  | ||||
| /* a rectangle with the origin at the upper left (integer) */ | ||||
| typedef struct rect { | ||||
| typedef struct Recti { | ||||
| _Alignas(16) | ||||
|     int32_t x; | ||||
|     int32_t y; | ||||
|     int32_t w; | ||||
|     int32_t h; | ||||
| } t_rect; | ||||
| } Recti; | ||||
|  | ||||
|  | ||||
| /* a rectangle with the origin at the upper left (floating point) */ | ||||
| typedef struct frect { | ||||
| typedef struct Rect { | ||||
| _Alignas(16) | ||||
|     float x; | ||||
|     float y; | ||||
|     float w; | ||||
|     float h; | ||||
| } t_frect; | ||||
| } Rect; | ||||
|  | ||||
|  | ||||
| /* calculates the overlap of two rectangles and places it in result. */ | ||||
| /* result may be NULL. if this is the case, it will simply be ignored. */ | ||||
| /* returns true if the rectangles are indeed intersecting. */ | ||||
| TWN_API bool overlap_rect(const t_rect *a, const t_rect *b, t_rect *result); | ||||
| TWN_API bool overlap_frect(const t_frect *a, const t_frect *b, t_frect *result); | ||||
| TWN_API bool overlap_rect(const Recti *a, const Recti *b, Recti *result); | ||||
| TWN_API bool overlap_frect(const Rect *a, const Rect *b, Rect *result); | ||||
|  | ||||
| /* returns true if two rectangles are intersecting */ | ||||
| TWN_API bool intersect_rect(const t_rect *a, const t_rect *b); | ||||
| TWN_API bool intersect_frect(const t_frect *a, const t_frect *b); | ||||
| TWN_API bool intersect_rect(const Recti *a, const Recti *b); | ||||
| TWN_API bool intersect_frect(const Rect *a, const Rect *b); | ||||
|  | ||||
| /* TODO: generics and specials (see m_to_fvec2() for an example)*/ | ||||
| TWN_API t_frect to_frect(t_rect rect); | ||||
| /* TODO: generics and specials (see m_vec2_from() for an example)*/ | ||||
| TWN_API Rect to_frect(Recti rect); | ||||
|  | ||||
| TWN_API t_fvec2 frect_center(t_frect rect); | ||||
| TWN_API Vec2 frect_center(Rect rect); | ||||
|  | ||||
|  | ||||
| typedef struct matrix4 { | ||||
|     t_fvec4 row[4]; | ||||
| } t_matrix4; | ||||
| typedef struct Matrix4 { | ||||
|     Vec4 row[4]; | ||||
| } Matrix4; | ||||
|  | ||||
|  | ||||
| /* decrements an lvalue (which should be an int), stopping at 0 */ | ||||
| @@ -162,9 +162,9 @@ static inline float fast_sqrt(float x) | ||||
| } | ||||
|  | ||||
|  | ||||
| static inline t_fvec2 fast_cossine(float a) { | ||||
| static inline Vec2 fast_cossine(float a) { | ||||
|     const float s = sinf(a); | ||||
|     return (t_fvec2){ | ||||
|     return (Vec2){ | ||||
|         .x = fast_sqrt(1.0f - s * s) * (a >= (float)M_PI_2 && a < (float)(M_PI + M_PI_2) ? -1 : 1), | ||||
|         .y = s | ||||
|     }; | ||||
|   | ||||
| @@ -6,92 +6,92 @@ | ||||
|  | ||||
|  | ||||
| /* a point in some space (integer) */ | ||||
| typedef struct vec2 { | ||||
| typedef struct Vec2i { | ||||
| _Alignas(8) | ||||
|     int32_t x; | ||||
|     int32_t y; | ||||
| } t_vec2; | ||||
| } Vec2i; | ||||
|  | ||||
|  | ||||
| /* a point in some space (floating point) */ | ||||
| typedef struct fvec2 { | ||||
| typedef struct Vec2 { | ||||
| _Alignas(8) | ||||
|     float x; | ||||
|     float y; | ||||
| } t_fvec2; | ||||
| } Vec2; | ||||
|  | ||||
|  | ||||
| /* a point in some three dimension space (floating point) */ | ||||
| /* y goes up, x goes to the right */ | ||||
| typedef struct fvec3 { | ||||
| typedef struct Vec3 { | ||||
| _Alignas(16) | ||||
|     float x; | ||||
|     float y; | ||||
|     float z; | ||||
| } t_fvec3; | ||||
| } Vec3; | ||||
|  | ||||
|  | ||||
| /* a point in some three dimension space (floating point) */ | ||||
| /* y goes up, x goes to the right */ | ||||
| typedef struct fvec4 { | ||||
| typedef struct Vec4 { | ||||
| _Alignas(16) | ||||
|     float x; | ||||
|     float y; | ||||
|     float z; | ||||
|     float w; | ||||
| } t_fvec4; | ||||
| } Vec4; | ||||
|  | ||||
|  | ||||
| /* a point in some space (short) */ | ||||
| typedef struct shvec2 { | ||||
| typedef struct Vec2sh { | ||||
| _Alignas(4) | ||||
|     int16_t x; | ||||
|     int16_t y; | ||||
| } t_shvec2; | ||||
| } Vec2sh; | ||||
|  | ||||
|  | ||||
| /* 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) { | ||||
| static inline Vec2 vec2_from_vec2i(Vec2i vec) { | ||||
|     return (Vec2) { | ||||
|         .x = (float)vec.x, | ||||
|         .y = (float)vec.y, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static inline t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { | ||||
|     return (t_fvec2) { | ||||
| static inline Vec2 vec2_from_vec2sh(Vec2sh vec) { | ||||
|     return (Vec2) { | ||||
|         .x = (float)vec.x, | ||||
|         .y = (float)vec.y, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static inline t_fvec3 fvec3_add(t_fvec3 a, t_fvec3 b) { | ||||
|     return (t_fvec3) { a.x + b.x, a.y + b.y, a.z + b.z }; | ||||
| static inline Vec3 vec3_add(Vec3 a, Vec3 b) { | ||||
|     return (Vec3) { a.x + b.x, a.y + b.y, a.z + b.z }; | ||||
| } | ||||
|  | ||||
| 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 Vec3 vec3_sub(Vec3 a, Vec3 b) { | ||||
|     return (Vec3) { a.x - b.x, a.y - b.y, a.z - b.z }; | ||||
| } | ||||
|  | ||||
| static inline t_fvec2 fvec2_div(t_fvec2 a, t_fvec2 b) { | ||||
|     return (t_fvec2) { a.x / b.x, a.y / b.y }; | ||||
| static inline Vec2 vec2_div(Vec2 a, Vec2 b) { | ||||
|     return (Vec2) { a.x / b.x, a.y / b.y }; | ||||
| } | ||||
|  | ||||
| static inline t_fvec2 fvec2_scale(t_fvec2 a, float s) { | ||||
|     return (t_fvec2) { a.x * s, a.y * s }; | ||||
| static inline Vec2 vec2_scale(Vec2 a, float s) { | ||||
|     return (Vec2) { a.x * s, a.y * s }; | ||||
| } | ||||
|  | ||||
| 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 Vec3 vec3_scale(Vec3 a, float s) { | ||||
|     return (Vec3) { a.x * s, a.y * s, a.z * s }; | ||||
| } | ||||
|  | ||||
| static inline float fvec3_dot(t_fvec3 a, t_fvec3 b) { | ||||
| static inline float vec3_dot(Vec3 a, Vec3 b) { | ||||
|     return a.x * b.x + a.y * b.y + a.z * b.z; | ||||
| } | ||||
|  | ||||
| static inline t_fvec3 fvec3_cross(t_fvec3 a, t_fvec3 b) { | ||||
|     return (t_fvec3) { | ||||
| static inline Vec3 vec3_cross(Vec3 a, Vec3 b) { | ||||
|     return (Vec3) { | ||||
|         a.y * b.z - a.z * b.y, | ||||
|         a.z * b.x - a.x * b.z, | ||||
|         a.x * b.y - a.y * b.x, | ||||
| @@ -99,66 +99,66 @@ static inline t_fvec3 fvec3_cross(t_fvec3 a, t_fvec3 b) { | ||||
| } | ||||
|  | ||||
| /* TODO: fast_sqrt version? */ | ||||
| static inline t_fvec3 fvec3_norm(t_fvec3 a) { | ||||
|     const float n = sqrtf(fvec3_dot(a, a)); | ||||
| static inline Vec3 vec3_norm(Vec3 a) { | ||||
|     const float n = sqrtf(vec3_dot(a, a)); | ||||
|     /* TODO: do we need truncating over epsilon as cglm does? */ | ||||
|     return fvec3_scale(a, 1.0f / n); | ||||
|     return vec3_scale(a, 1.0f / n); | ||||
| } | ||||
|  | ||||
| static inline t_fvec3 fvec3_rotate(t_fvec3 v, float angle, t_fvec3 axis) { | ||||
| static inline Vec3 vec3_rotate(Vec3 v, float angle, Vec3 axis) { | ||||
|     /* from cglm */ | ||||
|     t_fvec3 v1, v2, k; | ||||
|     Vec3 v1, v2, k; | ||||
|     float c, s; | ||||
|  | ||||
|     c = cosf(angle); | ||||
|     s = sinf(angle); | ||||
|  | ||||
|     k = fvec3_norm(axis); | ||||
|     k = vec3_norm(axis); | ||||
|  | ||||
|     /* Right Hand, Rodrigues' rotation formula: | ||||
|         v = v*cos(t) + (kxv)sin(t) + k*(k.v)(1 - cos(t)) | ||||
|     */ | ||||
|     v1 = fvec3_scale(v, c); | ||||
|     v1 = vec3_scale(v, c); | ||||
|  | ||||
|     v2 = fvec3_cross(k, v); | ||||
|     v2 = fvec3_scale(v2, s); | ||||
|     v2 = vec3_cross(k, v); | ||||
|     v2 = vec3_scale(v2, s); | ||||
|  | ||||
|     v1 = fvec3_add(v1, v2); | ||||
|     v1 = vec3_add(v1, v2); | ||||
|  | ||||
|     v2 = fvec3_scale(k, fvec3_dot(k, v) * (1.0f - c)); | ||||
|     v = fvec3_add(v1, v2); | ||||
|     v2 = vec3_scale(k, vec3_dot(k, v) * (1.0f - c)); | ||||
|     v = vec3_add(v1, v2); | ||||
|  | ||||
|     return v; | ||||
| } | ||||
|  | ||||
| #define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2),          \ | ||||
|                                     t_vec2:   fvec2_from_vec2,  \ | ||||
|                                     t_shvec2: fvec2_from_shvec2 \ | ||||
| #define m_vec2_from(p_any_vec2) (_Generic((p_any_vec2),       \ | ||||
|                                     Vec2i:  vec2_from_vec2i,  \ | ||||
|                                     Vec2sh: vec2_from_vec2sh  \ | ||||
|                                 )(p_any_vec2)) | ||||
|  | ||||
| #define m_vec_sub(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0),   \ | ||||
|                                     t_fvec3:   fvec3_sub            \ | ||||
|                                     Vec3:   vec3_sub                \ | ||||
|                                 )(p_any_vec0, p_any_vec1)) | ||||
|  | ||||
| #define m_vec_div(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0),   \ | ||||
|                                     t_fvec2:   fvec2_div            \ | ||||
|                                     Vec2:   vec2_div                \ | ||||
|                                 )(p_any_vec0, p_any_vec1)) | ||||
|  | ||||
| #define m_vec_scale(p_any_vec, p_any_scalar) (_Generic((p_any_vec),    \ | ||||
|                                     t_fvec2:   fvec2_scale,             \ | ||||
|                                     t_fvec3:   fvec3_scale              \ | ||||
|                                     Vec2:   vec2_scale,                \ | ||||
|                                     Vec3:   vec3_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            \ | ||||
|                                     Vec3:   vec3_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              \ | ||||
|                                     Vec3:   vec3_cross                   \ | ||||
|                                 )(p_any_vec0, p_any_vec1)) | ||||
|  | ||||
| #define m_vec_norm(p_any_vec) (_Generic((p_any_vec),            \ | ||||
|                                     t_fvec3:   fvec3_norm       \ | ||||
|                                     Vec3:   vec3_norm           \ | ||||
|                                 )(p_any_vec)) | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -7,14 +7,14 @@ | ||||
| #include <stb_ds.h> | ||||
|  | ||||
|  | ||||
| void push_circle(t_fvec2 position, float radius, t_color color) { | ||||
|     struct circle_primitive circle = { | ||||
| void push_circle(Vec2 position, float radius, Color color) { | ||||
|     CirclePrimitive circle = { | ||||
|         .radius = radius, | ||||
|         .color = color, | ||||
|         .position = position, | ||||
|     }; | ||||
|  | ||||
|     struct primitive_2d primitive = { | ||||
|     Primitive2D primitive = { | ||||
|         .type = PRIMITIVE_2D_CIRCLE, | ||||
|         .circle = circle, | ||||
|     }; | ||||
| @@ -24,8 +24,8 @@ void push_circle(t_fvec2 position, float radius, t_color color) { | ||||
|  | ||||
| /* TODO: caching and reuse scheme */ | ||||
| /* vertices_out and indices_out MUST BE FREED */ | ||||
| void create_circle_geometry(t_fvec2 position, | ||||
|                                    t_color color, | ||||
| void create_circle_geometry(Vec2 position, | ||||
|                                    Color color, | ||||
|                                    float radius, | ||||
|                                    size_t num_vertices, | ||||
|                                    SDL_Vertex **vertices_out, | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "twn_util.h" | ||||
|  | ||||
|  | ||||
| gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps) { | ||||
| GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps) { | ||||
|     GLuint texture; | ||||
|     glGenTextures(1, &texture); | ||||
|     glBindTexture(GL_TEXTURE_2D, texture); | ||||
| @@ -29,12 +29,12 @@ gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps | ||||
| } | ||||
|  | ||||
|  | ||||
| void delete_gpu_texture(gpu_texture texture) { | ||||
| void delete_gpu_texture(GPUTexture texture) { | ||||
|     glDeleteTextures(1, &texture); | ||||
| } | ||||
|  | ||||
|  | ||||
| void upload_gpu_texture(gpu_texture texture, void *pixels, int channels, int width, int height) { | ||||
| void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height) { | ||||
|     glBindTexture(GL_TEXTURE_2D, texture); | ||||
|  | ||||
|     int format_internal, format; | ||||
| @@ -66,6 +66,6 @@ void upload_gpu_texture(gpu_texture texture, void *pixels, int channels, int wid | ||||
| } | ||||
|  | ||||
|  | ||||
| void bind_gpu_texture(gpu_texture texture) { | ||||
| void bind_gpu_texture(GPUTexture texture) { | ||||
|     glBindTexture(GL_TEXTURE_2D, texture); | ||||
| } | ||||
|   | ||||
| @@ -11,50 +11,50 @@ | ||||
| /* TODO: use int16_t for uvs */ | ||||
| /* TODO: use packed types? */ | ||||
| /* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ | ||||
| struct element_indexed_quad { | ||||
| typedef struct ElementIndexedQuad { | ||||
|     /* upper-left */ | ||||
|     t_fvec2 v0; | ||||
|     t_fvec2 uv0; | ||||
|     t_color c0; | ||||
|     Vec2 v0; | ||||
|     Vec2 uv0; | ||||
|     Color c0; | ||||
|     /* bottom-left */ | ||||
|     t_fvec2 v1; | ||||
|     t_fvec2 uv1; | ||||
|     t_color c1; | ||||
|     Vec2 v1; | ||||
|     Vec2 uv1; | ||||
|     Color c1; | ||||
|     /* bottom-right */ | ||||
|     t_fvec2 v2; | ||||
|     t_fvec2 uv2; | ||||
|     t_color c2; | ||||
|     Vec2 v2; | ||||
|     Vec2 uv2; | ||||
|     Color c2; | ||||
|     /* upper-right */ | ||||
|     t_fvec2 v3; | ||||
|     t_fvec2 uv3; | ||||
|     t_color c3; | ||||
| }; | ||||
|     Vec2 v3; | ||||
|     Vec2 uv3; | ||||
|     Color c3; | ||||
| } ElementIndexedQuad; | ||||
|  | ||||
|  | ||||
| struct element_indexed_quad_without_color { | ||||
| typedef struct ElementIndexedQuadWithoutColor { | ||||
|     /* upper-left */ | ||||
|     t_fvec2 v0; | ||||
|     t_fvec2 uv0; | ||||
|     Vec2 v0; | ||||
|     Vec2 uv0; | ||||
|     /* bottom-left */ | ||||
|     t_fvec2 v1; | ||||
|     t_fvec2 uv1; | ||||
|     Vec2 v1; | ||||
|     Vec2 uv1; | ||||
|     /* bottom-right */ | ||||
|     t_fvec2 v2; | ||||
|     t_fvec2 uv2; | ||||
|     Vec2 v2; | ||||
|     Vec2 uv2; | ||||
|     /* upper-right */ | ||||
|     t_fvec2 v3; | ||||
|     t_fvec2 uv3; | ||||
| }; | ||||
|     Vec2 v3; | ||||
|     Vec2 uv3; | ||||
| } ElementIndexedQuadWithoutColor; | ||||
|  | ||||
|  | ||||
| typedef enum { | ||||
|     PIPELINE_NO, | ||||
|     PIPELINE_SPACE, | ||||
|     PIPELINE_2D, | ||||
| } pipeline; | ||||
| } Pipeline; | ||||
|  | ||||
|  | ||||
| static pipeline pipeline_last_used = PIPELINE_NO; | ||||
| static Pipeline pipeline_last_used = PIPELINE_NO; | ||||
|  | ||||
|  | ||||
| void use_space_pipeline(void) { | ||||
| @@ -117,7 +117,7 @@ void use_2d_pipeline(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void upload_quad_vertices(t_frect rect) { | ||||
| void upload_quad_vertices(Rect rect) { | ||||
|     /* client memory needs to be reachable on glDraw*, so */ | ||||
|     static float vertices[6 * 2]; | ||||
|  | ||||
| @@ -132,7 +132,7 @@ void upload_quad_vertices(t_frect rect) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void render_rectangle(const struct rect_primitive *rectangle) { | ||||
| void render_rectangle(const RectPrimitive *rectangle) { | ||||
|     glColor4ub(rectangle->color.r, rectangle->color.g, | ||||
|                rectangle->color.b, rectangle->color.a); | ||||
|  | ||||
| @@ -144,7 +144,7 @@ void render_rectangle(const struct rect_primitive *rectangle) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void render_circle(const struct circle_primitive *circle) { | ||||
| void render_circle(const CirclePrimitive *circle) { | ||||
|     SDL_Vertex *vertices = NULL; | ||||
|     int *indices = NULL; | ||||
|     int num_vertices = (int)circle->radius; | ||||
| @@ -181,7 +181,7 @@ void render_circle(const struct circle_primitive *circle) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_texture_mode(enum texture_mode mode) { | ||||
| void use_texture_mode(TextureMode mode) { | ||||
|     if (mode == TEXTURE_MODE_GHOSTLY) { | ||||
|         glEnable(GL_BLEND); | ||||
|         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
| @@ -203,21 +203,21 @@ void use_texture_mode(enum texture_mode mode) { | ||||
| } | ||||
|  | ||||
|  | ||||
| vertex_buffer_builder build_vertex_buffer(vertex_buffer buffer, size_t bytes) { | ||||
| VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) { | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, buffer); | ||||
|     glBufferData(GL_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW); | ||||
|     void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); | ||||
|     if (!mapping) | ||||
|         CRY("build_vertex_buffer", "Error mapping a vertex array buffer"); | ||||
|  | ||||
|     return (vertex_buffer_builder) { | ||||
|     return (VertexBufferBuilder) { | ||||
|         .mapping = mapping, | ||||
|         .bytes_left = bytes, | ||||
|     }; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool push_to_vertex_buffer_builder(vertex_buffer_builder *builder, | ||||
| bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder, | ||||
|                                    void *bytes, size_t size) { | ||||
|     if (builder->bytes_left == 0) | ||||
|         return false; | ||||
| @@ -237,9 +237,9 @@ bool push_to_vertex_buffer_builder(vertex_buffer_builder *builder, | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_render_sprites(const struct primitive_2d primitives[], | ||||
|                             const struct sprite_batch batch, | ||||
|                             const vertex_buffer buffer) | ||||
| void finally_render_sprites(const Primitive2D primitives[], | ||||
|                             const struct SpriteBatch batch, | ||||
|                             const VertexBuffer buffer) | ||||
| { | ||||
|     /* TODO: maybe do, dunno */ | ||||
|     // glBindBuffer(GL_VERTEX_ARRAY, vertex_buffer); | ||||
| @@ -250,13 +250,13 @@ void finally_render_sprites(const struct primitive_2d primitives[], | ||||
|     GLsizei uvoff; | ||||
|  | ||||
|     if (!batch.constant_colored) { | ||||
|         off   = offsetof(struct element_indexed_quad, v1); | ||||
|         voff  = offsetof(struct element_indexed_quad, v0); | ||||
|         uvoff = offsetof(struct element_indexed_quad, uv0); | ||||
|         off   = offsetof(ElementIndexedQuad, v1); | ||||
|         voff  = offsetof(ElementIndexedQuad, v0); | ||||
|         uvoff = offsetof(ElementIndexedQuad, uv0); | ||||
|     } else { | ||||
|         off   = offsetof(struct element_indexed_quad_without_color, v1); | ||||
|         voff  = offsetof(struct element_indexed_quad_without_color, v0); | ||||
|         uvoff = offsetof(struct element_indexed_quad_without_color, uv0); | ||||
|         off   = offsetof(ElementIndexedQuadWithoutColor, v1); | ||||
|         voff  = offsetof(ElementIndexedQuadWithoutColor, v0); | ||||
|         uvoff = offsetof(ElementIndexedQuadWithoutColor, uv0); | ||||
|     } | ||||
|  | ||||
|     /* vertex specification */ | ||||
| @@ -278,7 +278,7 @@ void finally_render_sprites(const struct primitive_2d primitives[], | ||||
|         glColorPointer(4, | ||||
|                        GL_UNSIGNED_BYTE, | ||||
|                        off, | ||||
|                        (void *)offsetof(struct element_indexed_quad, c0)); | ||||
|                        (void *)offsetof(ElementIndexedQuad, c0)); | ||||
|     } else | ||||
|         glColor4ub(primitives[0].sprite.color.r, | ||||
|                    primitives[0].sprite.color.g, | ||||
| @@ -307,22 +307,22 @@ void finally_render_sprites(const struct primitive_2d primitives[], | ||||
| } | ||||
|  | ||||
|  | ||||
| size_t get_sprite_payload_size(struct sprite_batch batch) { | ||||
| size_t get_sprite_payload_size(struct SpriteBatch batch) { | ||||
|     if (batch.constant_colored) | ||||
|         return sizeof (struct element_indexed_quad_without_color); | ||||
|         return sizeof (ElementIndexedQuadWithoutColor); | ||||
|     else | ||||
|         return sizeof (struct element_indexed_quad); | ||||
|         return sizeof (ElementIndexedQuad); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch, | ||||
|                                                   vertex_buffer_builder *builder, | ||||
|                                                   t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3, | ||||
|                                                   t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3, | ||||
|                                                   t_color color) | ||||
| bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch, | ||||
|                                                   VertexBufferBuilder *builder, | ||||
|                                                   Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3, | ||||
|                                                   Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3, | ||||
|                                                   Color color) | ||||
| { | ||||
|     if (!batch.constant_colored) { | ||||
|         struct element_indexed_quad buffer_element = { | ||||
|         ElementIndexedQuad buffer_element = { | ||||
|             .v0 = v0, | ||||
|             .v1 = v1, | ||||
|             .v2 = v2, | ||||
| @@ -343,7 +343,7 @@ bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch, | ||||
|         return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); | ||||
|  | ||||
|     } else { | ||||
|         struct element_indexed_quad_without_color buffer_element = { | ||||
|         ElementIndexedQuadWithoutColor buffer_element = { | ||||
|             .v0 = v0, | ||||
|             .v1 = v1, | ||||
|             .v2 = v2, | ||||
| @@ -360,9 +360,9 @@ bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch, | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_draw_uncolored_space_traingle_batch(const struct mesh_batch *batch, | ||||
|                                                  const t_texture_key texture_key, | ||||
|                                                  const vertex_buffer buffer) | ||||
| void finally_draw_uncolored_space_traingle_batch(const MeshBatch *batch, | ||||
|                                                  const TextureKey texture_key, | ||||
|                                                  const VertexBuffer buffer) | ||||
| { | ||||
|     const size_t primitives_len = arrlenu(batch->primitives); | ||||
|  | ||||
| @@ -374,15 +374,15 @@ void finally_draw_uncolored_space_traingle_batch(const struct mesh_batch *batch, | ||||
|     glEnableClientState(GL_VERTEX_ARRAY); | ||||
|     glVertexPointer(3, | ||||
|                     GL_FLOAT, | ||||
|                     offsetof(struct uncolored_space_triangle_payload, v1), | ||||
|                     (void *)offsetof(struct uncolored_space_triangle_payload, v0)); | ||||
|                     offsetof(struct UncoloredSpaceTrianglePayload, v1), | ||||
|                     (void *)offsetof(struct UncoloredSpaceTrianglePayload, v0)); | ||||
|  | ||||
|     glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||||
|     glClientActiveTexture(GL_TEXTURE0); | ||||
|     glTexCoordPointer(2, | ||||
|                       GL_FLOAT, | ||||
|                       offsetof(struct uncolored_space_triangle_payload, v1), | ||||
|                       (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); | ||||
|                       offsetof(struct UncoloredSpaceTrianglePayload, v1), | ||||
|                       (void *)offsetof(struct UncoloredSpaceTrianglePayload, uv0)); | ||||
|  | ||||
|     /* commit for drawing */ | ||||
|     glDrawArrays(GL_TRIANGLES, 0, 3 * (GLint)primitives_len); | ||||
| @@ -396,32 +396,32 @@ void finally_draw_uncolored_space_traingle_batch(const struct mesh_batch *batch, | ||||
| } | ||||
|  | ||||
|  | ||||
| bool push_text_payload_to_vertex_buffer_builder(struct font_data const *font_data, | ||||
|                                                 vertex_buffer_builder *builder, | ||||
| bool push_text_payload_to_vertex_buffer_builder(FontData const *font_data, | ||||
|                                                 VertexBufferBuilder *builder, | ||||
|                                                 stbtt_aligned_quad quad) | ||||
| { | ||||
|     (void)font_data; | ||||
|  | ||||
|     struct element_indexed_quad_without_color buffer_element = { | ||||
|         .v0 = (t_fvec2){ quad.x0, quad.y0 }, | ||||
|         .v1 = (t_fvec2){ quad.x1, quad.y0 }, | ||||
|         .v2 = (t_fvec2){ quad.x1, quad.y1 }, | ||||
|         .v3 = (t_fvec2){ quad.x0, quad.y1 }, | ||||
|     ElementIndexedQuadWithoutColor buffer_element = { | ||||
|         .v0 = (Vec2){ quad.x0, quad.y0 }, | ||||
|         .v1 = (Vec2){ quad.x1, quad.y0 }, | ||||
|         .v2 = (Vec2){ quad.x1, quad.y1 }, | ||||
|         .v3 = (Vec2){ quad.x0, quad.y1 }, | ||||
|  | ||||
|         .uv0 = (t_fvec2){ quad.s0, quad.t0 }, | ||||
|         .uv1 = (t_fvec2){ quad.s1, quad.t0 }, | ||||
|         .uv2 = (t_fvec2){ quad.s1, quad.t1 }, | ||||
|         .uv3 = (t_fvec2){ quad.s0, quad.t1 }, | ||||
|         .uv0 = (Vec2){ quad.s0, quad.t0 }, | ||||
|         .uv1 = (Vec2){ quad.s1, quad.t0 }, | ||||
|         .uv2 = (Vec2){ quad.s1, quad.t1 }, | ||||
|         .uv3 = (Vec2){ quad.s0, quad.t1 }, | ||||
|     }; | ||||
|  | ||||
|     return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_draw_text(struct font_data const *font_data, | ||||
| void finally_draw_text(FontData const *font_data, | ||||
|                        size_t len, | ||||
|                        t_color color, | ||||
|                        vertex_buffer buffer) | ||||
|                        Color color, | ||||
|                        VertexBuffer buffer) | ||||
| { | ||||
|     (void)buffer; | ||||
|  | ||||
| @@ -429,15 +429,15 @@ void finally_draw_text(struct font_data const *font_data, | ||||
|     glEnableClientState(GL_VERTEX_ARRAY); | ||||
|     glVertexPointer(2, | ||||
|                     GL_FLOAT, | ||||
|                     offsetof(struct element_indexed_quad_without_color, v1), | ||||
|                     (void *)(size_t)offsetof(struct element_indexed_quad_without_color, v0)); | ||||
|                     offsetof(ElementIndexedQuadWithoutColor, v1), | ||||
|                     (void *)(size_t)offsetof(ElementIndexedQuadWithoutColor, v0)); | ||||
|  | ||||
|     glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||||
|     glClientActiveTexture(GL_TEXTURE0); | ||||
|     glTexCoordPointer(2, | ||||
|                       GL_FLOAT, | ||||
|                       offsetof(struct element_indexed_quad_without_color, v1), | ||||
|                       (void *)(size_t)offsetof(struct element_indexed_quad_without_color, uv0)); | ||||
|                       offsetof(ElementIndexedQuadWithoutColor, v1), | ||||
|                       (void *)(size_t)offsetof(ElementIndexedQuadWithoutColor, uv0)); | ||||
|  | ||||
|     bind_quad_element_buffer(); | ||||
|  | ||||
| @@ -462,5 +462,5 @@ void finally_draw_text(struct font_data const *font_data, | ||||
|  | ||||
|  | ||||
| size_t get_text_payload_size(void) { | ||||
|     return sizeof (struct element_indexed_quad_without_color); | ||||
|     return sizeof (ElementIndexedQuadWithoutColor); | ||||
| } | ||||
|   | ||||
| @@ -14,19 +14,19 @@ void setup_viewport(int x, int y, int width, int height) { | ||||
| } | ||||
|  | ||||
|  | ||||
| vertex_buffer create_vertex_buffer(void) { | ||||
| VertexBuffer create_vertex_buffer(void) { | ||||
|     GLuint result; | ||||
|     glGenBuffers(1, &result); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
|  | ||||
| void delete_vertex_buffer(vertex_buffer buffer) { | ||||
| void delete_vertex_buffer(VertexBuffer buffer) { | ||||
|     glDeleteBuffers(1, &buffer); | ||||
| } | ||||
|  | ||||
|  | ||||
| void specify_vertex_buffer(vertex_buffer buffer, void *data, size_t bytes) { | ||||
| void specify_vertex_buffer(VertexBuffer buffer, void *data, size_t bytes) { | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, buffer); | ||||
|     glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW); | ||||
| } | ||||
|   | ||||
| @@ -3,19 +3,19 @@ | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| typedef GLuint gpu_texture; | ||||
| typedef GLuint GPUTexture; | ||||
|  | ||||
| enum texture_filter { | ||||
| typedef enum TextureFilter { | ||||
|     TEXTURE_FILTER_NEAREAST, | ||||
|     TEXTURE_FILTER_LINEAR, | ||||
| }; | ||||
| } TextureFilter; | ||||
|  | ||||
| gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps); | ||||
| GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps); | ||||
|  | ||||
| void delete_gpu_texture(gpu_texture texture); | ||||
| void delete_gpu_texture(GPUTexture texture); | ||||
|  | ||||
| void upload_gpu_texture(gpu_texture texture, void *pixels, int channels, int width, int height); | ||||
| void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height); | ||||
|  | ||||
| void bind_gpu_texture(gpu_texture texture); | ||||
| void bind_gpu_texture(GPUTexture texture); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -17,8 +17,8 @@ | ||||
|  | ||||
|  | ||||
| /* TODO: have a default initialized one */ | ||||
| t_matrix4 camera_projection_matrix; | ||||
| t_matrix4 camera_look_at_matrix; | ||||
| Matrix4 camera_projection_matrix; | ||||
| Matrix4 camera_look_at_matrix; | ||||
|  | ||||
|  | ||||
| void render_queue_clear(void) { | ||||
| @@ -41,13 +41,13 @@ void render_queue_clear(void) { | ||||
|  | ||||
|  | ||||
| /* rectangle */ | ||||
| void push_rectangle(t_frect rect, t_color color) { | ||||
|     struct rect_primitive rectangle = { | ||||
| void push_rectangle(Rect rect, Color color) { | ||||
|     RectPrimitive rectangle = { | ||||
|         .rect = rect, | ||||
|         .color = color, | ||||
|     }; | ||||
|  | ||||
|     struct primitive_2d primitive = { | ||||
|     Primitive2D primitive = { | ||||
|         .type = PRIMITIVE_2D_RECT, | ||||
|         .rect = rectangle, | ||||
|     }; | ||||
| @@ -64,11 +64,11 @@ static void render_2d(void) { | ||||
|     size_t batch_count = 0; | ||||
|  | ||||
|     for (size_t i = 0; i < render_queue_len; ++i) { | ||||
|         const struct primitive_2d *current = &ctx.render_queue_2d[i]; | ||||
|         const Primitive2D *current = &ctx.render_queue_2d[i]; | ||||
|  | ||||
|         switch (current->type) { | ||||
|             case PRIMITIVE_2D_SPRITE: { | ||||
|                 const struct sprite_batch batch = | ||||
|                 const struct SpriteBatch batch = | ||||
|                     collect_sprite_batch(current, render_queue_len - i); | ||||
|  | ||||
|                 /* TODO: what's even the point? just use OR_EQUAL comparison */ | ||||
| @@ -140,7 +140,7 @@ void render(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void set_camera(const t_camera *const camera) { | ||||
| void set_camera(const Camera *const camera) { | ||||
|     /* TODO: skip recaulculating if it's the same? */ | ||||
|     camera_projection_matrix = camera_perspective(camera); | ||||
|     camera_look_at_matrix    = camera_look_at(camera); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| #define TWN_RENDERING_C_H | ||||
|  | ||||
| #include "twn_textures_c.h" | ||||
| #include "twn_text_c.h" | ||||
| #include "twn_util.h" | ||||
| #include "twn_option.h" | ||||
|  | ||||
| @@ -16,107 +17,107 @@ | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| extern t_matrix4 camera_projection_matrix; | ||||
| extern t_matrix4 camera_look_at_matrix; | ||||
| extern Matrix4 camera_projection_matrix; | ||||
| extern Matrix4 camera_look_at_matrix; | ||||
|  | ||||
| #define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) | ||||
|  | ||||
|  | ||||
| typedef GLuint vertex_buffer; | ||||
| typedef GLuint VertexBuffer; | ||||
|  | ||||
| typedef struct vertex_buffer_builder { | ||||
| typedef struct VertexBufferBuilder { | ||||
|     size_t bytes_left; | ||||
|     void *mapping; | ||||
| } vertex_buffer_builder; | ||||
| } VertexBufferBuilder; | ||||
|  | ||||
|  | ||||
| struct sprite_primitive { | ||||
|     t_frect rect; | ||||
|     t_color color; | ||||
| typedef struct SpritePrimitive { | ||||
|     Rect rect; | ||||
|     Color color; | ||||
|     float rotation; | ||||
|     t_texture_key texture_key; | ||||
|     TextureKey texture_key; | ||||
|     bool flip_x; | ||||
|     bool flip_y; | ||||
|     bool repeat; | ||||
|  | ||||
|     m_option_list( | ||||
|         t_frect, texture_region ) | ||||
| }; | ||||
|         Rect, texture_region ) | ||||
| } SpritePrimitive; | ||||
|  | ||||
| struct rect_primitive { | ||||
|     t_frect rect; | ||||
|     t_color color; | ||||
| }; | ||||
| typedef struct RectPrimitive { | ||||
|     Rect rect; | ||||
|     Color color; | ||||
| } RectPrimitive; | ||||
|  | ||||
| struct circle_primitive { | ||||
| typedef struct CirclePrimitive { | ||||
|     float radius; | ||||
|     t_color color; | ||||
|     t_fvec2 position; | ||||
| }; | ||||
|     Color color; | ||||
|     Vec2 position; | ||||
| } CirclePrimitive; | ||||
|  | ||||
| struct text_primitive { | ||||
|     t_color color; | ||||
|     t_fvec2 position; | ||||
| typedef struct TextPrimitive { | ||||
|     Color color; | ||||
|     Vec2 position; | ||||
|     char *text; | ||||
|     const char *font; | ||||
|     int height_px; | ||||
| }; | ||||
| } TextPrimitive; | ||||
|  | ||||
| enum primitive_2d_type { | ||||
| typedef enum Primitive2DType { | ||||
|     PRIMITIVE_2D_SPRITE, | ||||
|     PRIMITIVE_2D_RECT, | ||||
|     PRIMITIVE_2D_CIRCLE, | ||||
|     PRIMITIVE_2D_TEXT, | ||||
| }; | ||||
| } Primitive2DType; | ||||
|  | ||||
| struct primitive_2d { | ||||
|     enum primitive_2d_type type; | ||||
| typedef struct Primitive2D { | ||||
|     Primitive2DType type; | ||||
|  | ||||
|     union { | ||||
|         struct sprite_primitive sprite; | ||||
|         struct rect_primitive rect; | ||||
|         struct circle_primitive circle; | ||||
|         struct text_primitive text; | ||||
|     }; | ||||
|         SpritePrimitive sprite; | ||||
|         RectPrimitive rect; | ||||
|         CirclePrimitive circle; | ||||
|         TextPrimitive text; | ||||
|     }; | ||||
| } Primitive2D; | ||||
|  | ||||
| /* union for in-place recalculation of texture coordinates */ | ||||
| union uncolored_space_triangle { | ||||
| union UncoloredSpaceTriangle { | ||||
|     /* pending for sending, uvs are not final as texture atlases could update */ | ||||
|     struct uncolored_space_triangle_primitive { | ||||
|         t_fvec3 v0; | ||||
|         t_fvec2 uv0; /* in pixels */ | ||||
|         t_fvec3 v1; | ||||
|         t_fvec2 uv1; /* in pixels */ | ||||
|         t_fvec3 v2; | ||||
|         t_fvec2 uv2; /* in pixels */ | ||||
|     struct UncoloredSpaceTrianglePrimitive { | ||||
|         Vec3 v0; | ||||
|         Vec2 uv0; /* in pixels */ | ||||
|         Vec3 v1; | ||||
|         Vec2 uv1; /* in pixels */ | ||||
|         Vec3 v2; | ||||
|         Vec2 uv2; /* in pixels */ | ||||
|     } primitive; | ||||
|  | ||||
|     /* TODO: have it packed? */ | ||||
|     /* structure that is passed in opengl vertex array */ | ||||
|     struct uncolored_space_triangle_payload { | ||||
|         t_fvec3 v0; | ||||
|         t_fvec2 uv0; | ||||
|         t_fvec3 v1; | ||||
|         t_fvec2 uv1; | ||||
|         t_fvec3 v2; | ||||
|         t_fvec2 uv2; | ||||
|     struct UncoloredSpaceTrianglePayload { | ||||
|         Vec3 v0; | ||||
|         Vec2 uv0; | ||||
|         Vec3 v1; | ||||
|         Vec2 uv1; | ||||
|         Vec3 v2; | ||||
|         Vec2 uv2; | ||||
|     } payload; | ||||
| }; | ||||
|  | ||||
| /* batch of primitives with overlapping properties */ | ||||
| struct mesh_batch { | ||||
| typedef struct MeshBatch { | ||||
|     uint8_t *primitives; | ||||
| }; | ||||
| } MeshBatch; | ||||
|  | ||||
| struct mesh_batch_item { | ||||
|     t_texture_key key; | ||||
|     struct mesh_batch value; | ||||
| }; | ||||
| typedef struct MeshBatchItem { | ||||
|     TextureKey key; | ||||
|     struct MeshBatch value; | ||||
| } MeshBatchItem; | ||||
|  | ||||
| struct text_cache { | ||||
|     struct font_data **data; | ||||
| }; | ||||
| typedef struct TextCache { | ||||
|     struct FontData **data; | ||||
| } TextCache; | ||||
|  | ||||
|  | ||||
| /* renders the background, then the primitives in all render queues */ | ||||
| @@ -125,47 +126,47 @@ void render(void); | ||||
| /* clears all render queues */ | ||||
| void render_queue_clear(void); | ||||
|  | ||||
| void create_circle_geometry(t_fvec2 position, | ||||
|                             t_color color, | ||||
| void create_circle_geometry(Vec2 position, | ||||
|                             Color color, | ||||
|                             float radius, | ||||
|                             size_t num_vertices, | ||||
|                             SDL_Vertex **vertices_out, | ||||
|                             int **indices_out); | ||||
|  | ||||
| struct sprite_batch { | ||||
| struct SpriteBatch { | ||||
|     size_t size;             /* how many primitives are in current batch */ | ||||
|     enum texture_mode mode; | ||||
|     TextureMode mode; | ||||
|     bool constant_colored;   /* whether colored batch is uniformly colored */ | ||||
|     bool repeat;             /* whether repeat is needed */ | ||||
| } collect_sprite_batch(const struct primitive_2d primitives[], size_t len); | ||||
| } collect_sprite_batch(const Primitive2D primitives[], size_t len); | ||||
|  | ||||
| void render_sprites(const struct primitive_2d primitives[], | ||||
|                     const struct sprite_batch batch); | ||||
| void render_sprites(const Primitive2D primitives[], | ||||
|                     const struct SpriteBatch batch); | ||||
|  | ||||
| void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, | ||||
|                                          t_texture_key texture_key); | ||||
| void draw_uncolored_space_traingle_batch(MeshBatch *batch, | ||||
|                                          TextureKey texture_key); | ||||
|  | ||||
| /* text */ | ||||
|  | ||||
| void render_text(const struct text_primitive *text); | ||||
| void render_text(const TextPrimitive *text); | ||||
|  | ||||
| void text_cache_init(struct text_cache *cache); | ||||
| void text_cache_init(TextCache *cache); | ||||
|  | ||||
| void text_cache_deinit(struct text_cache *cache); | ||||
| void text_cache_deinit(TextCache *cache); | ||||
|  | ||||
| /* vertex buffer */ | ||||
|  | ||||
| vertex_buffer create_vertex_buffer(void); | ||||
| VertexBuffer create_vertex_buffer(void); | ||||
|  | ||||
| void delete_vertex_buffer(vertex_buffer buffer); | ||||
| void delete_vertex_buffer(VertexBuffer buffer); | ||||
|  | ||||
| void specify_vertex_buffer(vertex_buffer buffer, void *data, size_t bytes); | ||||
| void specify_vertex_buffer(VertexBuffer buffer, void *data, size_t bytes); | ||||
|  | ||||
| /* uses present in 1.5 buffer mapping feature */ | ||||
| vertex_buffer_builder build_vertex_buffer(vertex_buffer buffer, size_t bytes); | ||||
| VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes); | ||||
|  | ||||
| /* collects bytes for sending to the gpu until all is pushed, which is when false is returned */ | ||||
| bool push_to_vertex_buffer_builder(vertex_buffer_builder *builder, | ||||
| bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder, | ||||
|                                    void *bytes, | ||||
|                                    size_t size); | ||||
|  | ||||
| @@ -181,41 +182,41 @@ void set_depth_range(double low, double high); | ||||
|  | ||||
| void bind_quad_element_buffer(void); | ||||
|  | ||||
| void render_circle(const struct circle_primitive *circle); | ||||
| void render_circle(const CirclePrimitive *circle); | ||||
|  | ||||
| void render_rectangle(const struct rect_primitive *rectangle); | ||||
| void render_rectangle(const RectPrimitive *rectangle); | ||||
|  | ||||
| void use_space_pipeline(void); | ||||
|  | ||||
| void use_2d_pipeline(void); | ||||
|  | ||||
| void use_texture_mode(enum texture_mode mode); | ||||
| void use_texture_mode(TextureMode mode); | ||||
|  | ||||
| void finally_render_sprites(struct primitive_2d const primitives[], | ||||
|                             struct sprite_batch batch, | ||||
|                             vertex_buffer buffer); | ||||
| void finally_render_sprites(Primitive2D const primitives[], | ||||
|                             struct SpriteBatch batch, | ||||
|                             VertexBuffer buffer); | ||||
|  | ||||
| size_t get_sprite_payload_size(struct sprite_batch batch); | ||||
| size_t get_sprite_payload_size(struct SpriteBatch batch); | ||||
|  | ||||
| bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch, | ||||
|                                                   vertex_buffer_builder *builder, | ||||
|                                                   t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3, | ||||
|                                                   t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3, | ||||
|                                                   t_color color); | ||||
| bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch, | ||||
|                                                   VertexBufferBuilder *builder, | ||||
|                                                   Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3, | ||||
|                                                   Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3, | ||||
|                                                   Color color); | ||||
|  | ||||
| void finally_draw_uncolored_space_traingle_batch(struct mesh_batch const *batch, | ||||
|                                                  t_texture_key texture_key, | ||||
|                                                  vertex_buffer buffer); | ||||
| void finally_draw_uncolored_space_traingle_batch(MeshBatch const *batch, | ||||
|                                                  TextureKey texture_key, | ||||
|                                                  VertexBuffer buffer); | ||||
|  | ||||
| size_t get_text_payload_size(void); | ||||
|  | ||||
| bool push_text_payload_to_vertex_buffer_builder(struct font_data const *font_data, | ||||
|                                                 vertex_buffer_builder *builder, | ||||
| bool push_text_payload_to_vertex_buffer_builder(FontData const *font_data, | ||||
|                                                 VertexBufferBuilder *builder, | ||||
|                                                 stbtt_aligned_quad quad); | ||||
|  | ||||
| void finally_draw_text(struct font_data const *font_data, | ||||
| void finally_draw_text(FontData const *font_data, | ||||
|                        size_t len, | ||||
|                        t_color color, | ||||
|                        vertex_buffer buffer); | ||||
|                        Color color, | ||||
|                        VertexBuffer buffer); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -17,10 +17,10 @@ | ||||
|  */ | ||||
| /* TODO: it might make sense to infer alpha channel presence / meaningfulness for textures in atlas */ | ||||
| /*          so that they are rendered with no blend / batched in a way to reduce overdraw automatically */ | ||||
| void push_sprite(const t_push_sprite_args args) { | ||||
|     struct sprite_primitive sprite = { | ||||
| void push_sprite(const PushSpriteArgs args) { | ||||
|     SpritePrimitive sprite = { | ||||
|         .rect = args.rect, | ||||
|         .color = m_or(args, color, ((t_color) { 255, 255, 255, 255 })), | ||||
|         .color = m_or(args, color, ((Color) { 255, 255, 255, 255 })), | ||||
|         .rotation = m_or(args, rotation, 0.0f), | ||||
|         .texture_key = textures_get_key(&ctx.texture_cache, args.path), | ||||
|         .flip_x = m_or(args, flip_x, false), | ||||
| @@ -29,7 +29,7 @@ void push_sprite(const t_push_sprite_args args) { | ||||
|         m_opt_from(texture_region, args, texture_region) | ||||
|     }; | ||||
|  | ||||
|     struct primitive_2d primitive = { | ||||
|     Primitive2D primitive = { | ||||
|         .type = PRIMITIVE_2D_SPRITE, | ||||
|         .sprite = sprite, | ||||
|     }; | ||||
| @@ -38,12 +38,12 @@ void push_sprite(const t_push_sprite_args args) { | ||||
| } | ||||
|  | ||||
|  | ||||
| struct sprite_batch collect_sprite_batch(const struct primitive_2d primitives[], size_t len) { | ||||
| struct SpriteBatch collect_sprite_batch(const Primitive2D primitives[], size_t len) { | ||||
|     /* assumes that first primitive is already a sprite */ | ||||
|     const uint16_t texture_key_id = primitives[0].sprite.texture_key.id; | ||||
|     const int atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key); | ||||
|  | ||||
|     struct sprite_batch batch = { | ||||
|     struct SpriteBatch batch = { | ||||
|         .mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key), | ||||
|         .constant_colored = true, | ||||
|         .repeat = primitives[0].sprite.repeat, | ||||
| @@ -56,14 +56,14 @@ struct sprite_batch collect_sprite_batch(const struct primitive_2d primitives[], | ||||
|         len = QUAD_ELEMENT_BUFFER_LENGTH; | ||||
|  | ||||
|     for (size_t i = 0; i < len; ++i) { | ||||
|         const struct primitive_2d *const current = &primitives[i]; | ||||
|         const Primitive2D *const current = &primitives[i]; | ||||
|  | ||||
|         /* don't touch things other than sprites */ | ||||
|         if (current->type != PRIMITIVE_2D_SPRITE) | ||||
|             break; | ||||
|  | ||||
|         /* only collect the same blend modes */ | ||||
|         const enum texture_mode mode = textures_get_mode(&ctx.texture_cache, current->sprite.texture_key); | ||||
|         const TextureMode mode = textures_get_mode(&ctx.texture_cache, current->sprite.texture_key); | ||||
|         if (mode != batch.mode) | ||||
|             break; | ||||
|  | ||||
| @@ -94,32 +94,32 @@ struct sprite_batch collect_sprite_batch(const struct primitive_2d primitives[], | ||||
|  | ||||
|  | ||||
| /* assumes that orthogonal matrix setup is done already */ | ||||
| void render_sprites(const struct primitive_2d primitives[], | ||||
|                            const struct sprite_batch batch) | ||||
| void render_sprites(const Primitive2D primitives[], | ||||
|                     const struct SpriteBatch batch) | ||||
| { | ||||
|     /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ | ||||
|     static vertex_buffer vertex_array = 0; | ||||
|     static VertexBuffer vertex_array = 0; | ||||
|     if (vertex_array == 0) | ||||
|         vertex_array = create_vertex_buffer(); | ||||
|  | ||||
|     use_texture_mode(batch.mode); | ||||
|  | ||||
|     const t_frect dims = | ||||
|     const Rect dims = | ||||
|         textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key); | ||||
|  | ||||
|     /* vertex population over a vertex buffer builder interface */ | ||||
|     { | ||||
|         vertex_buffer_builder payload = build_vertex_buffer(vertex_array, get_sprite_payload_size(batch) * batch.size); | ||||
|         VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_sprite_payload_size(batch) * batch.size); | ||||
|  | ||||
|         for (size_t i = 0; i < batch.size; ++i) { | ||||
|             /* render opaques front to back */ | ||||
|             const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1; | ||||
|             const struct sprite_primitive sprite = primitives[cur].sprite; | ||||
|             const SpritePrimitive sprite = primitives[cur].sprite; | ||||
|  | ||||
|             const t_frect srcrect = | ||||
|             const Rect srcrect = | ||||
|                 textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key); | ||||
|  | ||||
|             t_fvec2 uv0, uv1, uv2, uv3; | ||||
|             Vec2 uv0, uv1, uv2, uv3; | ||||
|  | ||||
|             if (!sprite.repeat) { | ||||
|                 const float wr = srcrect.w / dims.w; | ||||
| @@ -128,20 +128,20 @@ void render_sprites(const struct primitive_2d primitives[], | ||||
|                 const float yr = srcrect.y / dims.h; | ||||
|  | ||||
|                 if (!m_is_set(sprite, texture_region)) { | ||||
|                     uv0 = (t_fvec2){ xr + wr *  sprite.flip_x, yr + hr *  sprite.flip_y }; | ||||
|                     uv1 = (t_fvec2){ xr + wr *  sprite.flip_x, yr + hr * !sprite.flip_y }; | ||||
|                     uv2 = (t_fvec2){ xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y }; | ||||
|                     uv3 = (t_fvec2){ xr + wr * !sprite.flip_x, yr + hr *  sprite.flip_y }; | ||||
|                     uv0 = (Vec2){ xr + wr *  sprite.flip_x, yr + hr *  sprite.flip_y }; | ||||
|                     uv1 = (Vec2){ xr + wr *  sprite.flip_x, yr + hr * !sprite.flip_y }; | ||||
|                     uv2 = (Vec2){ xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y }; | ||||
|                     uv3 = (Vec2){ xr + wr * !sprite.flip_x, yr + hr *  sprite.flip_y }; | ||||
|                 } else { | ||||
|                     const float offx = (sprite.texture_region_opt.x / srcrect.w) * (srcrect.w / dims.w); | ||||
|                     const float offy = (sprite.texture_region_opt.y / srcrect.h) * (srcrect.h / dims.h); | ||||
|                     const float offw = (1.0f - (sprite.texture_region_opt.w / srcrect.w)) * (srcrect.w / dims.w); | ||||
|                     const float offh = (1.0f - (sprite.texture_region_opt.h / srcrect.h)) * (srcrect.h / dims.h); | ||||
|  | ||||
|                     uv0 = (t_fvec2){ xr + offx + wr *  sprite.flip_x, yr + offy + hr *  sprite.flip_y }; | ||||
|                     uv1 = (t_fvec2){ xr + offx + wr *  sprite.flip_x, yr - offh + hr * !sprite.flip_y }; | ||||
|                     uv2 = (t_fvec2){ xr - offw + wr * !sprite.flip_x, yr - offh + hr * !sprite.flip_y }; | ||||
|                     uv3 = (t_fvec2){ xr - offw + wr * !sprite.flip_x, yr + offy + hr *  sprite.flip_y }; | ||||
|                     uv0 = (Vec2){ xr + offx + wr *  sprite.flip_x, yr + offy + hr *  sprite.flip_y }; | ||||
|                     uv1 = (Vec2){ xr + offx + wr *  sprite.flip_x, yr - offh + hr * !sprite.flip_y }; | ||||
|                     uv2 = (Vec2){ xr - offw + wr * !sprite.flip_x, yr - offh + hr * !sprite.flip_y }; | ||||
|                     uv3 = (Vec2){ xr - offw + wr * !sprite.flip_x, yr + offy + hr *  sprite.flip_y }; | ||||
|                 } | ||||
|             } else { | ||||
|                 /* try fitting texture into supplied destination rectangle */ | ||||
| @@ -149,10 +149,10 @@ void render_sprites(const struct primitive_2d primitives[], | ||||
|                 const float rx = sprite.rect.w / srcrect.w; | ||||
|                 const float ry = sprite.rect.h / srcrect.h; | ||||
|  | ||||
|                 uv0 = (t_fvec2){ rx *  sprite.flip_x, ry *  sprite.flip_y }; | ||||
|                 uv1 = (t_fvec2){ rx *  sprite.flip_x, ry * !sprite.flip_y }; | ||||
|                 uv2 = (t_fvec2){ rx * !sprite.flip_x, ry * !sprite.flip_y }; | ||||
|                 uv3 = (t_fvec2){ rx * !sprite.flip_x, ry *  sprite.flip_y }; | ||||
|                 uv0 = (Vec2){ rx *  sprite.flip_x, ry *  sprite.flip_y }; | ||||
|                 uv1 = (Vec2){ rx *  sprite.flip_x, ry * !sprite.flip_y }; | ||||
|                 uv2 = (Vec2){ rx * !sprite.flip_x, ry * !sprite.flip_y }; | ||||
|                 uv3 = (Vec2){ rx * !sprite.flip_x, ry *  sprite.flip_y }; | ||||
|  | ||||
|                 if (m_is_set(sprite, texture_region)) { | ||||
|                     /* displace origin */ | ||||
| @@ -165,44 +165,44 @@ void render_sprites(const struct primitive_2d primitives[], | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             t_fvec2 v0, v1, v2, v3; | ||||
|             Vec2 v0, v1, v2, v3; | ||||
|  | ||||
|             /* todo: fast PI/2 degree divisible rotations? */ | ||||
|             if (sprite.rotation == 0.0f) { | ||||
|                 /* non-rotated case */ | ||||
|  | ||||
|                 v0 = (t_fvec2){ sprite.rect.x,                 sprite.rect.y }; | ||||
|                 v1 = (t_fvec2){ sprite.rect.x,                 sprite.rect.y + sprite.rect.h }; | ||||
|                 v2 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h }; | ||||
|                 v3 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y }; | ||||
|                 v0 = (Vec2){ sprite.rect.x,                 sprite.rect.y }; | ||||
|                 v1 = (Vec2){ sprite.rect.x,                 sprite.rect.y + sprite.rect.h }; | ||||
|                 v2 = (Vec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h }; | ||||
|                 v3 = (Vec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y }; | ||||
|  | ||||
|             } else if (sprite.rect.w == sprite.rect.h) { | ||||
|                 /* rotated square case */ | ||||
|  | ||||
|                 const t_fvec2 c = frect_center(sprite.rect); | ||||
|                 const t_fvec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); | ||||
|                 const t_fvec2 d = { | ||||
|                 const Vec2 c = frect_center(sprite.rect); | ||||
|                 const Vec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); | ||||
|                 const Vec2 d = { | ||||
|                     .x = t.x * sprite.rect.w * (float)M_SQRT1_2, | ||||
|                     .y = t.y * sprite.rect.h * (float)M_SQRT1_2, | ||||
|                 }; | ||||
|  | ||||
|                 v0 = (t_fvec2){ c.x - d.x, c.y - d.y }; | ||||
|                 v1 = (t_fvec2){ c.x - d.y, c.y + d.x }; | ||||
|                 v2 = (t_fvec2){ c.x + d.x, c.y + d.y }; | ||||
|                 v3 = (t_fvec2){ c.x + d.y, c.y - d.x }; | ||||
|                 v0 = (Vec2){ c.x - d.x, c.y - d.y }; | ||||
|                 v1 = (Vec2){ c.x - d.y, c.y + d.x }; | ||||
|                 v2 = (Vec2){ c.x + d.x, c.y + d.y }; | ||||
|                 v3 = (Vec2){ c.x + d.y, c.y - d.x }; | ||||
|  | ||||
|             } else { | ||||
|                 /* rotated non-square case*/ | ||||
|  | ||||
|                 const t_fvec2 c = frect_center(sprite.rect); | ||||
|                 const t_fvec2 t = fast_cossine(sprite.rotation); | ||||
|                 const Vec2 c = frect_center(sprite.rect); | ||||
|                 const Vec2 t = fast_cossine(sprite.rotation); | ||||
|  | ||||
|                 const t_fvec2 h = { sprite.rect.w / 2, sprite.rect.h / 2 }; | ||||
|                 const Vec2 h = { sprite.rect.w / 2, sprite.rect.h / 2 }; | ||||
|  | ||||
|                 v0 = (t_fvec2){ c.x + t.x * -h.x - t.y * -h.y, c.y + t.y * -h.x + t.x * -h.y }; | ||||
|                 v1 = (t_fvec2){ c.x + t.x * -h.x - t.y * +h.y, c.y + t.y * -h.x + t.x * +h.y }; | ||||
|                 v2 = (t_fvec2){ c.x + t.x * +h.x - t.y * +h.y, c.y + t.y * +h.x + t.x * +h.y }; | ||||
|                 v3 = (t_fvec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y }; | ||||
|                 v0 = (Vec2){ c.x + t.x * -h.x - t.y * -h.y, c.y + t.y * -h.x + t.x * -h.y }; | ||||
|                 v1 = (Vec2){ c.x + t.x * -h.x - t.y * +h.y, c.y + t.y * -h.x + t.x * +h.y }; | ||||
|                 v2 = (Vec2){ c.x + t.x * +h.x - t.y * +h.y, c.y + t.y * +h.x + t.x * +h.y }; | ||||
|                 v3 = (Vec2){ c.x + t.x * +h.x - t.y * -h.y, c.y + t.y * +h.x + t.x * -h.y }; | ||||
|             } | ||||
|  | ||||
|             push_sprite_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color); | ||||
|   | ||||
| @@ -12,26 +12,8 @@ | ||||
| #define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1) | ||||
|  | ||||
|  | ||||
| struct font_data { | ||||
|     stbtt_packedchar char_data[NUM_DISPLAY_ASCII]; | ||||
|     stbtt_fontinfo info; | ||||
|  | ||||
|     const char *file_path; | ||||
|     unsigned char *file_bytes; | ||||
|     size_t file_bytes_len; | ||||
|  | ||||
|     gpu_texture texture; | ||||
|  | ||||
|     int height_px; | ||||
|     float scale_factor; | ||||
|     int ascent; | ||||
|     int descent; | ||||
|     int line_gap; | ||||
| }; | ||||
|  | ||||
|  | ||||
| static struct font_data *text_load_font_data(const char *path, int height_px) { | ||||
|     struct font_data *font_data = ccalloc(1, sizeof *font_data); | ||||
| static FontData *text_load_font_data(const char *path, int height_px) { | ||||
|     FontData *font_data = ccalloc(1, sizeof *font_data); | ||||
|     font_data->file_path = path; | ||||
|     font_data->height_px = height_px; | ||||
|  | ||||
| @@ -77,21 +59,21 @@ static struct font_data *text_load_font_data(const char *path, int height_px) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void text_destroy_font_data(struct font_data *font_data) { | ||||
| static void text_destroy_font_data(FontData *font_data) { | ||||
|     free(font_data->file_bytes); | ||||
|     delete_gpu_texture(font_data->texture); | ||||
|     free(font_data); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 position, t_color color) { | ||||
|     static vertex_buffer vertex_array = 0; | ||||
| static void text_draw_with(FontData* font_data, char* text, Vec2 position, Color color) { | ||||
|     VertexBuffer vertex_array = 0; | ||||
|     if (vertex_array == 0) | ||||
|         vertex_array = create_vertex_buffer(); | ||||
|  | ||||
|     const size_t len = SDL_strlen(text); | ||||
|  | ||||
|     vertex_buffer_builder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len); | ||||
|     VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len); | ||||
|  | ||||
|     for (size_t i = 0; i < len; ++i) { | ||||
|         const char c = text[i]; | ||||
| @@ -131,21 +113,21 @@ static void ensure_font_cache(const char *font_path, int height_px) { | ||||
|     /* HACK: stupid, bad, don't do this */ | ||||
|     bool is_cached = false; | ||||
|     for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { | ||||
|         struct font_data *font_data = ctx.text_cache.data[i]; | ||||
|         FontData *font_data = ctx.text_cache.data[i]; | ||||
|         if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) { | ||||
|             is_cached = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (!is_cached) { | ||||
|         struct font_data *new_font_data = text_load_font_data(font_path, height_px); | ||||
|         FontData *new_font_data = text_load_font_data(font_path, height_px); | ||||
|         arrput(ctx.text_cache.data, new_font_data); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| static struct font_data *get_font_data(const char *font_path, int height_px) { | ||||
|     struct font_data *font_data = NULL; | ||||
| static FontData *get_font_data(const char *font_path, int height_px) { | ||||
|     FontData *font_data = NULL; | ||||
|     for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { | ||||
|         font_data = ctx.text_cache.data[i]; | ||||
|         if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) { | ||||
| @@ -156,18 +138,18 @@ static struct font_data *get_font_data(const char *font_path, int height_px) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void render_text(const struct text_primitive *text) { | ||||
|     struct font_data *font_data = get_font_data(text->font, text->height_px); | ||||
| void render_text(const TextPrimitive *text) { | ||||
|     FontData *font_data = get_font_data(text->font, text->height_px); | ||||
|     text_draw_with(font_data, text->text, text->position, text->color); | ||||
| } | ||||
|  | ||||
|  | ||||
| void text_cache_init(struct text_cache *cache) { | ||||
| void text_cache_init(TextCache *cache) { | ||||
|     arrsetlen(cache->data, 0); | ||||
| } | ||||
|  | ||||
|  | ||||
| void text_cache_deinit(struct text_cache *cache) { | ||||
| void text_cache_deinit(TextCache *cache) { | ||||
|     for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { | ||||
|         text_destroy_font_data(ctx.text_cache.data[i]); | ||||
|     } | ||||
| @@ -176,7 +158,7 @@ void text_cache_deinit(struct text_cache *cache) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path) { | ||||
| void push_text(char *string, Vec2 position, int height_px, Color color, const char *font_path) { | ||||
|     ensure_font_cache(font_path, height_px); | ||||
|  | ||||
|     /* the string might not be around by the time it's used, so copy it */ | ||||
| @@ -185,7 +167,7 @@ void push_text(char *string, t_fvec2 position, int height_px, t_color color, con | ||||
|     char *dup_string = cmalloc(strlen(string) + 1); | ||||
|     strcpy(dup_string, string); | ||||
|  | ||||
|     struct text_primitive text = { | ||||
|     TextPrimitive text = { | ||||
|         .color = color, | ||||
|         .position = position, | ||||
|         .text = dup_string, | ||||
| @@ -193,7 +175,7 @@ void push_text(char *string, t_fvec2 position, int height_px, t_color color, con | ||||
|         .height_px = height_px, | ||||
|     }; | ||||
|  | ||||
|     struct primitive_2d primitive = { | ||||
|     Primitive2D primitive = { | ||||
|         .type = PRIMITIVE_2D_TEXT, | ||||
|         .text = text, | ||||
|     }; | ||||
| @@ -204,7 +186,7 @@ void push_text(char *string, t_fvec2 position, int height_px, t_color color, con | ||||
|  | ||||
| int get_text_width(char *string, int height_px, const char *font_path) { | ||||
|     ensure_font_cache(font_path, height_px); | ||||
|     struct font_data *font_data = get_font_data(font_path, height_px); | ||||
|     FontData *font_data = get_font_data(font_path, height_px); | ||||
|  | ||||
|     int length = 0; | ||||
|     for (const char *p = string; *p != '\0'; ++p) { | ||||
|   | ||||
| @@ -1,3 +1,10 @@ | ||||
| #ifndef TWN_TEXT_C_H | ||||
| #define TWN_TEXT_C_H | ||||
|  | ||||
|  | ||||
| #include "twn_gpu_texture_c.h" | ||||
|  | ||||
|  | ||||
| #include <stb_truetype.h> | ||||
|  | ||||
|  | ||||
| @@ -6,7 +13,7 @@ | ||||
| #define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1) | ||||
|  | ||||
|  | ||||
| struct font_data { | ||||
| typedef struct FontData { | ||||
|     stbtt_packedchar char_data[NUM_DISPLAY_ASCII]; | ||||
|     stbtt_fontinfo info; | ||||
|  | ||||
| @@ -14,11 +21,14 @@ struct font_data { | ||||
|     unsigned char *file_bytes; | ||||
|     size_t file_bytes_len; | ||||
|  | ||||
|     gpu_texture texture; | ||||
|     GPUTexture texture; | ||||
|  | ||||
|     int height_px; | ||||
|     float scale_factor; | ||||
|     int ascent; | ||||
|     int descent; | ||||
|     int line_gap; | ||||
| }; | ||||
| } FontData; | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -9,42 +9,42 @@ | ||||
| /* TODO: automatic handling of repeating textures */ | ||||
| /*       for that we could allocate a loner texture */ | ||||
| void unfurl_triangle(const char *path, | ||||
|                      t_fvec3 v0, | ||||
|                      t_fvec3 v1, | ||||
|                      t_fvec3 v2, | ||||
|                      t_shvec2 uv0, | ||||
|                      t_shvec2 uv1, | ||||
|                      t_shvec2 uv2) | ||||
|                      Vec3 v0, | ||||
|                      Vec3 v1, | ||||
|                      Vec3 v2, | ||||
|                      Vec2sh uv0, | ||||
|                      Vec2sh uv1, | ||||
|                      Vec2sh uv2) | ||||
| { | ||||
|     const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path); | ||||
|     const TextureKey texture_key = textures_get_key(&ctx.texture_cache, path); | ||||
|  | ||||
|     struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); | ||||
|     struct MeshBatchItem *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); | ||||
|     if (!batch_p) { | ||||
|         struct mesh_batch item = {0}; | ||||
|         struct MeshBatch item = {0}; | ||||
|         hmput(ctx.uncolored_mesh_batches, texture_key, item); | ||||
|         batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */ | ||||
|     } | ||||
|  | ||||
|     union uncolored_space_triangle triangle = { .primitive = { | ||||
|     union UncoloredSpaceTriangle triangle = { .primitive = { | ||||
|         .v0 = v0, | ||||
|         .v1 = v1, | ||||
|         .v2 = v2, | ||||
|         .uv1 = m_to_fvec2(uv1), | ||||
|         .uv0 = m_to_fvec2(uv0), | ||||
|         .uv2 = m_to_fvec2(uv2), | ||||
|         .uv1 = m_vec2_from(uv1), | ||||
|         .uv0 = m_vec2_from(uv0), | ||||
|         .uv2 = m_vec2_from(uv2), | ||||
|     }}; | ||||
|  | ||||
|     union uncolored_space_triangle *triangles = (union uncolored_space_triangle *)batch_p->value.primitives; | ||||
|     union UncoloredSpaceTriangle *triangles = (union UncoloredSpaceTriangle *)batch_p->value.primitives; | ||||
|  | ||||
|     arrpush(triangles, triangle); | ||||
|     batch_p->value.primitives = (uint8_t *)triangles; | ||||
| } | ||||
|  | ||||
|  | ||||
| void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, | ||||
|                                          t_texture_key texture_key) | ||||
| void draw_uncolored_space_traingle_batch(struct MeshBatch *batch, | ||||
|                                          TextureKey texture_key) | ||||
| { | ||||
|     static vertex_buffer vertex_array = 0; | ||||
|     static VertexBuffer vertex_array = 0; | ||||
|     if (vertex_array == 0) | ||||
|         vertex_array = create_vertex_buffer(); | ||||
|  | ||||
| @@ -54,8 +54,8 @@ void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, | ||||
|     if (primitives_len == 0) | ||||
|         return; | ||||
|  | ||||
|     const t_frect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); | ||||
|     const t_frect dims    = textures_get_dims(&ctx.texture_cache, texture_key); | ||||
|     const Rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); | ||||
|     const Rect dims    = textures_get_dims(&ctx.texture_cache, texture_key); | ||||
|  | ||||
|     const float wr = srcrect.w / dims.w; | ||||
|     const float hr = srcrect.h / dims.h; | ||||
| @@ -64,8 +64,8 @@ void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, | ||||
|  | ||||
|     /* update pixel-based uvs to correspond with texture atlases */ | ||||
|     for (size_t i = 0; i < primitives_len; ++i) { | ||||
|         struct uncolored_space_triangle_payload *payload = | ||||
|             &((union uncolored_space_triangle *)batch->primitives)[i].payload; | ||||
|         struct UncoloredSpaceTrianglePayload *payload = | ||||
|             &((union UncoloredSpaceTriangle *)batch->primitives)[i].payload; | ||||
|  | ||||
|         payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr; | ||||
|         payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr; | ||||
| @@ -75,7 +75,7 @@ void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, | ||||
|         payload->uv2.y = yr + ((float)payload->uv2.y / srcrect.h) * hr; | ||||
|     } | ||||
|  | ||||
|     specify_vertex_buffer(vertex_array, batch->primitives, primitives_len * sizeof (struct uncolored_space_triangle_payload)); | ||||
|     specify_vertex_buffer(vertex_array, batch->primitives, primitives_len * sizeof (struct UncoloredSpaceTrianglePayload)); | ||||
|  | ||||
|     finally_draw_uncolored_space_traingle_batch(batch, texture_key, vertex_array); | ||||
| } | ||||
|   | ||||
| @@ -24,9 +24,9 @@ static const char *audio_exts[audio_file_type_count] = { | ||||
| /* TODO: count frames without use, free the memory when threshold is met */ | ||||
| /* TODO: count repeated usages for sound effect cases with rendering to ram? */ | ||||
| /* stores path to data hash, useful for sound effects */ | ||||
| static struct audio_file_cache { | ||||
| static struct AudioFileCache { | ||||
|     char *key; | ||||
|     struct audio_file_cache_value { | ||||
|     struct AudioFileCacheValue { | ||||
|         unsigned char *data; | ||||
|         size_t len; | ||||
|     } value; | ||||
| @@ -34,7 +34,7 @@ static struct audio_file_cache { | ||||
|  | ||||
|  | ||||
| static int64_t get_audio_data(const char *path, unsigned char **data) { | ||||
|     const struct audio_file_cache *cache = shgetp_null(audio_file_cache, path); | ||||
|     const struct AudioFileCache *cache = shgetp_null(audio_file_cache, path); | ||||
|     if (!cache) { | ||||
|         unsigned char *file; | ||||
|         int64_t len = file_to_bytes(path, &file); | ||||
| @@ -43,7 +43,7 @@ static int64_t get_audio_data(const char *path, unsigned char **data) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         const struct audio_file_cache_value value = { file, (size_t)len }; | ||||
|         const struct AudioFileCacheValue value = { file, (size_t)len }; | ||||
|         shput(audio_file_cache, path, value); | ||||
|  | ||||
|         *data = file; | ||||
| @@ -55,16 +55,16 @@ static int64_t get_audio_data(const char *path, unsigned char **data) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void play_audio(const char *path, const char *channel) { | ||||
|     const struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); | ||||
| void audio_play(const char *path, const char *channel) { | ||||
|     const AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel); | ||||
|     if (!pair) | ||||
|         play_audio_ex(path, channel, get_default_audio_args()); | ||||
|         audio_play_ex(path, channel, audio_get_default_args()); | ||||
|     else | ||||
|         play_audio_ex(path, channel, pair->value.args); | ||||
|         audio_play_ex(path, channel, pair->value.args); | ||||
| } | ||||
|  | ||||
|  | ||||
| static t_audio_file_type infer_audio_file_type(const char *path) { | ||||
| static AudioFileType infer_audio_file_type(const char *path) { | ||||
|     size_t path_len = strlen(path); | ||||
|  | ||||
|     for (int i = 0; i < audio_file_type_count; ++i) { | ||||
| @@ -73,7 +73,7 @@ static t_audio_file_type infer_audio_file_type(const char *path) { | ||||
|             continue; | ||||
|  | ||||
|         if (strcmp(&path[path_len - ext_length], audio_exts[i]) == 0) | ||||
|             return (t_audio_file_type)i; | ||||
|             return (AudioFileType)i; | ||||
|     } | ||||
|  | ||||
|     return audio_file_type_unknown; | ||||
| @@ -82,7 +82,7 @@ static t_audio_file_type infer_audio_file_type(const char *path) { | ||||
|  | ||||
| /* TODO: error propagation and clearing of resources on partial success? */ | ||||
| /*       or should we expect things to simply fail? */ | ||||
| static union audio_context init_audio_context(const char *path, t_audio_file_type type) { | ||||
| static union AudioContext init_audio_context(const char *path, AudioFileType type) { | ||||
|     switch (type) { | ||||
|     case audio_file_type_ogg: { | ||||
|         unsigned char *data; | ||||
| @@ -101,7 +101,7 @@ static union audio_context init_audio_context(const char *path, t_audio_file_typ | ||||
|  | ||||
|         stb_vorbis_info info = stb_vorbis_get_info(handle); | ||||
|  | ||||
|         return (union audio_context) { | ||||
|         return (union AudioContext) { | ||||
|             .vorbis = { | ||||
|                 .data = data, | ||||
|                 .handle = handle, | ||||
| @@ -131,21 +131,21 @@ static union audio_context init_audio_context(const char *path, t_audio_file_typ | ||||
|  | ||||
|         xm_set_max_loop_count(handle, 1); | ||||
|  | ||||
|         return (union audio_context) { | ||||
|         return (union AudioContext) { | ||||
|             .xm = { .handle = handle } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     default: | ||||
|         CRY("Audio error", "Unhandled audio format (in init)"); | ||||
|         return (union audio_context){0}; | ||||
|         return (union AudioContext){0}; | ||||
|     } | ||||
|  | ||||
|     return (union audio_context){0}; | ||||
|     return (union AudioContext){0}; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void repeat_audio(struct audio_channel *channel) { | ||||
| static void repeat_audio(AudioChannel *channel) { | ||||
|     switch (channel->file_type) { | ||||
|     case audio_file_type_ogg: { | ||||
|         stb_vorbis_seek_start(channel->context.vorbis.handle); | ||||
| @@ -164,13 +164,13 @@ static void repeat_audio(struct audio_channel *channel) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void play_audio_ex(const char *path, const char *channel, t_play_audio_args args) { | ||||
|     struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); | ||||
| void audio_play_ex(const char *path, const char *channel, PlayAudioArgs args) { | ||||
|     AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel); | ||||
|  | ||||
|     /* create a channel if it doesn't exist */ | ||||
|     if (!pair) { | ||||
|         t_audio_file_type file_type = infer_audio_file_type(path); | ||||
|         struct audio_channel new_channel = { | ||||
|         AudioFileType file_type = infer_audio_file_type(path); | ||||
|         AudioChannel new_channel = { | ||||
|             .args = args, | ||||
|             .file_type = file_type, | ||||
|             .context = init_audio_context(path, file_type), | ||||
| @@ -189,8 +189,8 @@ void play_audio_ex(const char *path, const char *channel, t_play_audio_args args | ||||
| } | ||||
|  | ||||
|  | ||||
| t_play_audio_args *get_audio_args(const char *channel) { | ||||
|     struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); | ||||
| PlayAudioArgs *audio_get_args(const char *channel) { | ||||
|     AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel); | ||||
|     if (!pair) | ||||
|         return NULL; | ||||
|  | ||||
| @@ -198,8 +198,8 @@ t_play_audio_args *get_audio_args(const char *channel) { | ||||
| } | ||||
|  | ||||
|  | ||||
| t_play_audio_args get_default_audio_args(void) { | ||||
|     return (t_play_audio_args){ | ||||
| PlayAudioArgs audio_get_default_args(void) { | ||||
|     return (PlayAudioArgs){ | ||||
|         .repeat = false, | ||||
|         .crossfade = false, | ||||
|         .volume = 1.0f, | ||||
| @@ -209,7 +209,7 @@ t_play_audio_args get_default_audio_args(void) { | ||||
|  | ||||
|  | ||||
| /* this assumes int16_t based streams */ | ||||
| static void audio_mixin_streams(const struct audio_channel *channel, | ||||
| static void audio_mixin_streams(const AudioChannel *channel, | ||||
|                                 uint8_t *restrict a, | ||||
|                                 uint8_t *restrict b, | ||||
|                                 size_t frames) | ||||
| @@ -236,7 +236,7 @@ static void audio_mixin_streams(const struct audio_channel *channel, | ||||
|  | ||||
|  | ||||
| /* remember: sample is data for all channels where frame is a part of it */ | ||||
| static void audio_sample_and_mixin_channel(const struct audio_channel *channel, | ||||
| static void audio_sample_and_mixin_channel(const AudioChannel *channel, | ||||
|                                            uint8_t *stream, | ||||
|                                            int len) | ||||
| { | ||||
| @@ -323,7 +323,7 @@ static void audio_sample_and_mixin_channel(const struct audio_channel *channel, | ||||
| } | ||||
|  | ||||
|  | ||||
| static void sanity_check_channel(const struct audio_channel *channel) { | ||||
| static void sanity_check_channel(const AudioChannel *channel) { | ||||
|     if (channel->args.volume < 0.0f || channel->args.volume > 1.0f) | ||||
|         log_warn("Volume argument is out of range for channel (%s)", channel->name); | ||||
|  | ||||
|   | ||||
| @@ -13,15 +13,15 @@ | ||||
| #include <stdint.h> | ||||
|  | ||||
|  | ||||
| typedef enum audio_file_type { | ||||
| typedef enum AudioFileType { | ||||
|     audio_file_type_ogg, | ||||
|     audio_file_type_xm, | ||||
|     audio_file_type_count, | ||||
|     audio_file_type_unknown, | ||||
| } t_audio_file_type; | ||||
| } AudioFileType; | ||||
|  | ||||
|  | ||||
| union audio_context { | ||||
| union AudioContext { | ||||
|     struct { | ||||
|         stb_vorbis *handle; | ||||
|         unsigned char *data; | ||||
| @@ -35,19 +35,19 @@ union audio_context { | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct audio_channel { | ||||
|     t_play_audio_args args; | ||||
|     enum audio_file_type file_type; | ||||
|     union audio_context context; /* interpreted by `file_type` value */ | ||||
| typedef struct AudioChannel { | ||||
|     PlayAudioArgs args; | ||||
|     AudioFileType file_type; | ||||
|     union AudioContext context; /* interpreted by `file_type` value */ | ||||
|     const char *path; | ||||
|     const char *name; | ||||
| }; | ||||
| } AudioChannel; | ||||
|  | ||||
|  | ||||
| struct audio_channel_item { | ||||
| typedef struct AudioChannelItem { | ||||
|     char *key; | ||||
|     struct audio_channel value; | ||||
| }; | ||||
|     struct AudioChannel value; | ||||
| } AudioChannelItem; | ||||
|  | ||||
|  | ||||
| void audio_callback(void *userdata, uint8_t *stream, int len); | ||||
|   | ||||
| @@ -8,12 +8,12 @@ | ||||
| #define CAMERA_FAR_Z 100.0f | ||||
|  | ||||
|  | ||||
| t_matrix4 camera_look_at(const t_camera *const camera) { | ||||
| Matrix4 camera_look_at(const Camera *const camera) { | ||||
|     /* from cglm */ | ||||
|     const t_fvec3 r = m_vec_norm(m_vec_cross(camera->target, camera->up)); | ||||
|     const t_fvec3 u = m_vec_cross(r, camera->target); | ||||
|     const Vec3 r = m_vec_norm(m_vec_cross(camera->target, camera->up)); | ||||
|     const Vec3 u = m_vec_cross(r, camera->target); | ||||
|  | ||||
|     t_matrix4 result; | ||||
|     Matrix4 result; | ||||
|  | ||||
|     result.row[0].x =  r.x; | ||||
|     result.row[0].y =  u.x; | ||||
| @@ -33,9 +33,9 @@ t_matrix4 camera_look_at(const t_camera *const camera) { | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| t_matrix4 camera_perspective(const t_camera *const camera) { | ||||
| Matrix4 camera_perspective(const Camera *const camera) { | ||||
|     /* from cglm */ | ||||
|     t_matrix4 result = {0}; | ||||
|     Matrix4 result = {0}; | ||||
|  | ||||
|     const float aspect = RENDER_BASE_RATIO; | ||||
|  | ||||
|   | ||||
| @@ -2,4 +2,4 @@ | ||||
|  | ||||
| /* internally there's only one context, but it gets type casted to game and engine variants based on which header is included */ | ||||
| /* engine parts should never mix twn_engine_context_c.h with twn_context.h */ | ||||
| t_engine_ctx ctx = {0}; | ||||
| EngineContext ctx = {0}; | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|  | ||||
| #include "twn_context.h" | ||||
| #include "twn_textures_c.h" | ||||
| #include "twn_audio_c.h" | ||||
| #include "twn_engine_api.h" | ||||
| #include "twn_input.h" | ||||
| #include "rendering/twn_rendering_c.h" | ||||
| @@ -13,20 +14,20 @@ | ||||
| #include <stdint.h> | ||||
|  | ||||
|  | ||||
| typedef struct engine_context { | ||||
| typedef struct EngineContext { | ||||
|     /* user code facing context */ | ||||
|     t_ctx game; | ||||
|     Context game; | ||||
|  | ||||
|     /* the program's actual argc and argv */ | ||||
|     int argc; | ||||
|     char **argv; | ||||
|  | ||||
|     struct primitive_2d *render_queue_2d; | ||||
|     struct mesh_batch_item *uncolored_mesh_batches; | ||||
|     struct text_cache text_cache; | ||||
|     struct texture_cache texture_cache; | ||||
|     struct Primitive2D *render_queue_2d; | ||||
|     MeshBatchItem *uncolored_mesh_batches; | ||||
|     struct TextCache text_cache; | ||||
|     TextureCache texture_cache; | ||||
|  | ||||
|     struct audio_channel_item *audio_channels; | ||||
|     AudioChannelItem *audio_channels; | ||||
|     SDL_AudioDeviceID audio_device; | ||||
|     int audio_stream_frequency; | ||||
|     SDL_AudioFormat audio_stream_format; | ||||
| @@ -46,8 +47,8 @@ typedef struct engine_context { | ||||
|  | ||||
|     bool resync_flag; | ||||
|     bool was_successful; | ||||
| } t_engine_ctx; | ||||
| } EngineContext; | ||||
|  | ||||
| TWN_API extern t_engine_ctx ctx; | ||||
| TWN_API extern EngineContext ctx; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
| static void update_action_pressed_state(struct input_state *input, struct action *action) { | ||||
| static void update_action_pressed_state(InputState *input, Action *action) { | ||||
|     for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) { | ||||
|         switch (action->bindings[i].source) { | ||||
|         case BUTTON_SOURCE_NOT_SET: | ||||
| @@ -61,21 +61,21 @@ static void update_action_pressed_state(struct input_state *input, struct action | ||||
| } | ||||
|  | ||||
|  | ||||
| static void input_bind_code_to_action(struct input_state *input, | ||||
| static void input_bind_code_to_action(InputState *input, | ||||
|                                       char *action_name, | ||||
|                                       enum button_source source, | ||||
|                                       union button_code code) | ||||
|                                       ButtonSource source, | ||||
|                                       union ButtonCode code) | ||||
| { | ||||
|     struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name); | ||||
|     ActionHashItem *action_item = shgetp_null(input->action_hash, action_name); | ||||
|     if (action_item == NULL) { | ||||
|         log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); | ||||
|         return; | ||||
|     } | ||||
|     struct action *action = &action_item->value; | ||||
|     Action *action = &action_item->value; | ||||
|  | ||||
|     /* check every binding to make sure this code isn't already bound */ | ||||
|     for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) { | ||||
|         struct button *binding = &action->bindings[i]; | ||||
|         Button *binding = &action->bindings[i]; | ||||
|  | ||||
|         if (binding->source != source) | ||||
|             break; | ||||
| @@ -111,30 +111,30 @@ static void input_bind_code_to_action(struct input_state *input, | ||||
|         memmove(action->bindings, action->bindings + 1, shifted_size); | ||||
|     } | ||||
|  | ||||
|     action->bindings[action->num_bindings++] = (struct button) { | ||||
|     action->bindings[action->num_bindings++] = (Button) { | ||||
|         .source = source, | ||||
|         .code = code, | ||||
|     }; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void input_unbind_code_from_action(struct input_state *input, | ||||
| static void input_unbind_code_from_action(InputState *input, | ||||
|                                           char *action_name, | ||||
|                                           enum button_source source, | ||||
|                                           union button_code code) | ||||
|                                           ButtonSource source, | ||||
|                                           union ButtonCode code) | ||||
| { | ||||
|     struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name); | ||||
|     ActionHashItem *action_item = shgetp_null(input->action_hash, action_name); | ||||
|     if (action_item == NULL) { | ||||
|         log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); | ||||
|         return; | ||||
|     } | ||||
|     struct action *action = &action_item->value; | ||||
|     Action *action = &action_item->value; | ||||
|  | ||||
|     /* check every binding to make sure this code is bound */ | ||||
|     size_t index = 0; | ||||
|     bool is_bound = false; | ||||
|     for (index = 0; index < SDL_arraysize(action->bindings); ++index) { | ||||
|         struct button *binding = &action->bindings[index]; | ||||
|         Button *binding = &action->bindings[index]; | ||||
|  | ||||
|         if (binding->source != source) | ||||
|             continue; | ||||
| @@ -174,17 +174,17 @@ static void input_unbind_code_from_action(struct input_state *input, | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_state_init(struct input_state *input) { | ||||
| void input_state_init(InputState *input) { | ||||
|     sh_new_strdup(input->action_hash); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_state_deinit(struct input_state *input) { | ||||
| void input_state_deinit(InputState *input) { | ||||
|     shfree(input->action_hash); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_state_update(struct input_state *input) { | ||||
| void input_state_update(InputState *input) { | ||||
|     input->keyboard_state = SDL_GetKeyboardState(NULL); | ||||
|     input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x, | ||||
|                                            &input->mouse_window_position.y); | ||||
| @@ -193,74 +193,74 @@ void input_state_update(struct input_state *input) { | ||||
|                               &input->mouse_relative_position.y); | ||||
|  | ||||
|     for (size_t i = 0; i < shlenu(input->action_hash); ++i) { | ||||
|         struct action *action = &input->action_hash[i].value; | ||||
|         Action *action = &input->action_hash[i].value; | ||||
|         update_action_pressed_state(input, action); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_bind_action_scancode(struct input_state *input, | ||||
| void input_bind_action_scancode(InputState *input, | ||||
|                                 char *action_name, | ||||
|                                 SDL_Scancode scancode) | ||||
| { | ||||
|     input_bind_code_to_action(input, | ||||
|                               action_name, | ||||
|                               BUTTON_SOURCE_KEYBOARD_PHYSICAL, | ||||
|                               (union button_code) { .scancode = scancode }); | ||||
|                               (union ButtonCode) { .scancode = scancode }); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_unbind_action_scancode(struct input_state *input, | ||||
| void input_unbind_action_scancode(InputState *input, | ||||
|                                   char *action_name, | ||||
|                                   SDL_Scancode scancode) | ||||
| { | ||||
|     input_unbind_code_from_action(input, | ||||
|                                   action_name, | ||||
|                                   BUTTON_SOURCE_KEYBOARD_PHYSICAL, | ||||
|                                   (union button_code) { .scancode = scancode }); | ||||
|                                   (union ButtonCode) { .scancode = scancode }); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_bind_action_mouse(struct input_state *input, | ||||
| void input_bind_action_mouse(InputState *input, | ||||
|                              char *action_name, | ||||
|                              uint8_t mouse_button) | ||||
| { | ||||
|     input_bind_code_to_action(input, | ||||
|                               action_name, | ||||
|                               BUTTON_SOURCE_MOUSE, | ||||
|                               (union button_code) { .mouse_button = mouse_button}); | ||||
|                               (union ButtonCode) { .mouse_button = mouse_button}); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_unbind_action_mouse(struct input_state *input, | ||||
| void input_unbind_action_mouse(InputState *input, | ||||
|                                char *action_name, | ||||
|                                uint8_t mouse_button) | ||||
| { | ||||
|     input_unbind_code_from_action(input, | ||||
|                                   action_name, | ||||
|                                   BUTTON_SOURCE_MOUSE, | ||||
|                                   (union button_code) { .mouse_button = mouse_button}); | ||||
|                                   (union ButtonCode) { .mouse_button = mouse_button}); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_add_action(struct input_state *input, char *action_name) { | ||||
| void input_add_action(InputState *input, char *action_name) { | ||||
|     if (shgeti(input->action_hash, action_name) >= 0) { | ||||
|         log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     shput(input->action_hash, action_name, (struct action) { 0 }); | ||||
|     shput(input->action_hash, action_name, (Action) { 0 }); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_delete_action(struct input_state *input, char *action_name) { | ||||
| void input_delete_action(InputState *input, char *action_name) { | ||||
|     if (shdel(input->action_hash, action_name) == 0) | ||||
|         log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool input_is_action_pressed(struct input_state *input, char *action_name) { | ||||
|     struct action_hash_item *action = shgetp_null(input->action_hash, action_name); | ||||
| bool input_is_action_pressed(InputState *input, char *action_name) { | ||||
|     ActionHashItem *action = shgetp_null(input->action_hash, action_name); | ||||
|     if (action == NULL) { | ||||
|         log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); | ||||
|         return false; | ||||
| @@ -269,8 +269,8 @@ bool input_is_action_pressed(struct input_state *input, char *action_name) { | ||||
| } | ||||
|  | ||||
|  | ||||
| bool input_is_action_just_pressed(struct input_state *input, char *action_name) { | ||||
|     struct action_hash_item *action = shgetp_null(input->action_hash, action_name); | ||||
| bool input_is_action_just_pressed(InputState *input, char *action_name) { | ||||
|     ActionHashItem *action = shgetp_null(input->action_hash, action_name); | ||||
|     if (action == NULL) { | ||||
|         log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); | ||||
|         return false; | ||||
| @@ -279,8 +279,8 @@ bool input_is_action_just_pressed(struct input_state *input, char *action_name) | ||||
| } | ||||
|  | ||||
|  | ||||
| bool input_is_action_just_released(struct input_state *input, char *action_name) { | ||||
|     struct action_hash_item *action = shgetp_null(input->action_hash, action_name); | ||||
| bool input_is_action_just_released(InputState *input, char *action_name) { | ||||
|     ActionHashItem *action = shgetp_null(input->action_hash, action_name); | ||||
|     if (action == NULL) { | ||||
|         log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); | ||||
|         return false; | ||||
| @@ -289,30 +289,30 @@ bool input_is_action_just_released(struct input_state *input, char *action_name) | ||||
| } | ||||
|  | ||||
|  | ||||
| t_fvec2 input_get_action_position(struct input_state *input, char *action_name) { | ||||
|     struct action_hash_item *action = shgetp_null(input->action_hash, action_name); | ||||
| Vec2 input_get_action_position(InputState *input, char *action_name) { | ||||
|     ActionHashItem *action = shgetp_null(input->action_hash, action_name); | ||||
|     if (action == NULL) { | ||||
|         log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); | ||||
|         return (t_fvec2) { 0 }; | ||||
|         return (Vec2) { 0 }; | ||||
|     } | ||||
|  | ||||
|     return action->value.position; | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_set_mouse_captured(struct input_state *input, bool enabled) { | ||||
| void input_set_mouse_captured(InputState *input, bool enabled) { | ||||
|     (void)input; | ||||
|     /* TODO: returns -1 if not supported, but like... do we care? */ | ||||
|     SDL_SetRelativeMouseMode(enabled); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool input_is_mouse_captured(struct input_state *input) { | ||||
| bool input_is_mouse_captured(InputState *input) { | ||||
|     (void)input; | ||||
|     return SDL_GetRelativeMouseMode(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void input_reset_state(struct input_state *input) { | ||||
| void input_reset_state(InputState *input) { | ||||
|     stbds_shfree(input->action_hash); | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,6 @@ | ||||
|  | ||||
| #include "twn_input.h" | ||||
|  | ||||
| void input_reset_state(struct input_state *input); | ||||
| void input_reset_state(InputState *input); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -167,7 +167,7 @@ static void main_loop(void) { | ||||
|             poll_events(); | ||||
|  | ||||
|             if (ctx.game.window_size_has_changed) { | ||||
|                 t_vec2 size; | ||||
|                 Vec2i size; | ||||
|                 SDL_GetWindowSize(ctx.window, &size.x, &size.y); | ||||
|                 ctx.game.window_w = size.x; | ||||
|                 ctx.game.window_h = size.y; | ||||
|   | ||||
| @@ -113,14 +113,14 @@ static SDL_Surface *create_surface(int width, int height) { | ||||
|  | ||||
|  | ||||
| /* adds a new, blank atlas surface to the cache */ | ||||
| static void add_new_atlas(struct texture_cache *cache) { | ||||
| static void add_new_atlas(TextureCache *cache) { | ||||
|     SDL_Surface *new_atlas = create_surface(TEXTURE_ATLAS_SIZE, TEXTURE_ATLAS_SIZE); | ||||
|     arrput(cache->atlas_surfaces, new_atlas); | ||||
|     arrput(cache->atlas_textures, create_gpu_texture(TEXTURE_FILTER_NEAREAST, true)); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void upload_texture_from_surface(gpu_texture texture, SDL_Surface *surface) { | ||||
| static void upload_texture_from_surface(GPUTexture texture, SDL_Surface *surface) { | ||||
|     SDL_LockSurface(surface); | ||||
|  | ||||
|     upload_gpu_texture(texture, surface->pixels, surface->format->BytesPerPixel, surface->w, surface->h); | ||||
| @@ -129,7 +129,7 @@ static void upload_texture_from_surface(gpu_texture texture, SDL_Surface *surfac | ||||
| } | ||||
|  | ||||
|  | ||||
| static void recreate_current_atlas_texture(struct texture_cache *cache) { | ||||
| static void recreate_current_atlas_texture(TextureCache *cache) { | ||||
|     /* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */ | ||||
|     /*       for example, if full page of 64x64 tiles was already filled, there's no real reason to process them further */ | ||||
|     SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index]; | ||||
| @@ -164,7 +164,7 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { | ||||
|  | ||||
|  | ||||
| /* uses the textures currently in the cache to create an array of stbrp_rects */ | ||||
| static stbrp_rect *create_rects_from_cache(struct texture_cache *cache) { | ||||
| static stbrp_rect *create_rects_from_cache(TextureCache *cache) { | ||||
|     stbrp_rect *rects = NULL; | ||||
|     for (size_t i = 0; i < shlenu(cache->hash); ++i) { | ||||
|         if (cache->hash[i].value.loner_texture != 0) | ||||
| @@ -203,7 +203,7 @@ static stbrp_rect *filter_unpacked_rects(stbrp_rect *rects) { | ||||
|  | ||||
| /* updates the original rects array with the data from packed_rects */ | ||||
| /* returns true if all rects were packed successfully */ | ||||
| static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) { | ||||
| static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) { | ||||
|     /* !!! do not grow either of the arrays !!! */ | ||||
|     /* the reallocation will try to reassign the array pointer, to no effect. */ | ||||
|     /* see stb_ds.h */ | ||||
| @@ -229,13 +229,13 @@ static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_r | ||||
|  | ||||
|  | ||||
| /* updates the atlas location of every rect in the cache */ | ||||
| static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) { | ||||
| static void update_texture_rects_in_atlas(TextureCache *cache, stbrp_rect *rects) { | ||||
|     int r = 0; | ||||
|     for (size_t i = 0; i < shlenu(cache->hash); ++i) { | ||||
|         if (cache->hash[i].value.loner_texture != 0) | ||||
|             continue; | ||||
|  | ||||
|         cache->hash[i].value.srcrect = (t_frect) { | ||||
|         cache->hash[i].value.srcrect = (Rect) { | ||||
|             .x = (float)rects[r].x, | ||||
|             .y = (float)rects[r].y, | ||||
|             .w = (float)rects[r].w, | ||||
| @@ -247,7 +247,7 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec | ||||
| } | ||||
|  | ||||
|  | ||||
| void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { | ||||
| void textures_cache_init(TextureCache *cache, SDL_Window *window) { | ||||
|     cache->window = window; | ||||
|     sh_new_arena(cache->hash); | ||||
|  | ||||
| @@ -258,7 +258,7 @@ void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void textures_cache_deinit(struct texture_cache *cache) { | ||||
| void textures_cache_deinit(TextureCache *cache) { | ||||
|     /* free atlas textures */ | ||||
|     for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) { | ||||
|         delete_gpu_texture(cache->atlas_textures[i]); | ||||
| @@ -282,7 +282,7 @@ void textures_cache_deinit(struct texture_cache *cache) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void textures_dump_atlases(struct texture_cache *cache) { | ||||
| void textures_dump_atlases(TextureCache *cache) { | ||||
|     PHYSFS_mkdir("/dump"); | ||||
|  | ||||
|     const char string_template[] = "/dump/atlas%zd.png"; | ||||
| @@ -307,18 +307,18 @@ void textures_dump_atlases(struct texture_cache *cache) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static enum texture_mode infer_texture_mode(SDL_Surface *surface) { | ||||
| static enum TextureMode infer_texture_mode(SDL_Surface *surface) { | ||||
|     const uint32_t amask = surface->format->Amask; | ||||
|     if (amask == 0) | ||||
|         return TEXTURE_MODE_OPAQUE; | ||||
|  | ||||
|     enum texture_mode result = TEXTURE_MODE_OPAQUE; | ||||
|     enum TextureMode result = TEXTURE_MODE_OPAQUE; | ||||
|  | ||||
|     SDL_LockSurface(surface); | ||||
|  | ||||
|     for (int i = 0; i < surface->w * surface->h; ++i) { | ||||
|         /* TODO: don't assume 32 bit depth ? */ | ||||
|         t_color color; | ||||
|         Color color; | ||||
|         SDL_GetRGBA(((uint32_t *)surface->pixels)[i], surface->format, &color.r, &color.g, &color.b, &color.a); | ||||
|  | ||||
|         if (color.a == 0) | ||||
| @@ -335,14 +335,14 @@ static enum texture_mode infer_texture_mode(SDL_Surface *surface) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static t_texture_key textures_load(struct texture_cache *cache, const char *path) { | ||||
| static TextureKey textures_load(TextureCache *cache, const char *path) { | ||||
|     /* no need to do anything if it was loaded already */ | ||||
|     const ptrdiff_t i = shgeti(cache->hash, path); | ||||
|     if (i >= 0) | ||||
|         return (t_texture_key){ (uint16_t)i }; | ||||
|         return (TextureKey){ (uint16_t)i }; | ||||
|  | ||||
|     SDL_Surface *surface = image_to_surface(path); | ||||
|     struct texture new_texture = { | ||||
|     Texture new_texture = { | ||||
|         .data = surface, | ||||
|         .mode = infer_texture_mode(surface), | ||||
|     }; | ||||
| @@ -357,18 +357,18 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path | ||||
|         } | ||||
|         new_texture.loner_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, true); | ||||
|         upload_texture_from_surface(new_texture.loner_texture, surface);  | ||||
|         new_texture.srcrect = (t_frect) { .w = (float)surface->w, .h = (float)surface->h }; | ||||
|         new_texture.srcrect = (Rect) { .w = (float)surface->w, .h = (float)surface->h }; | ||||
|     } else { | ||||
|         /* will be fully populated as the atlas updates */ | ||||
|         new_texture.atlas_index = cache->atlas_index; | ||||
|         cache->is_dirty = true; | ||||
|     } | ||||
|     shput(cache->hash, path, new_texture); | ||||
|     return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; | ||||
|     return (TextureKey){ (uint16_t)shgeti(cache->hash, path) }; | ||||
| } | ||||
|  | ||||
|  | ||||
| void textures_update_atlas(struct texture_cache *cache) { | ||||
| void textures_update_atlas(TextureCache *cache) { | ||||
|     if (!cache->is_dirty) | ||||
|         return; | ||||
|  | ||||
| @@ -421,14 +421,14 @@ static const char *rodata_start; | ||||
| static const char *rodata_stop; | ||||
|  | ||||
| static const char *last_path = NULL; | ||||
| static t_texture_key last_texture; | ||||
| static struct ptr_to_texture { | ||||
| static TextureKey last_texture; | ||||
| static struct PtrToTexture { | ||||
|     const void *key; | ||||
|     t_texture_key value; | ||||
|     TextureKey value; | ||||
| } *ptr_to_texture; | ||||
|  | ||||
| /* TODO: separate and reuse */ | ||||
| t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { | ||||
| TextureKey textures_get_key(TextureCache *cache, const char *path) { | ||||
|     if (rodata_stop == NULL) | ||||
|         if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop)) | ||||
|             CRY("Section inference", ".rodata section lookup failed"); | ||||
| @@ -458,7 +458,7 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { | ||||
| } | ||||
|  | ||||
| #else | ||||
| t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { | ||||
| TextureKey textures_get_key(TextureCache *cache, const char *path) { | ||||
|     /* hash tables are assumed to be stable, so we just return indices */ | ||||
|     const ptrdiff_t texture = shgeti(cache->hash, path); | ||||
|  | ||||
| @@ -466,12 +466,12 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { | ||||
|     if (texture == -1) { | ||||
|         return textures_load(cache, path); | ||||
|     } else | ||||
|         return (t_texture_key){ (uint16_t)texture }; | ||||
|         return (TextureKey){ (uint16_t)texture }; | ||||
| } | ||||
|  | ||||
| #endif /* generic implementation of textures_get_key() */ | ||||
|  | ||||
| int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key) { | ||||
| int32_t textures_get_atlas_id(const TextureCache *cache, TextureKey key) { | ||||
|     if (m_texture_key_is_valid(key)) { | ||||
|         if (cache->hash[key.id].value.loner_texture != 0) | ||||
|             return -cache->hash[key.id].value.loner_texture; | ||||
| @@ -484,32 +484,32 @@ int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key k | ||||
|     } | ||||
| } | ||||
|  | ||||
| t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) { | ||||
| Rect textures_get_srcrect(const TextureCache *cache, TextureKey key) { | ||||
|     if (m_texture_key_is_valid(key)) { | ||||
|         return cache->hash[key.id].value.srcrect; | ||||
|     } else { | ||||
|         CRY("Texture lookup failed.", | ||||
|             "Tried to get texture that isn't loaded."); | ||||
|         return (t_frect){ 0, 0, 0, 0 }; | ||||
|         return (Rect){ 0, 0, 0, 0 }; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key) { | ||||
| Rect textures_get_dims(const TextureCache *cache, TextureKey key) { | ||||
|     if (m_texture_key_is_valid(key)) { | ||||
|         if (cache->hash[key.id].value.loner_texture != 0) | ||||
|             return cache->hash[key.id].value.srcrect; | ||||
|         else | ||||
|             return (t_frect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; | ||||
|             return (Rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; | ||||
|     } else { | ||||
|         CRY("Texture lookup failed.", | ||||
|             "Tried to get texture that isn't loaded."); | ||||
|         return (t_frect){ 0, 0, 0, 0 }; | ||||
|         return (Rect){ 0, 0, 0, 0 }; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| void textures_bind(const struct texture_cache *cache, t_texture_key key) { | ||||
| void textures_bind(const TextureCache *cache, TextureKey key) { | ||||
|     if (m_texture_key_is_valid(key)) { | ||||
|         if (cache->hash[key.id].value.loner_texture == 0) | ||||
|             bind_gpu_texture(cache->atlas_textures[cache->hash[key.id].value.atlas_index]); | ||||
| @@ -523,7 +523,7 @@ void textures_bind(const struct texture_cache *cache, t_texture_key key) { | ||||
|  | ||||
|  | ||||
| /* TODO: alternative schemes, such as: array texture, fragment shader and geometry division */ | ||||
| void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key) { | ||||
| void textures_bind_repeating(const TextureCache *cache, TextureKey key) { | ||||
|     if (m_texture_key_is_valid(key)) { | ||||
|         if (cache->hash[key.id].value.loner_texture == 0) { | ||||
|  | ||||
| @@ -533,9 +533,9 @@ void textures_bind_repeating(const struct texture_cache *cache, t_texture_key ke | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const struct texture texture = cache->hash[key.id].value; | ||||
|             const Texture texture = cache->hash[key.id].value; | ||||
|  | ||||
|             const gpu_texture repeating_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, false); | ||||
|             const GPUTexture repeating_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, false); | ||||
|  | ||||
|             SDL_LockSurface(texture.data); | ||||
|  | ||||
| @@ -561,7 +561,7 @@ void textures_bind_repeating(const struct texture_cache *cache, t_texture_key ke | ||||
| } | ||||
|  | ||||
|  | ||||
| enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key) { | ||||
| TextureMode textures_get_mode(const TextureCache *cache, TextureKey key) { | ||||
|     if (m_texture_key_is_valid(key)) { | ||||
|         return cache->hash[key.id].value.mode; | ||||
|     } else { | ||||
| @@ -572,14 +572,14 @@ enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture | ||||
| } | ||||
|  | ||||
|  | ||||
| size_t textures_get_num_atlases(const struct texture_cache *cache) { | ||||
| size_t textures_get_num_atlases(const TextureCache *cache) { | ||||
|     return cache->atlas_index + 1;     | ||||
| } | ||||
|  | ||||
| void textures_reset_state(void) { | ||||
| #if defined(__linux__) && !defined(HOT_RELOAD_SUPPORT) | ||||
|     last_path = NULL; | ||||
|     last_texture = (t_texture_key){0}; | ||||
|     last_texture = (TextureKey){0}; | ||||
|     shfree(ptr_to_texture); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -11,47 +11,47 @@ | ||||
| #include <stdbool.h> | ||||
|  | ||||
|  | ||||
| struct texture { | ||||
|     t_frect srcrect;            /* position in atlas */ | ||||
| typedef struct Texture { | ||||
|     Rect srcrect;            /* position in atlas */ | ||||
|     SDL_Surface *data;          /* original image data */ | ||||
|     int atlas_index; | ||||
|     gpu_texture loner_texture;       /* stored directly for loners, == 0 means atlas_index should be used */ | ||||
|     gpu_texture repeating_texture;   /* separately allocated texture, for loners == loner_texture */ | ||||
|     enum texture_mode mode; | ||||
| }; | ||||
|     GPUTexture loner_texture;       /* stored directly for loners, == 0 means atlas_index should be used */ | ||||
|     GPUTexture repeating_texture;   /* separately allocated Texture, for loners == loner_texture */ | ||||
|     enum TextureMode mode; | ||||
| } Texture; | ||||
|  | ||||
|  | ||||
| struct texture_cache_item { | ||||
| typedef struct TextureCacheItem { | ||||
|     char *key; | ||||
|     struct texture value; | ||||
| }; | ||||
|     struct Texture value; | ||||
| } TextureCacheItem; | ||||
|  | ||||
|  | ||||
| struct texture_cache { | ||||
| typedef struct TextureCache { | ||||
|     SDL_Window *window;          /* from context */ | ||||
|  | ||||
|     struct texture_cache_item *hash; | ||||
|     struct TextureCacheItem *hash; | ||||
|  | ||||
|     stbrp_node *node_buffer;     /* used internally by stb_rect_pack */ | ||||
|  | ||||
|     SDL_Surface **atlas_surfaces; | ||||
|     gpu_texture *atlas_textures;      /* shared by atlas textures */ | ||||
|     GPUTexture *atlas_textures;      /* shared by atlas textures */ | ||||
|     int atlas_index;             /* atlas that is currently being built */ | ||||
|  | ||||
|     bool is_dirty;               /* current atlas needs to be recreated */ | ||||
| }; | ||||
| } TextureCache; | ||||
|  | ||||
| /* type safe structure for persistent texture handles */ | ||||
| typedef struct { uint16_t id; } t_texture_key; | ||||
| typedef struct TextureKey { uint16_t id; } TextureKey; | ||||
|  | ||||
| /* tests whether given key structure corresponds to any texture */ | ||||
| #define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1) | ||||
|  | ||||
| void textures_cache_init(struct texture_cache *cache, SDL_Window *window); | ||||
| void textures_cache_deinit(struct texture_cache *cache); | ||||
| void textures_cache_init(struct TextureCache *cache, SDL_Window *window); | ||||
| void textures_cache_deinit(struct TextureCache *cache); | ||||
|  | ||||
| /* for debugging */ | ||||
| void textures_dump_atlases(struct texture_cache *cache); | ||||
| void textures_dump_atlases(struct TextureCache *cache); | ||||
|  | ||||
| /* loads an image if it isn't in the cache, otherwise a no-op. */ | ||||
| /* can be called from anywhere at any time after init, useful if you want to */ | ||||
| @@ -61,30 +61,30 @@ void textures_dump_atlases(struct texture_cache *cache); | ||||
| /* repacks the current texture atlas based on the texture cache if needed */ | ||||
| /* any previously returned srcrect results are invalidated after that */ | ||||
| /* call it every time before rendering */ | ||||
| void textures_update_atlas(struct texture_cache *cache); | ||||
| void textures_update_atlas(TextureCache *cache); | ||||
|  | ||||
| /* returns a persistent handle to some texture in cache, loading it if needed */ | ||||
| /* check the result with m_texture_key_is_valid() */ | ||||
| t_texture_key textures_get_key(struct texture_cache *cache, const char *path); | ||||
| TextureKey textures_get_key(TextureCache *cache, const char *path); | ||||
|  | ||||
| /* returns a rect in a texture cache of the given key */ | ||||
| t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key); | ||||
| Rect textures_get_srcrect(const TextureCache *cache, TextureKey key); | ||||
|  | ||||
| /* returns a rect of dimensions of the whole texture (whole atlas) */ | ||||
| t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key); | ||||
| Rect textures_get_dims(const TextureCache *cache, TextureKey key); | ||||
|  | ||||
| /* returns an identifier that is equal for all textures placed in the same atlas */ | ||||
| int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key); | ||||
| int32_t textures_get_atlas_id(const TextureCache *cache, TextureKey key); | ||||
|  | ||||
| void textures_bind(const struct texture_cache *cache, t_texture_key key); | ||||
| void textures_bind(const TextureCache *cache, TextureKey key); | ||||
|  | ||||
| void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key); | ||||
| void textures_bind_repeating(const TextureCache *cache, TextureKey key); | ||||
|  | ||||
| /* returns helpful information about contents of alpha channel in given texture */ | ||||
| enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key); | ||||
| TextureMode textures_get_mode(const TextureCache *cache, TextureKey key); | ||||
|  | ||||
| /* returns the number of atlases in the cache */ | ||||
| size_t textures_get_num_atlases(const struct texture_cache *cache); | ||||
| size_t textures_get_num_atlases(const TextureCache *cache); | ||||
|  | ||||
| /* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */ | ||||
| void textures_reset_state(void); | ||||
|   | ||||
| @@ -171,7 +171,7 @@ bool strends(const char *str, const char *suffix) { | ||||
| } | ||||
|  | ||||
|  | ||||
| bool overlap_rect(const t_rect *a, const t_rect *b, t_rect *result) { | ||||
| bool overlap_rect(const Recti *a, const Recti *b, Recti *result) { | ||||
|     SDL_Rect a_sdl = { a->x, a->y, a->w, a->h }; | ||||
|     SDL_Rect b_sdl = { b->x, b->y, b->w, b->h }; | ||||
|     SDL_Rect result_sdl = { 0 }; | ||||
| @@ -179,13 +179,13 @@ bool overlap_rect(const t_rect *a, const t_rect *b, t_rect *result) { | ||||
|     bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl); | ||||
|  | ||||
|     if (result != NULL) | ||||
|         *result = (t_rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h }; | ||||
|         *result = (Recti){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h }; | ||||
|  | ||||
|     return intersection; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool overlap_frect(const t_frect *a, const t_frect *b, t_frect *result) { | ||||
| bool overlap_frect(const Rect *a, const Rect *b, Rect *result) { | ||||
|     SDL_FRect a_sdl = { a->x, a->y, a->w, a->h }; | ||||
|     SDL_FRect b_sdl = { b->x, b->y, b->w, b->h }; | ||||
|     SDL_FRect result_sdl = { 0 }; | ||||
| @@ -193,28 +193,28 @@ bool overlap_frect(const t_frect *a, const t_frect *b, t_frect *result) { | ||||
|     bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl); | ||||
|  | ||||
|     if (result != NULL) | ||||
|         *result = (t_frect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h }; | ||||
|         *result = (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h }; | ||||
|  | ||||
|     return intersection; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool intersect_rect(const t_rect *a, const t_rect *b) { | ||||
| bool intersect_rect(const Recti *a, const Recti *b) { | ||||
|     SDL_Rect a_sdl = { a->x, a->y, a->w, a->h }; | ||||
|     SDL_Rect b_sdl = { b->x, b->y, b->w, b->h }; | ||||
|     return SDL_HasIntersection(&a_sdl, &b_sdl); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool intersect_frect(const t_frect *a, const t_frect *b) { | ||||
| bool intersect_frect(const Rect *a, const Rect *b) { | ||||
|     SDL_FRect a_sdl = { a->x, a->y, a->w, a->h }; | ||||
|     SDL_FRect b_sdl = { b->x, b->y, b->w, b->h }; | ||||
|     return SDL_HasIntersectionF(&a_sdl, &b_sdl); | ||||
| } | ||||
|  | ||||
|  | ||||
| t_frect to_frect(t_rect rect) { | ||||
|     return (t_frect) { | ||||
| Rect to_frect(Recti rect) { | ||||
|     return (Rect) { | ||||
|         .h = (float)rect.h, | ||||
|         .w = (float)rect.w, | ||||
|         .x = (float)rect.x, | ||||
| @@ -223,8 +223,8 @@ t_frect to_frect(t_rect rect) { | ||||
| } | ||||
|  | ||||
|  | ||||
| t_fvec2 frect_center(t_frect rect) { | ||||
|     return (t_fvec2){ | ||||
| Vec2 frect_center(Rect rect) { | ||||
|     return (Vec2){ | ||||
|         .x = rect.x + rect.w / 2, | ||||
|         .y = rect.y + rect.h / 2, | ||||
|     }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user