typedef & PascalCase for ALL structs and enums

This commit is contained in:
wanp 2024-09-23 14:43:16 -03:00
parent e093a6d492
commit 73bf92e706
43 changed files with 795 additions and 793 deletions

View File

@ -13,9 +13,9 @@
void game_tick(void) { void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
if (!ctx.udata) { 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->ctx = &ctx;
state->scene = title_scene(state); 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); 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")) { if (input_is_action_just_pressed(&ctx.input, "debug_toggle")) {
ctx.debug = !ctx.debug; ctx.debug = !ctx.debug;
@ -67,7 +67,7 @@ void game_tick(void) {
void game_end(void) { void game_end(void) {
struct state *state = ctx.udata; State *state = ctx.udata;
state->scene->end(state); state->scene->end(state);
free(state); free(state);
} }

View File

@ -10,14 +10,14 @@
#include <tgmath.h> #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_air_timer);
tick_timer(&player->jump_coyote_timer); tick_timer(&player->jump_coyote_timer);
tick_timer(&player->jump_buffer_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 */ /* apply horizontal damping when the player stops moving */
/* in other words, make it decelerate to a standstill */ /* in other words, make it decelerate to a standstill */
if (!input_is_action_pressed(input, "player_left") && 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_coyote_timer = 0;
player->jump_buffer_timer = 0; player->jump_buffer_timer = 0;
player->dy = player->jump_force_initial; 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; player->current_gravity_multiplier = player->jump_default_multiplier;
if (input_is_action_just_pressed(input, "player_jump")) { 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.w = player->rect.w;
player->collider_x.h = player->rect.h - 8; player->collider_x.h = player->rect.h - 8;
player->collider_x.x = player->rect.x; 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.w = player->rect.w;
player->collider_y.h = player->rect.h; player->collider_y.h = player->rect.h;
player->collider_y.x = player->rect.x + ((player->rect.w - player->collider_y.w) / 2); 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 -= gravity * player->current_gravity_multiplier;
player->dy = fmax(player->dy, -player->terminal_velocity); 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 */ /* 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 * somewhat of a hack here. we only want to do corner correction
* if the corner in question really is the corner of a "platform," * 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) { static void calc_collisions_x(Player *player) {
t_frect collision; Rect collision;
bool is_colliding = world_find_intersect_frect(player->world, player->collider_x, &collision); bool is_colliding = world_find_intersect_frect(player->world, player->collider_x, &collision);
if (!is_colliding) return; if (!is_colliding) return;
float player_center_x = player->collider_x.x + (player->collider_x.w / 2); float player_center_x = player->collider_x.x + (player->collider_x.w / 2);
float collision_center_x = collision.x + (collision.w / 2); float collision_center_x = collision.x + (collision.w / 2);
enum collision_direction { COLLISION_LEFT = -1, COLLISION_RIGHT = 1 }; typedef enum CollisionDirection { COLLISION_LEFT = -1, COLLISION_RIGHT = 1 } CollisionDirection;
enum collision_direction dir_x = CollisionDirection dir_x =
player_center_x > collision_center_x ? COLLISION_LEFT : COLLISION_RIGHT; player_center_x > collision_center_x ? COLLISION_LEFT : COLLISION_RIGHT;
player->rect.x -= collision.w * (float)dir_x; 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) { static void calc_collisions_y(Player *player) {
t_frect collision; Rect collision;
bool is_colliding = world_find_intersect_frect(player->world, player->collider_y, &collision); bool is_colliding = world_find_intersect_frect(player->world, player->collider_y, &collision);
if (!is_colliding) return; if (!is_colliding) return;
float player_center_y = player->collider_y.y + (player->collider_y.h / 2); float player_center_y = player->collider_y.y + (player->collider_y.h / 2);
float collision_center_y = collision.y + (collision.h / 2); float collision_center_y = collision.y + (collision.h / 2);
enum collision_direction { COLLISION_ABOVE = -1, COLLISION_BELOW = 1 }; typedef enum CollisionDirection { COLLISION_ABOVE = -1, COLLISION_BELOW = 1 } CollisionDirection;
enum collision_direction dir_y = CollisionDirection dir_y =
player_center_y > collision_center_y ? COLLISION_ABOVE : COLLISION_BELOW; player_center_y > collision_center_y ? COLLISION_ABOVE : COLLISION_BELOW;
/* before the resolution */ /* before the resolution */
@ -202,16 +202,16 @@ static void calc_collisions_y(struct player *player) {
} }
struct player *player_create(struct world *world) { Player *player_create(World *world) {
struct player *player = cmalloc(sizeof *player); Player *player = cmalloc(sizeof *player);
*player = (struct player) { *player = (Player) {
.world = world, .world = world,
.sprite_w = 48, .sprite_w = 48,
.sprite_h = 48, .sprite_h = 48,
.rect = (t_frect) { .rect = (Rect) {
.x = 92, .x = 92,
.y = 200, .y = 200,
.w = 16, .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", m_sprite("/assets/player/baron-walk.png",
(t_frect) { (Rect) {
.x = player->rect.x + ((player->rect.w - player->sprite_w) / 2), .x = player->rect.x + ((player->rect.w - player->sprite_w) / 2),
.y = player->rect.y - 8, .y = player->rect.y - 8,
.w = player->sprite_w, .w = player->sprite_w,
.h = player->sprite_h, .h = player->sprite_h,
}); });
push_circle((t_fvec2) { 256, 128 }, push_circle((Vec2) { 256, 128 },
24, 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) if (!ctx.debug)
return; return;
@ -269,19 +269,19 @@ static void drawdef_debug(struct player *player) {
/* }; */ /* }; */
push_rectangle(player->collider_x, push_rectangle(player->collider_x,
(t_color){ 0, 0, 255, 128 }); (Color){ 0, 0, 255, 128 });
push_rectangle(player->collider_y, 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); free(player);
} }
void player_calc(struct player *player) { void player_calc(Player *player) {
update_timers(player); update_timers(player);
input_move(&ctx.input, player); input_move(&ctx.input, player);

View File

@ -4,32 +4,32 @@
#include "twn_game_api.h" #include "twn_game_api.h"
struct world; typedef struct World World;
enum player_action { typedef enum PlayerAction {
PLAYER_ACTION_GROUND, PLAYER_ACTION_GROUND,
PLAYER_ACTION_FALL, PLAYER_ACTION_FALL,
PLAYER_ACTION_JUMP, PLAYER_ACTION_JUMP,
}; } PlayerAction;
struct player { typedef struct Player {
struct world *world; World *world;
/* visual */ /* visual */
float sprite_w; float sprite_w;
float sprite_h; float sprite_h;
/* body */ /* body */
t_frect rect; Rect rect;
/* state */ /* state */
enum player_action action; PlayerAction action;
/* physics */ /* physics */
t_frect collider_x; Rect collider_x;
t_frect collider_y; Rect collider_y;
int collider_thickness; int collider_thickness;
float dx; float dx;
float dy; float dy;
@ -54,12 +54,12 @@ struct player {
float jump_boosted_multiplier; float jump_boosted_multiplier;
float jump_corner_correction_offset; /* from center */ float jump_corner_correction_offset; /* from center */
}; } Player;
struct player *player_create(struct world *world); Player *player_create(World *world);
void player_destroy(struct player *player); void player_destroy(Player *player);
void player_calc(struct player *player); void player_calc(Player *player);
#endif #endif

View File

@ -8,8 +8,8 @@
#include <stb_perlin.h> #include <stb_perlin.h>
static void ingame_tick(struct state *state) { static void ingame_tick(State *state) {
struct scene_ingame *scn = (struct scene_ingame *)state->scene; SceneIngame *scn = (SceneIngame *)state->scene;
world_drawdef(scn->world); world_drawdef(scn->world);
player_calc(scn->player); 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 yaw_rad = scn->yaw * (float)DEG2RAD;
const float pitch_rad = scn->pitch * (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), cosf(yaw_rad) * cosf(pitch_rad),
sinf(pitch_rad), sinf(pitch_rad),
sinf(yaw_rad) * cosf(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 */ const float speed = 0.04f; /* TODO: put this in a better place */
if (input_is_action_pressed(&ctx.input, "player_left")) 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")) 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")) 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")) 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")) if (input_is_action_pressed(&ctx.input, "player_jump"))
scn->cam.pos.y += speed; 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_sprite(m_set(path, "/assets/9slice.png"),
m_set(rect, ((t_frect){ 16, 16, 128, 128 })), m_set(rect, ((Rect){ 16, 16, 128, 128 })),
m_opt(texture_region, ((t_frect){ 0, 0, (float)(ctx.tick_count % 48), (float)(ctx.tick_count % 48) }))); 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_sprite(m_set(path, "/assets/light.png"),
m_set(rect, ((t_frect){ 48, 64, 64, 64 })), m_set(rect, ((Rect){ 48, 64, 64, 64 })),
m_opt(color, ((t_color){ 255, 0, 0, 255 }))); m_opt(color, ((Color){ 255, 0, 0, 255 })));
m_sprite(m_set(path, "/assets/light.png"), m_sprite(m_set(path, "/assets/light.png"),
m_set(rect, ((t_frect){ 64, 64, 64, 64 })), m_set(rect, ((Rect){ 64, 64, 64, 64 })),
m_opt(color, ((t_color){ 0, 255, 0, 255 }))); m_opt(color, ((Color){ 0, 255, 0, 255 })));
m_sprite(m_set(path, "/assets/light.png"), m_sprite(m_set(path, "/assets/light.png"),
m_set(rect, ((t_frect){ 80, 64, 64, 64 })), m_set(rect, ((Rect){ 80, 64, 64, 64 })),
m_opt(color, ((t_color){ 0, 0, 255, 255 }))); m_opt(color, ((Color){ 0, 0, 255, 255 })));
m_sprite(m_set(path, "/assets/player/baron-walk.png"), 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_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 ));
m_sprite(m_set(path, "/assets/player/baron-walk.png"), 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 )); m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 ));
set_camera(&scn->cam); 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; 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", unfurl_triangle("/assets/grass.gif",
(t_fvec3){ (float)x, d0, (float)y }, (Vec3){ (float)x, d0, (float)y },
(t_fvec3){ (float)x + 1, d1, (float)y }, (Vec3){ (float)x + 1, d1, (float)y },
(t_fvec3){ (float)x, d3, (float)y - 1 }, (Vec3){ (float)x, d3, (float)y - 1 },
(t_shvec2){ 1024, 768 }, (Vec2sh){ 1024, 768 },
(t_shvec2){ 1024, 0 }, (Vec2sh){ 1024, 0 },
(t_shvec2){ 0, 768 }); (Vec2sh){ 0, 768 });
unfurl_triangle("/assets/grass.gif", unfurl_triangle("/assets/grass.gif",
(t_fvec3){ (float)x + 1, d1, (float)y }, (Vec3){ (float)x + 1, d1, (float)y },
(t_fvec3){ (float)x + 1, d2, (float)y - 1 }, (Vec3){ (float)x + 1, d2, (float)y - 1 },
(t_fvec3){ (float)x, d3, (float)y - 1 }, (Vec3){ (float)x, d3, (float)y - 1 },
(t_shvec2){ 1024, 0 }, (Vec2sh){ 1024, 0 },
(t_shvec2){ 0, 0 }, (Vec2sh){ 0, 0 },
(t_shvec2){ 0, 768 }); (Vec2sh){ 0, 768 });
} }
} }
} }
static void ingame_end(struct state *state) { static void ingame_end(State *state) {
struct scene_ingame *scn = (struct scene_ingame *)state->scene; SceneIngame *scn = (SceneIngame *)state->scene;
player_destroy(scn->player); player_destroy(scn->player);
world_destroy(scn->world); world_destroy(scn->world);
free(state->scene); free(state->scene);
} }
struct scene *ingame_scene(struct state *state) { Scene *ingame_scene(State *state) {
(void)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.tick = ingame_tick;
new_scene->base.end = ingame_end; new_scene->base.end = ingame_end;
new_scene->world = world_create(); new_scene->world = world_create();
new_scene->player = player_create(new_scene->world); 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, .repeat = true,
.volume = 1.0f .volume = 1.0f
}); });
input_set_mouse_captured(&ctx.input, true); input_set_mouse_captured(&ctx.input, true);
return (struct scene *)new_scene; return (Scene *)new_scene;
} }

View File

@ -9,22 +9,22 @@
#include "../world.h" #include "../world.h"
struct scene_ingame { typedef struct SceneIngame {
struct scene base; Scene base;
struct world *world; World *world;
struct player *player; Player *player;
t_camera cam; Camera cam;
/* TODO: put this in a better place */ /* TODO: put this in a better place */
float yaw; float yaw;
float pitch; float pitch;
float roll; float roll;
}; } SceneIngame;
struct scene *ingame_scene(struct state *state); Scene *ingame_scene(State *state);
#endif #endif

View File

@ -2,7 +2,7 @@
#include "../state.h" #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->next_scene = scene_func(state);
state->is_scene_switching = true; state->is_scene_switching = true;
} }

View File

@ -2,15 +2,15 @@
#define SCENE_H #define SCENE_H
struct state; typedef struct State State;
struct scene { typedef struct Scene {
char *id; char *id;
void (*tick)(struct state *); void (*tick)(State *);
void (*end)(struct 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 #endif

View File

@ -8,8 +8,8 @@
#include <stdio.h> #include <stdio.h>
static void title_tick(struct state *state) { static void title_tick(State *state) {
struct scene_title *scn = (struct scene_title *)state->scene; SceneTitle *scn = (SceneTitle *)state->scene;
(void)scn; (void)scn;
if (input_is_action_just_pressed(&state->ctx->input, "ui_accept")) { 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 })); ((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); int text_w = get_text_width(text_str, text_h, font);
push_rectangle( push_rectangle(
(t_frect) { (Rect) {
.x = 0, .x = 0,
.y = 0, .y = 0,
.w = (float)text_w, .w = (float)text_w,
.h = (float)text_h, .h = (float)text_h,
}, },
(t_color) { 0, 0, 0, 255 } (Color) { 0, 0, 0, 255 }
); );
push_text( push_text(
text_str, text_str,
(t_fvec2){ 0, 0 }, (Vec2){ 0, 0 },
text_h, text_h,
(t_color) { 255, 255, 255, 255 }, (Color) { 255, 255, 255, 255 },
font font
); );
free(text_str); free(text_str);
} }
static void title_end(struct state *state) { static void title_end(State *state) {
struct scene_title *scn = (struct scene_title *)state->scene; SceneTitle *scn = (SceneTitle *)state->scene;
player_destroy(scn->player); player_destroy(scn->player);
world_destroy(scn->world); world_destroy(scn->world);
free(state->scene); free(state->scene);
} }
struct scene *title_scene(struct state *state) { Scene *title_scene(State *state) {
(void)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.tick = title_tick;
new_scene->base.end = title_end; new_scene->base.end = title_end;
new_scene->world = world_create(); new_scene->world = world_create();
new_scene->player = player_create(new_scene->world); new_scene->player = player_create(new_scene->world);
return (struct scene *)new_scene; return (Scene *)new_scene;
} }

View File

@ -7,15 +7,15 @@
#include "../world.h" #include "../world.h"
struct scene_title { typedef struct SceneTitle {
struct scene base; Scene base;
struct world *world; World *world;
struct player *player; Player *player;
}; } SceneTitle;
struct scene *title_scene(struct state *state); Scene *title_scene(State *state);
#endif #endif

View File

@ -6,12 +6,15 @@
#include <stdbool.h> #include <stdbool.h>
struct state {
t_ctx *ctx; typedef struct Scene Scene;
struct scene *scene;
struct scene *next_scene; typedef struct State {
Context *ctx;
Scene *scene;
Scene *next_scene;
bool is_scene_switching; bool is_scene_switching;
}; } State;
#endif #endif

View File

@ -8,11 +8,11 @@
#include <tgmath.h> #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 row = 0; row < world->tilemap_height; ++row) {
for (size_t col = 0; col < world->tilemap_width; ++col) { for (size_t col = 0; col < world->tilemap_width; ++col) {
world->tiles[(row * world->tilemap_width) + col] = (struct tile) { world->tiles[(row * world->tilemap_width) + col] = (struct Tile) {
.rect = (t_rect) { .rect = (Recti) {
.x = (int)col * world->tile_size, .x = (int)col * world->tile_size,
.y = (int)row * world->tile_size, .y = (int)row * world->tile_size,
.w = 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) { static Vec2i to_grid_location(struct World *world, float x, float y) {
return (t_vec2) { return (Vec2i) {
.x = (int)floor(x / (float)world->tile_size), .x = (int)floor(x / (float)world->tile_size),
.y = (int)floor(y / (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; if (!ctx.debug) return;
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) { for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID) continue; if (world->tiles[i].type == TILE_TYPE_VOID) continue;
push_rectangle(to_frect(world->tiles[i].rect), 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_create(void) {
struct world *world = cmalloc(sizeof *world); struct World *world = cmalloc(sizeof *world);
*world = (struct world) { *world = (struct World) {
.tiles = NULL, .tiles = NULL,
.tile_size = 42, .tile_size = 42,
.tilemap_width = 20, .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); free(world->tiles);
for (size_t i = 0; i < world->tilemap_height; ++i) { 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) { for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID) if (world->tiles[i].type == TILE_TYPE_VOID)
continue; 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; bool is_intersecting = false;
const size_t tile_count = world->tilemap_height * world->tilemap_width; 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) if (world->tiles[i].type == TILE_TYPE_VOID)
continue; continue;
t_frect tile_frect = { Rect tile_frect = {
.x = (float)(world->tiles[i].rect.x), .x = (float)(world->tiles[i].rect.x),
.y = (float)(world->tiles[i].rect.y), .y = (float)(world->tiles[i].rect.y),
.w = (float)(world->tiles[i].rect.w), .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) { if (intersection == NULL) {
t_frect temp; Rect temp;
is_intersecting = overlap_frect(&rect, &tile_frect, &temp); is_intersecting = overlap_frect(&rect, &tile_frect, &temp);
} else { } else {
is_intersecting = overlap_frect(&rect, &tile_frect, intersection); 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; bool is_intersecting = false;
const size_t tile_count = world->tilemap_height * world->tilemap_width; 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) if (world->tiles[i].type == TILE_TYPE_VOID)
continue; continue;
t_rect *tile_rect = &world->tiles[i].rect; Recti *tile_rect = &world->tiles[i].rect;
if (intersection == NULL) { if (intersection == NULL) {
t_rect temp; Recti temp;
is_intersecting = overlap_rect(&rect, tile_rect, &temp); is_intersecting = overlap_rect(&rect, tile_rect, &temp);
} else { } else {
is_intersecting = overlap_rect(&rect, tile_rect, intersection); 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) { bool world_is_tile_at(struct World *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, 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; 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) { void world_place_tile(struct World *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y); Vec2i position_in_grid = to_grid_location(world, x, y);
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_SOLID; world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_SOLID;
update_tiles(world); update_tiles(world);
} }
void world_remove_tile(struct world *world, float x, float y) { void world_remove_tile(struct World *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y); Vec2i position_in_grid = to_grid_location(world, x, y);
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_VOID; world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_VOID;
update_tiles(world); update_tiles(world);
} }

View File

@ -1,42 +1,44 @@
#ifndef WORLD_H #ifndef WORLD_H
#define WORLD_H #define WORLD_H
#include "twn_game_api.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
enum tile_type { typedef enum TileType {
TILE_TYPE_VOID, TILE_TYPE_VOID,
TILE_TYPE_SOLID, TILE_TYPE_SOLID,
}; } TileType;
struct tile { typedef struct Tile {
t_rect rect; Recti rect;
enum tile_type type; TileType type;
}; } Tile;
struct world { typedef struct World {
enum tile_type **tilemap; TileType **tilemap;
struct tile *tiles; Tile *tiles;
int tile_size; int tile_size;
unsigned int tilemap_width; unsigned int tilemap_width;
unsigned int tilemap_height; unsigned int tilemap_height;
size_t tile_nonvoid_count; size_t tile_nonvoid_count;
float gravity; float gravity;
}; } World;
struct world *world_create(void); World *world_create(void);
void world_destroy(struct world *world); void world_destroy(World *world);
void world_drawdef(struct world *world); void world_drawdef(World *world);
bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *intersection); bool world_find_intersect_frect(World *world, Rect rect, Rect *intersection);
bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *intersection); bool world_find_intersect_rect(World *world, Recti rect, Recti *intersection);
bool world_is_tile_at(struct world *world, float x, float y); bool world_is_tile_at(World *world, float x, float y);
void world_place_tile(struct world *world, float x, float y); void world_place_tile(World *world, float x, float y);
void world_remove_tile(struct world *world, float x, float y); void world_remove_tile(World *world, float x, float y);
#endif #endif

BIN
data/assets/9slice.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -6,7 +6,7 @@
#include <stdbool.h> #include <stdbool.h>
typedef struct play_audio_args { typedef struct PlayAudioArgs {
/* default: false */ /* default: false */
bool repeat; bool repeat;
/* crossfade between already playing audio on a given channel, if any */ /* 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 */ /* range: -1.0 to 1.0f */
/* default: 0.0f */ /* default: 0.0f */
float panning; float panning;
} t_play_audio_args; } PlayAudioArgs;
/* plays audio file at specified channel or anywhere if NULL is passed */ /* 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 */ /* path must contain valid file extension to infer which file format it is */
/* supported formats: .ogg, .xm */ /* supported formats: .ogg, .xm */
/* preserves args that are already specified on the channel */ /* 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 */ /* could be used for modifying args */
/* warn: is only valid if no other calls to audio are made */ /* 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 #endif

View File

@ -8,15 +8,15 @@
/* for example, perspective matrix only needs recaluclation on FOV change */ /* for example, perspective matrix only needs recaluclation on FOV change */
/* first person camera class */ /* first person camera class */
typedef struct camera { typedef struct Camera {
t_fvec3 pos; /* eye position */ Vec3 pos; /* eye position */
t_fvec3 target; /* normalized target vector */ Vec3 target; /* normalized target vector */
t_fvec3 up; /* normalized up vector */ Vec3 up; /* normalized up vector */
float fov; /* field of view, in radians */ 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 #endif

View File

@ -8,8 +8,8 @@
#include <stdint.h> #include <stdint.h>
typedef struct context { typedef struct Context {
struct input_state input; struct InputState input;
int64_t delta_time; /* preserves real time frame delta with no manipilation */ int64_t delta_time; /* preserves real time frame delta with no manipilation */
uint64_t tick_count; uint64_t tick_count;
@ -31,10 +31,10 @@ typedef struct context {
bool is_running; bool is_running;
bool window_size_has_changed; bool window_size_has_changed;
bool initialization_needed; bool initialization_needed;
} t_ctx; } Context;
#ifndef TWN_ENGINE_CONTEXT_C_H #ifndef TWN_ENGINE_CONTEXT_C_H
TWN_API extern t_ctx ctx; TWN_API extern Context ctx;
#endif #endif
#endif #endif

View File

@ -12,16 +12,16 @@
#include <stdbool.h> #include <stdbool.h>
enum button_source { typedef enum ButtonSource {
BUTTON_SOURCE_NOT_SET, BUTTON_SOURCE_NOT_SET,
BUTTON_SOURCE_KEYBOARD_PHYSICAL, BUTTON_SOURCE_KEYBOARD_PHYSICAL,
BUTTON_SOURCE_KEYBOARD_CHARACTER, BUTTON_SOURCE_KEYBOARD_CHARACTER,
BUTTON_SOURCE_GAMEPAD, BUTTON_SOURCE_GAMEPAD,
BUTTON_SOURCE_MOUSE, BUTTON_SOURCE_MOUSE,
}; } ButtonSource;
union button_code { union ButtonCode {
SDL_Scancode scancode; SDL_Scancode scancode;
SDL_Keycode keycode; SDL_Keycode keycode;
SDL_GameControllerButton gamepad_button; SDL_GameControllerButton gamepad_button;
@ -31,71 +31,71 @@ union button_code {
/* an input to which an action is bound */ /* an input to which an action is bound */
/* it is not limited to literal buttons */ /* it is not limited to literal buttons */
struct button { typedef struct Button {
enum button_source source; enum ButtonSource source;
union button_code code; union ButtonCode code;
}; } Button;
/* represents the collective state of a group of buttons */ /* represents the collective state of a group of buttons */
/* that is, changes in the states of any of the bound buttons will affect it */ /* that is, changes in the states of any of the bound buttons will affect it */
struct action { typedef struct Action {
size_t num_bindings; size_t num_bindings;
/* if you bind more than NUM_KEYBIND_SLOTS (set in config.h) */ /* 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 */ /* it forgets the first Button to add the new one at the end */
struct button bindings[NUM_KEYBIND_SLOTS]; 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 is_pressed;
bool just_changed; bool just_changed;
}; } Action;
struct action_hash_item { typedef struct ActionHashItem {
char *key; char *key;
struct action value; Action value;
}; } ActionHashItem;
struct input_state { typedef struct InputState {
struct action_hash_item *action_hash; ActionHashItem *action_hash;
const uint8_t *keyboard_state; /* array of booleans indexed by scancode */ const uint8_t *keyboard_state; /* array of booleans indexed by scancode */
uint32_t mouse_state; /* SDL mouse button bitmask */ uint32_t mouse_state; /* SDL mouse button bitmask */
t_vec2 mouse_window_position; Vec2i mouse_window_position;
t_vec2 mouse_relative_position; Vec2i mouse_relative_position;
enum button_source last_active_source; ButtonSource last_active_source;
bool is_anything_just_pressed; bool is_anything_just_pressed;
}; } InputState;
TWN_API void input_state_init(struct input_state *input); TWN_API void input_state_init(InputState *input);
TWN_API void input_state_deinit(struct input_state *input); TWN_API void input_state_deinit(InputState *input);
TWN_API void input_state_update(struct input_state *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, char *action_name,
SDL_Scancode scancode); 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, char *action_name,
SDL_Scancode scancode); 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, char *action_name,
uint8_t mouse_button); 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, char *action_name,
uint8_t mouse_button); uint8_t mouse_button);
TWN_API void input_add_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(struct input_state *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_pressed(InputState *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_pressed(InputState *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_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 void input_set_mouse_captured(InputState *input, bool value);
TWN_API bool input_is_mouse_captured(struct input_state *input); TWN_API bool input_is_mouse_captured(InputState *input);
#endif #endif

View File

@ -10,67 +10,67 @@
#include <stdbool.h> #include <stdbool.h>
typedef struct push_sprite_args { typedef struct PushSpriteArgs {
char *path; char *path;
t_frect rect; Rect rect;
m_option_list( m_option_list(
t_frect, texture_region, Rect, texture_region,
t_color, color, Color, color,
float, rotation, float, rotation,
bool, flip_x, bool, flip_x,
bool, flip_y, bool, flip_y,
bool, stretch ) bool, stretch )
} t_push_sprite_args; } PushSpriteArgs;
/* pushes a sprite onto the sprite render queue */ /* pushes a sprite onto the sprite render queue */
/* this is a simplified version of push_sprite_ex for the most common case. */ /* 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 */ /* it assumes you want no color modulation, no rotation, no flip */
TWN_API void push_sprite(t_push_sprite_args args); TWN_API void push_sprite(PushSpriteArgs args);
#define m_sprite(...) (push_sprite((t_push_sprite_args){__VA_ARGS__})) #define m_sprite(...) (push_sprite((PushSpriteArgs){__VA_ARGS__}))
/* pushes a filled rectangle onto the rectangle render queue */ /* 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 */ /* 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); TWN_API int get_text_width(char *string, int height_px, const char *font_path);
/* pushes a textured 3d triangle onto the render queue */ /* pushes a textured 3d triangle onto the render queue */
/* vertices are in absolute coordinates, relative to world origin */ /* vertices are in absolute coordinates, relative to world origin */
/* texture coordinates are in pixels */ /* texture coordinates are in pixels */
TWN_API void unfurl_triangle(const char *path, TWN_API void unfurl_triangle(const char *path,
t_fvec3 v0, Vec3 v0,
t_fvec3 v1, Vec3 v1,
t_fvec3 v2, Vec3 v2,
t_shvec2 uv0, Vec2sh uv0,
t_shvec2 uv1, Vec2sh uv1,
t_shvec2 uv2); Vec2sh uv2);
// TODO: decide whether it's needed to begin with? // TODO: decide whether it's needed to begin with?
// intended usage for it is baked lighting, i would think. // intended usage for it is baked lighting, i would think.
/* pushes a colored textured 3d triangle onto the render queue */ /* pushes a colored textured 3d triangle onto the render queue */
// void unfurl_colored_triangle(const char *path, // void unfurl_colored_triangle(const char *path,
// t_fvec3 v0, // Vec3 v0,
// t_fvec3 v1, // Vec3 v1,
// t_fvec3 v2, // Vec3 v2,
// t_shvec2 uv0, // Vec2sh uv0,
// t_shvec2 uv1, // Vec2sh uv1,
// t_shvec2 uv2, // Vec2sh uv2,
// t_color c0, // Color c0,
// t_color c1, // Color c1,
// t_color c2); // Color c2);
// TODO: // TODO:
// http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2 // http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2
// void unfurl_billboard(const char *path, // void unfurl_billboard(const char *path,
// t_fvec3 position, // Vec2 position,
// t_fvec2 scaling, // Vec2 scaling,
// t_frect uvs); // Rect uvs);
/* pushes a camera state to be used for all future unfurl_* commands */ /* 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 #endif

View File

@ -2,10 +2,10 @@
#define TWN_TEXTURES_MODES_H #define TWN_TEXTURES_MODES_H
/* alpha channel information */ /* alpha channel information */
enum texture_mode { typedef enum TextureMode {
TEXTURE_MODE_OPAQUE, /* all pixels are solid */ TEXTURE_MODE_OPAQUE, /* all pixels are solid */
TEXTURE_MODE_SEETHROUGH, /* some pixels are alpha zero */ TEXTURE_MODE_SEETHROUGH, /* some pixels are alpha zero */
TEXTURE_MODE_GHOSTLY, /* arbitrary alpha values */ TEXTURE_MODE_GHOSTLY, /* arbitrary alpha values */
}; } TextureMode;
#endif #endif

View File

@ -80,54 +80,54 @@ TWN_API TWN_API bool strends(const char *str, const char *suffix);
/* */ /* */
/* 32-bit color data */ /* 32-bit color data */
typedef struct color { typedef struct Color {
_Alignas(4) _Alignas(4)
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
uint8_t a; uint8_t a;
} t_color; } Color;
/* a rectangle with the origin at the upper left (integer) */ /* a rectangle with the origin at the upper left (integer) */
typedef struct rect { typedef struct Recti {
_Alignas(16) _Alignas(16)
int32_t x; int32_t x;
int32_t y; int32_t y;
int32_t w; int32_t w;
int32_t h; int32_t h;
} t_rect; } Recti;
/* a rectangle with the origin at the upper left (floating point) */ /* a rectangle with the origin at the upper left (floating point) */
typedef struct frect { typedef struct Rect {
_Alignas(16) _Alignas(16)
float x; float x;
float y; float y;
float w; float w;
float h; float h;
} t_frect; } Rect;
/* calculates the overlap of two rectangles and places it in result. */ /* 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. */ /* result may be NULL. if this is the case, it will simply be ignored. */
/* returns true if the rectangles are indeed intersecting. */ /* 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_rect(const Recti *a, const Recti *b, Recti *result);
TWN_API bool overlap_frect(const t_frect *a, const t_frect *b, t_frect *result); TWN_API bool overlap_frect(const Rect *a, const Rect *b, Rect *result);
/* returns true if two rectangles are intersecting */ /* returns true if two rectangles are intersecting */
TWN_API bool intersect_rect(const t_rect *a, const t_rect *b); TWN_API bool intersect_rect(const Recti *a, const Recti *b);
TWN_API bool intersect_frect(const t_frect *a, const t_frect *b); TWN_API bool intersect_frect(const Rect *a, const Rect *b);
/* TODO: generics and specials (see m_to_fvec2() for an example)*/ /* TODO: generics and specials (see m_vec2_from() for an example)*/
TWN_API t_frect to_frect(t_rect rect); 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 { typedef struct Matrix4 {
t_fvec4 row[4]; Vec4 row[4];
} t_matrix4; } Matrix4;
/* decrements an lvalue (which should be an int), stopping at 0 */ /* 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); 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), .x = fast_sqrt(1.0f - s * s) * (a >= (float)M_PI_2 && a < (float)(M_PI + M_PI_2) ? -1 : 1),
.y = s .y = s
}; };

View File

@ -6,92 +6,92 @@
/* a point in some space (integer) */ /* a point in some space (integer) */
typedef struct vec2 { typedef struct Vec2i {
_Alignas(8) _Alignas(8)
int32_t x; int32_t x;
int32_t y; int32_t y;
} t_vec2; } Vec2i;
/* a point in some space (floating point) */ /* a point in some space (floating point) */
typedef struct fvec2 { typedef struct Vec2 {
_Alignas(8) _Alignas(8)
float x; float x;
float y; float y;
} t_fvec2; } Vec2;
/* a point in some three dimension space (floating point) */ /* a point in some three dimension space (floating point) */
/* y goes up, x goes to the right */ /* y goes up, x goes to the right */
typedef struct fvec3 { typedef struct Vec3 {
_Alignas(16) _Alignas(16)
float x; float x;
float y; float y;
float z; float z;
} t_fvec3; } Vec3;
/* a point in some three dimension space (floating point) */ /* a point in some three dimension space (floating point) */
/* y goes up, x goes to the right */ /* y goes up, x goes to the right */
typedef struct fvec4 { typedef struct Vec4 {
_Alignas(16) _Alignas(16)
float x; float x;
float y; float y;
float z; float z;
float w; float w;
} t_fvec4; } Vec4;
/* a point in some space (short) */ /* a point in some space (short) */
typedef struct shvec2 { typedef struct Vec2sh {
_Alignas(4) _Alignas(4)
int16_t x; int16_t x;
int16_t y; int16_t y;
} t_shvec2; } Vec2sh;
/* aren't macros to prevent double evaluation with side effects */ /* aren't macros to prevent double evaluation with side effects */
/* maybe could be inlined? i hope LTO will resolve this */ /* maybe could be inlined? i hope LTO will resolve this */
static inline t_fvec2 fvec2_from_vec2(t_vec2 vec) { static inline Vec2 vec2_from_vec2i(Vec2i vec) {
return (t_fvec2) { return (Vec2) {
.x = (float)vec.x, .x = (float)vec.x,
.y = (float)vec.y, .y = (float)vec.y,
}; };
} }
static inline t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { static inline Vec2 vec2_from_vec2sh(Vec2sh vec) {
return (t_fvec2) { return (Vec2) {
.x = (float)vec.x, .x = (float)vec.x,
.y = (float)vec.y, .y = (float)vec.y,
}; };
} }
static inline t_fvec3 fvec3_add(t_fvec3 a, t_fvec3 b) { static inline Vec3 vec3_add(Vec3 a, Vec3 b) {
return (t_fvec3) { a.x + b.x, a.y + b.y, a.z + b.z }; 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) { static inline Vec3 vec3_sub(Vec3 a, Vec3 b) {
return (t_fvec3) { a.x - b.x, a.y - b.y, a.z - b.z }; 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) { static inline Vec2 vec2_div(Vec2 a, Vec2 b) {
return (t_fvec2) { a.x / b.x, a.y / b.y }; return (Vec2) { a.x / b.x, a.y / b.y };
} }
static inline t_fvec2 fvec2_scale(t_fvec2 a, float s) { static inline Vec2 vec2_scale(Vec2 a, float s) {
return (t_fvec2) { a.x * s, a.y * s }; return (Vec2) { a.x * s, a.y * s };
} }
static inline t_fvec3 fvec3_scale(t_fvec3 a, float s) { static inline Vec3 vec3_scale(Vec3 a, float s) {
return (t_fvec3) { a.x * s, a.y * s, a.z * 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; 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) { static inline Vec3 vec3_cross(Vec3 a, Vec3 b) {
return (t_fvec3) { return (Vec3) {
a.y * b.z - a.z * b.y, a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z, a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x, 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? */ /* TODO: fast_sqrt version? */
static inline t_fvec3 fvec3_norm(t_fvec3 a) { static inline Vec3 vec3_norm(Vec3 a) {
const float n = sqrtf(fvec3_dot(a, a)); const float n = sqrtf(vec3_dot(a, a));
/* TODO: do we need truncating over epsilon as cglm does? */ /* 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 */ /* from cglm */
t_fvec3 v1, v2, k; Vec3 v1, v2, k;
float c, s; float c, s;
c = cosf(angle); c = cosf(angle);
s = sinf(angle); s = sinf(angle);
k = fvec3_norm(axis); k = vec3_norm(axis);
/* Right Hand, Rodrigues' rotation formula: /* Right Hand, Rodrigues' rotation formula:
v = v*cos(t) + (kxv)sin(t) + k*(k.v)(1 - cos(t)) 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 = vec3_cross(k, v);
v2 = fvec3_scale(v2, s); 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)); v2 = vec3_scale(k, vec3_dot(k, v) * (1.0f - c));
v = fvec3_add(v1, v2); v = vec3_add(v1, v2);
return v; return v;
} }
#define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2), \ #define m_vec2_from(p_any_vec2) (_Generic((p_any_vec2), \
t_vec2: fvec2_from_vec2, \ Vec2i: vec2_from_vec2i, \
t_shvec2: fvec2_from_shvec2 \ Vec2sh: vec2_from_vec2sh \
)(p_any_vec2)) )(p_any_vec2))
#define m_vec_sub(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ #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)) )(p_any_vec0, p_any_vec1))
#define m_vec_div(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ #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)) )(p_any_vec0, p_any_vec1))
#define m_vec_scale(p_any_vec, p_any_scalar) (_Generic((p_any_vec), \ #define m_vec_scale(p_any_vec, p_any_scalar) (_Generic((p_any_vec), \
t_fvec2: fvec2_scale, \ Vec2: vec2_scale, \
t_fvec3: fvec3_scale \ Vec3: vec3_scale \
)(p_any_vec, p_any_scalar)) )(p_any_vec, p_any_scalar))
#define m_vec_dot(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ #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)) )(p_any_vec0, p_any_vec1))
#define m_vec_cross(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ #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)) )(p_any_vec0, p_any_vec1))
#define m_vec_norm(p_any_vec) (_Generic((p_any_vec), \ #define m_vec_norm(p_any_vec) (_Generic((p_any_vec), \
t_fvec3: fvec3_norm \ Vec3: vec3_norm \
)(p_any_vec)) )(p_any_vec))
#endif #endif

View File

@ -7,14 +7,14 @@
#include <stb_ds.h> #include <stb_ds.h>
void push_circle(t_fvec2 position, float radius, t_color color) { void push_circle(Vec2 position, float radius, Color color) {
struct circle_primitive circle = { CirclePrimitive circle = {
.radius = radius, .radius = radius,
.color = color, .color = color,
.position = position, .position = position,
}; };
struct primitive_2d primitive = { Primitive2D primitive = {
.type = PRIMITIVE_2D_CIRCLE, .type = PRIMITIVE_2D_CIRCLE,
.circle = circle, .circle = circle,
}; };
@ -24,8 +24,8 @@ void push_circle(t_fvec2 position, float radius, t_color color) {
/* TODO: caching and reuse scheme */ /* TODO: caching and reuse scheme */
/* vertices_out and indices_out MUST BE FREED */ /* vertices_out and indices_out MUST BE FREED */
void create_circle_geometry(t_fvec2 position, void create_circle_geometry(Vec2 position,
t_color color, Color color,
float radius, float radius,
size_t num_vertices, size_t num_vertices,
SDL_Vertex **vertices_out, SDL_Vertex **vertices_out,

View File

@ -2,7 +2,7 @@
#include "twn_util.h" #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; GLuint texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, 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); 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); glBindTexture(GL_TEXTURE_2D, texture);
int format_internal, format; 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); glBindTexture(GL_TEXTURE_2D, texture);
} }

View File

@ -11,50 +11,50 @@
/* TODO: use int16_t for uvs */ /* TODO: use int16_t for uvs */
/* TODO: use packed types? */ /* TODO: use packed types? */
/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ /* 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 */ /* upper-left */
t_fvec2 v0; Vec2 v0;
t_fvec2 uv0; Vec2 uv0;
t_color c0; Color c0;
/* bottom-left */ /* bottom-left */
t_fvec2 v1; Vec2 v1;
t_fvec2 uv1; Vec2 uv1;
t_color c1; Color c1;
/* bottom-right */ /* bottom-right */
t_fvec2 v2; Vec2 v2;
t_fvec2 uv2; Vec2 uv2;
t_color c2; Color c2;
/* upper-right */ /* upper-right */
t_fvec2 v3; Vec2 v3;
t_fvec2 uv3; Vec2 uv3;
t_color c3; Color c3;
}; } ElementIndexedQuad;
struct element_indexed_quad_without_color { typedef struct ElementIndexedQuadWithoutColor {
/* upper-left */ /* upper-left */
t_fvec2 v0; Vec2 v0;
t_fvec2 uv0; Vec2 uv0;
/* bottom-left */ /* bottom-left */
t_fvec2 v1; Vec2 v1;
t_fvec2 uv1; Vec2 uv1;
/* bottom-right */ /* bottom-right */
t_fvec2 v2; Vec2 v2;
t_fvec2 uv2; Vec2 uv2;
/* upper-right */ /* upper-right */
t_fvec2 v3; Vec2 v3;
t_fvec2 uv3; Vec2 uv3;
}; } ElementIndexedQuadWithoutColor;
typedef enum { typedef enum {
PIPELINE_NO, PIPELINE_NO,
PIPELINE_SPACE, PIPELINE_SPACE,
PIPELINE_2D, PIPELINE_2D,
} pipeline; } Pipeline;
static pipeline pipeline_last_used = PIPELINE_NO; static Pipeline pipeline_last_used = PIPELINE_NO;
void use_space_pipeline(void) { 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 */ /* client memory needs to be reachable on glDraw*, so */
static float vertices[6 * 2]; 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, glColor4ub(rectangle->color.r, rectangle->color.g,
rectangle->color.b, rectangle->color.a); 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; SDL_Vertex *vertices = NULL;
int *indices = NULL; int *indices = NULL;
int num_vertices = (int)circle->radius; 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) { if (mode == TEXTURE_MODE_GHOSTLY) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 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); glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW);
void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
if (!mapping) if (!mapping)
CRY("build_vertex_buffer", "Error mapping a vertex array buffer"); CRY("build_vertex_buffer", "Error mapping a vertex array buffer");
return (vertex_buffer_builder) { return (VertexBufferBuilder) {
.mapping = mapping, .mapping = mapping,
.bytes_left = bytes, .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) { void *bytes, size_t size) {
if (builder->bytes_left == 0) if (builder->bytes_left == 0)
return false; 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[], void finally_render_sprites(const Primitive2D primitives[],
const struct sprite_batch batch, const struct SpriteBatch batch,
const vertex_buffer buffer) const VertexBuffer buffer)
{ {
/* TODO: maybe do, dunno */ /* TODO: maybe do, dunno */
// glBindBuffer(GL_VERTEX_ARRAY, vertex_buffer); // glBindBuffer(GL_VERTEX_ARRAY, vertex_buffer);
@ -250,13 +250,13 @@ void finally_render_sprites(const struct primitive_2d primitives[],
GLsizei uvoff; GLsizei uvoff;
if (!batch.constant_colored) { if (!batch.constant_colored) {
off = offsetof(struct element_indexed_quad, v1); off = offsetof(ElementIndexedQuad, v1);
voff = offsetof(struct element_indexed_quad, v0); voff = offsetof(ElementIndexedQuad, v0);
uvoff = offsetof(struct element_indexed_quad, uv0); uvoff = offsetof(ElementIndexedQuad, uv0);
} else { } else {
off = offsetof(struct element_indexed_quad_without_color, v1); off = offsetof(ElementIndexedQuadWithoutColor, v1);
voff = offsetof(struct element_indexed_quad_without_color, v0); voff = offsetof(ElementIndexedQuadWithoutColor, v0);
uvoff = offsetof(struct element_indexed_quad_without_color, uv0); uvoff = offsetof(ElementIndexedQuadWithoutColor, uv0);
} }
/* vertex specification */ /* vertex specification */
@ -278,7 +278,7 @@ void finally_render_sprites(const struct primitive_2d primitives[],
glColorPointer(4, glColorPointer(4,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
off, off,
(void *)offsetof(struct element_indexed_quad, c0)); (void *)offsetof(ElementIndexedQuad, c0));
} else } else
glColor4ub(primitives[0].sprite.color.r, glColor4ub(primitives[0].sprite.color.r,
primitives[0].sprite.color.g, 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) if (batch.constant_colored)
return sizeof (struct element_indexed_quad_without_color); return sizeof (ElementIndexedQuadWithoutColor);
else else
return sizeof (struct element_indexed_quad); return sizeof (ElementIndexedQuad);
} }
bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch, bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
vertex_buffer_builder *builder, VertexBufferBuilder *builder,
t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3, Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3, Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
t_color color) Color color)
{ {
if (!batch.constant_colored) { if (!batch.constant_colored) {
struct element_indexed_quad buffer_element = { ElementIndexedQuad buffer_element = {
.v0 = v0, .v0 = v0,
.v1 = v1, .v1 = v1,
.v2 = v2, .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); return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element);
} else { } else {
struct element_indexed_quad_without_color buffer_element = { ElementIndexedQuadWithoutColor buffer_element = {
.v0 = v0, .v0 = v0,
.v1 = v1, .v1 = v1,
.v2 = v2, .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, void finally_draw_uncolored_space_traingle_batch(const MeshBatch *batch,
const t_texture_key texture_key, const TextureKey texture_key,
const vertex_buffer buffer) const VertexBuffer buffer)
{ {
const size_t primitives_len = arrlenu(batch->primitives); 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); glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, glVertexPointer(3,
GL_FLOAT, GL_FLOAT,
offsetof(struct uncolored_space_triangle_payload, v1), offsetof(struct UncoloredSpaceTrianglePayload, v1),
(void *)offsetof(struct uncolored_space_triangle_payload, v0)); (void *)offsetof(struct UncoloredSpaceTrianglePayload, v0));
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0); glClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, glTexCoordPointer(2,
GL_FLOAT, GL_FLOAT,
offsetof(struct uncolored_space_triangle_payload, v1), offsetof(struct UncoloredSpaceTrianglePayload, v1),
(void *)offsetof(struct uncolored_space_triangle_payload, uv0)); (void *)offsetof(struct UncoloredSpaceTrianglePayload, uv0));
/* commit for drawing */ /* commit for drawing */
glDrawArrays(GL_TRIANGLES, 0, 3 * (GLint)primitives_len); 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, bool push_text_payload_to_vertex_buffer_builder(FontData const *font_data,
vertex_buffer_builder *builder, VertexBufferBuilder *builder,
stbtt_aligned_quad quad) stbtt_aligned_quad quad)
{ {
(void)font_data; (void)font_data;
struct element_indexed_quad_without_color buffer_element = { ElementIndexedQuadWithoutColor buffer_element = {
.v0 = (t_fvec2){ quad.x0, quad.y0 }, .v0 = (Vec2){ quad.x0, quad.y0 },
.v1 = (t_fvec2){ quad.x1, quad.y0 }, .v1 = (Vec2){ quad.x1, quad.y0 },
.v2 = (t_fvec2){ quad.x1, quad.y1 }, .v2 = (Vec2){ quad.x1, quad.y1 },
.v3 = (t_fvec2){ quad.x0, quad.y1 }, .v3 = (Vec2){ quad.x0, quad.y1 },
.uv0 = (t_fvec2){ quad.s0, quad.t0 }, .uv0 = (Vec2){ quad.s0, quad.t0 },
.uv1 = (t_fvec2){ quad.s1, quad.t0 }, .uv1 = (Vec2){ quad.s1, quad.t0 },
.uv2 = (t_fvec2){ quad.s1, quad.t1 }, .uv2 = (Vec2){ quad.s1, quad.t1 },
.uv3 = (t_fvec2){ quad.s0, quad.t1 }, .uv3 = (Vec2){ quad.s0, quad.t1 },
}; };
return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); 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, size_t len,
t_color color, Color color,
vertex_buffer buffer) VertexBuffer buffer)
{ {
(void)buffer; (void)buffer;
@ -429,15 +429,15 @@ void finally_draw_text(struct font_data const *font_data,
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, glVertexPointer(2,
GL_FLOAT, GL_FLOAT,
offsetof(struct element_indexed_quad_without_color, v1), offsetof(ElementIndexedQuadWithoutColor, v1),
(void *)(size_t)offsetof(struct element_indexed_quad_without_color, v0)); (void *)(size_t)offsetof(ElementIndexedQuadWithoutColor, v0));
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE0); glClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, glTexCoordPointer(2,
GL_FLOAT, GL_FLOAT,
offsetof(struct element_indexed_quad_without_color, v1), offsetof(ElementIndexedQuadWithoutColor, v1),
(void *)(size_t)offsetof(struct element_indexed_quad_without_color, uv0)); (void *)(size_t)offsetof(ElementIndexedQuadWithoutColor, uv0));
bind_quad_element_buffer(); 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) { size_t get_text_payload_size(void) {
return sizeof (struct element_indexed_quad_without_color); return sizeof (ElementIndexedQuadWithoutColor);
} }

View File

@ -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; GLuint result;
glGenBuffers(1, &result); glGenBuffers(1, &result);
return result; return result;
} }
void delete_vertex_buffer(vertex_buffer buffer) { void delete_vertex_buffer(VertexBuffer buffer) {
glDeleteBuffers(1, &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); glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW);
} }

View File

@ -3,19 +3,19 @@
#include <stdbool.h> #include <stdbool.h>
typedef GLuint gpu_texture; typedef GLuint GPUTexture;
enum texture_filter { typedef enum TextureFilter {
TEXTURE_FILTER_NEAREAST, TEXTURE_FILTER_NEAREAST,
TEXTURE_FILTER_LINEAR, 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 #endif

View File

@ -17,8 +17,8 @@
/* TODO: have a default initialized one */ /* TODO: have a default initialized one */
t_matrix4 camera_projection_matrix; Matrix4 camera_projection_matrix;
t_matrix4 camera_look_at_matrix; Matrix4 camera_look_at_matrix;
void render_queue_clear(void) { void render_queue_clear(void) {
@ -41,13 +41,13 @@ void render_queue_clear(void) {
/* rectangle */ /* rectangle */
void push_rectangle(t_frect rect, t_color color) { void push_rectangle(Rect rect, Color color) {
struct rect_primitive rectangle = { RectPrimitive rectangle = {
.rect = rect, .rect = rect,
.color = color, .color = color,
}; };
struct primitive_2d primitive = { Primitive2D primitive = {
.type = PRIMITIVE_2D_RECT, .type = PRIMITIVE_2D_RECT,
.rect = rectangle, .rect = rectangle,
}; };
@ -64,11 +64,11 @@ static void render_2d(void) {
size_t batch_count = 0; size_t batch_count = 0;
for (size_t i = 0; i < render_queue_len; ++i) { 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) { switch (current->type) {
case PRIMITIVE_2D_SPRITE: { case PRIMITIVE_2D_SPRITE: {
const struct sprite_batch batch = const struct SpriteBatch batch =
collect_sprite_batch(current, render_queue_len - i); collect_sprite_batch(current, render_queue_len - i);
/* TODO: what's even the point? just use OR_EQUAL comparison */ /* 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? */ /* TODO: skip recaulculating if it's the same? */
camera_projection_matrix = camera_perspective(camera); camera_projection_matrix = camera_perspective(camera);
camera_look_at_matrix = camera_look_at(camera); camera_look_at_matrix = camera_look_at(camera);

View File

@ -2,6 +2,7 @@
#define TWN_RENDERING_C_H #define TWN_RENDERING_C_H
#include "twn_textures_c.h" #include "twn_textures_c.h"
#include "twn_text_c.h"
#include "twn_util.h" #include "twn_util.h"
#include "twn_option.h" #include "twn_option.h"
@ -16,107 +17,107 @@
#include <stdbool.h> #include <stdbool.h>
extern t_matrix4 camera_projection_matrix; extern Matrix4 camera_projection_matrix;
extern t_matrix4 camera_look_at_matrix; extern Matrix4 camera_look_at_matrix;
#define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) #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; size_t bytes_left;
void *mapping; void *mapping;
} vertex_buffer_builder; } VertexBufferBuilder;
struct sprite_primitive { typedef struct SpritePrimitive {
t_frect rect; Rect rect;
t_color color; Color color;
float rotation; float rotation;
t_texture_key texture_key; TextureKey texture_key;
bool flip_x; bool flip_x;
bool flip_y; bool flip_y;
bool repeat; bool repeat;
m_option_list( m_option_list(
t_frect, texture_region ) Rect, texture_region )
}; } SpritePrimitive;
struct rect_primitive { typedef struct RectPrimitive {
t_frect rect; Rect rect;
t_color color; Color color;
}; } RectPrimitive;
struct circle_primitive { typedef struct CirclePrimitive {
float radius; float radius;
t_color color; Color color;
t_fvec2 position; Vec2 position;
}; } CirclePrimitive;
struct text_primitive { typedef struct TextPrimitive {
t_color color; Color color;
t_fvec2 position; Vec2 position;
char *text; char *text;
const char *font; const char *font;
int height_px; int height_px;
}; } TextPrimitive;
enum primitive_2d_type { typedef enum Primitive2DType {
PRIMITIVE_2D_SPRITE, PRIMITIVE_2D_SPRITE,
PRIMITIVE_2D_RECT, PRIMITIVE_2D_RECT,
PRIMITIVE_2D_CIRCLE, PRIMITIVE_2D_CIRCLE,
PRIMITIVE_2D_TEXT, PRIMITIVE_2D_TEXT,
}; } Primitive2DType;
struct primitive_2d { typedef struct Primitive2D {
enum primitive_2d_type type; Primitive2DType type;
union { union {
struct sprite_primitive sprite; SpritePrimitive sprite;
struct rect_primitive rect; RectPrimitive rect;
struct circle_primitive circle; CirclePrimitive circle;
struct text_primitive text; TextPrimitive text;
};
}; };
} Primitive2D;
/* union for in-place recalculation of texture coordinates */ /* 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 */ /* pending for sending, uvs are not final as texture atlases could update */
struct uncolored_space_triangle_primitive { struct UncoloredSpaceTrianglePrimitive {
t_fvec3 v0; Vec3 v0;
t_fvec2 uv0; /* in pixels */ Vec2 uv0; /* in pixels */
t_fvec3 v1; Vec3 v1;
t_fvec2 uv1; /* in pixels */ Vec2 uv1; /* in pixels */
t_fvec3 v2; Vec3 v2;
t_fvec2 uv2; /* in pixels */ Vec2 uv2; /* in pixels */
} primitive; } primitive;
/* TODO: have it packed? */ /* TODO: have it packed? */
/* structure that is passed in opengl vertex array */ /* structure that is passed in opengl vertex array */
struct uncolored_space_triangle_payload { struct UncoloredSpaceTrianglePayload {
t_fvec3 v0; Vec3 v0;
t_fvec2 uv0; Vec2 uv0;
t_fvec3 v1; Vec3 v1;
t_fvec2 uv1; Vec2 uv1;
t_fvec3 v2; Vec3 v2;
t_fvec2 uv2; Vec2 uv2;
} payload; } payload;
}; };
/* batch of primitives with overlapping properties */ /* batch of primitives with overlapping properties */
struct mesh_batch { typedef struct MeshBatch {
uint8_t *primitives; uint8_t *primitives;
}; } MeshBatch;
struct mesh_batch_item { typedef struct MeshBatchItem {
t_texture_key key; TextureKey key;
struct mesh_batch value; struct MeshBatch value;
}; } MeshBatchItem;
struct text_cache { typedef struct TextCache {
struct font_data **data; struct FontData **data;
}; } TextCache;
/* renders the background, then the primitives in all render queues */ /* renders the background, then the primitives in all render queues */
@ -125,47 +126,47 @@ void render(void);
/* clears all render queues */ /* clears all render queues */
void render_queue_clear(void); void render_queue_clear(void);
void create_circle_geometry(t_fvec2 position, void create_circle_geometry(Vec2 position,
t_color color, Color color,
float radius, float radius,
size_t num_vertices, size_t num_vertices,
SDL_Vertex **vertices_out, SDL_Vertex **vertices_out,
int **indices_out); int **indices_out);
struct sprite_batch { struct SpriteBatch {
size_t size; /* how many primitives are in current batch */ 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 constant_colored; /* whether colored batch is uniformly colored */
bool repeat; /* whether repeat is needed */ 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[], void render_sprites(const Primitive2D primitives[],
const struct sprite_batch batch); const struct SpriteBatch batch);
void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, void draw_uncolored_space_traingle_batch(MeshBatch *batch,
t_texture_key texture_key); TextureKey texture_key);
/* text */ /* 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 */
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 */ /* 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 */ /* 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, void *bytes,
size_t size); size_t size);
@ -181,41 +182,41 @@ void set_depth_range(double low, double high);
void bind_quad_element_buffer(void); 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_space_pipeline(void);
void use_2d_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[], void finally_render_sprites(Primitive2D const primitives[],
struct sprite_batch batch, struct SpriteBatch batch,
vertex_buffer buffer); 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, bool push_sprite_payload_to_vertex_buffer_builder(struct SpriteBatch batch,
vertex_buffer_builder *builder, VertexBufferBuilder *builder,
t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3, Vec2 v0, Vec2 v1, Vec2 v2, Vec2 v3,
t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3, Vec2 uv0, Vec2 uv1, Vec2 uv2, Vec2 uv3,
t_color color); Color color);
void finally_draw_uncolored_space_traingle_batch(struct mesh_batch const *batch, void finally_draw_uncolored_space_traingle_batch(MeshBatch const *batch,
t_texture_key texture_key, TextureKey texture_key,
vertex_buffer buffer); VertexBuffer buffer);
size_t get_text_payload_size(void); size_t get_text_payload_size(void);
bool push_text_payload_to_vertex_buffer_builder(struct font_data const *font_data, bool push_text_payload_to_vertex_buffer_builder(FontData const *font_data,
vertex_buffer_builder *builder, VertexBufferBuilder *builder,
stbtt_aligned_quad quad); 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, size_t len,
t_color color, Color color,
vertex_buffer buffer); VertexBuffer buffer);
#endif #endif

View File

@ -17,10 +17,10 @@
*/ */
/* TODO: it might make sense to infer alpha channel presence / meaningfulness for textures in atlas */ /* 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 */ /* 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) { void push_sprite(const PushSpriteArgs args) {
struct sprite_primitive sprite = { SpritePrimitive sprite = {
.rect = args.rect, .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), .rotation = m_or(args, rotation, 0.0f),
.texture_key = textures_get_key(&ctx.texture_cache, args.path), .texture_key = textures_get_key(&ctx.texture_cache, args.path),
.flip_x = m_or(args, flip_x, false), .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) m_opt_from(texture_region, args, texture_region)
}; };
struct primitive_2d primitive = { Primitive2D primitive = {
.type = PRIMITIVE_2D_SPRITE, .type = PRIMITIVE_2D_SPRITE,
.sprite = 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 */ /* assumes that first primitive is already a sprite */
const uint16_t texture_key_id = primitives[0].sprite.texture_key.id; 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); 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), .mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key),
.constant_colored = true, .constant_colored = true,
.repeat = primitives[0].sprite.repeat, .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; len = QUAD_ELEMENT_BUFFER_LENGTH;
for (size_t i = 0; i < len; ++i) { 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 */ /* don't touch things other than sprites */
if (current->type != PRIMITIVE_2D_SPRITE) if (current->type != PRIMITIVE_2D_SPRITE)
break; break;
/* only collect the same blend modes */ /* 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) if (mode != batch.mode)
break; break;
@ -94,32 +94,32 @@ struct sprite_batch collect_sprite_batch(const struct primitive_2d primitives[],
/* assumes that orthogonal matrix setup is done already */ /* assumes that orthogonal matrix setup is done already */
void render_sprites(const struct primitive_2d primitives[], void render_sprites(const Primitive2D primitives[],
const struct sprite_batch batch) const struct SpriteBatch batch)
{ {
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */ /* 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) if (vertex_array == 0)
vertex_array = create_vertex_buffer(); vertex_array = create_vertex_buffer();
use_texture_mode(batch.mode); use_texture_mode(batch.mode);
const t_frect dims = const Rect dims =
textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key); textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key);
/* vertex population over a vertex buffer builder interface */ /* 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) { for (size_t i = 0; i < batch.size; ++i) {
/* render opaques front to back */ /* render opaques front to back */
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1; 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); 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) { if (!sprite.repeat) {
const float wr = srcrect.w / dims.w; 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; const float yr = srcrect.y / dims.h;
if (!m_is_set(sprite, texture_region)) { if (!m_is_set(sprite, texture_region)) {
uv0 = (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 = (t_fvec2){ xr + wr * sprite.flip_x, yr + hr * !sprite.flip_y }; uv1 = (Vec2){ xr + wr * sprite.flip_x, yr + hr * !sprite.flip_y };
uv2 = (t_fvec2){ xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y }; uv2 = (Vec2){ xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y };
uv3 = (t_fvec2){ xr + wr * !sprite.flip_x, yr + hr * sprite.flip_y }; uv3 = (Vec2){ xr + wr * !sprite.flip_x, yr + hr * sprite.flip_y };
} else { } else {
const float offx = (sprite.texture_region_opt.x / srcrect.w) * (srcrect.w / dims.w); 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 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 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); 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 }; uv0 = (Vec2){ 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 }; uv1 = (Vec2){ 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 }; uv2 = (Vec2){ 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 }; uv3 = (Vec2){ xr - offw + wr * !sprite.flip_x, yr + offy + hr * sprite.flip_y };
} }
} else { } else {
/* try fitting texture into supplied destination rectangle */ /* 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 rx = sprite.rect.w / srcrect.w;
const float ry = sprite.rect.h / srcrect.h; const float ry = sprite.rect.h / srcrect.h;
uv0 = (t_fvec2){ rx * sprite.flip_x, ry * sprite.flip_y }; uv0 = (Vec2){ rx * sprite.flip_x, ry * sprite.flip_y };
uv1 = (t_fvec2){ rx * sprite.flip_x, ry * !sprite.flip_y }; uv1 = (Vec2){ rx * sprite.flip_x, ry * !sprite.flip_y };
uv2 = (t_fvec2){ rx * !sprite.flip_x, ry * !sprite.flip_y }; uv2 = (Vec2){ rx * !sprite.flip_x, ry * !sprite.flip_y };
uv3 = (t_fvec2){ 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)) { if (m_is_set(sprite, texture_region)) {
/* displace origin */ /* 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? */ /* todo: fast PI/2 degree divisible rotations? */
if (sprite.rotation == 0.0f) { if (sprite.rotation == 0.0f) {
/* non-rotated case */ /* non-rotated case */
v0 = (t_fvec2){ sprite.rect.x, sprite.rect.y }; v0 = (Vec2){ sprite.rect.x, sprite.rect.y };
v1 = (t_fvec2){ sprite.rect.x, sprite.rect.y + sprite.rect.h }; v1 = (Vec2){ sprite.rect.x, sprite.rect.y + sprite.rect.h };
v2 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h }; v2 = (Vec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h };
v3 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y }; v3 = (Vec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y };
} else if (sprite.rect.w == sprite.rect.h) { } else if (sprite.rect.w == sprite.rect.h) {
/* rotated square case */ /* rotated square case */
const t_fvec2 c = frect_center(sprite.rect); const Vec2 c = frect_center(sprite.rect);
const t_fvec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); const Vec2 t = fast_cossine(sprite.rotation + (float)M_PI_4);
const t_fvec2 d = { const Vec2 d = {
.x = t.x * sprite.rect.w * (float)M_SQRT1_2, .x = t.x * sprite.rect.w * (float)M_SQRT1_2,
.y = t.y * sprite.rect.h * (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 }; v0 = (Vec2){ c.x - d.x, c.y - d.y };
v1 = (t_fvec2){ c.x - d.y, c.y + d.x }; v1 = (Vec2){ c.x - d.y, c.y + d.x };
v2 = (t_fvec2){ c.x + d.x, c.y + d.y }; v2 = (Vec2){ c.x + d.x, c.y + d.y };
v3 = (t_fvec2){ c.x + d.y, c.y - d.x }; v3 = (Vec2){ c.x + d.y, c.y - d.x };
} else { } else {
/* rotated non-square case*/ /* rotated non-square case*/
const t_fvec2 c = frect_center(sprite.rect); const Vec2 c = frect_center(sprite.rect);
const t_fvec2 t = fast_cossine(sprite.rotation); 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 }; v0 = (Vec2){ 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 }; v1 = (Vec2){ 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 }; v2 = (Vec2){ 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 }; 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); push_sprite_payload_to_vertex_buffer_builder(batch, &payload, v0, v1, v2, v3, uv0, uv1, uv2, uv3, sprite.color);

View File

@ -12,26 +12,8 @@
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1) #define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
struct font_data { static FontData *text_load_font_data(const char *path, int height_px) {
stbtt_packedchar char_data[NUM_DISPLAY_ASCII]; FontData *font_data = ccalloc(1, sizeof *font_data);
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);
font_data->file_path = path; font_data->file_path = path;
font_data->height_px = height_px; 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); free(font_data->file_bytes);
delete_gpu_texture(font_data->texture); delete_gpu_texture(font_data->texture);
free(font_data); free(font_data);
} }
static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 position, t_color color) { static void text_draw_with(FontData* font_data, char* text, Vec2 position, Color color) {
static vertex_buffer vertex_array = 0; VertexBuffer vertex_array = 0;
if (vertex_array == 0) if (vertex_array == 0)
vertex_array = create_vertex_buffer(); vertex_array = create_vertex_buffer();
const size_t len = SDL_strlen(text); 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) { for (size_t i = 0; i < len; ++i) {
const char c = text[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 */ /* HACK: stupid, bad, don't do this */
bool is_cached = false; bool is_cached = false;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { 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) { if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
is_cached = true; is_cached = true;
break; break;
} }
} }
if (!is_cached) { 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); arrput(ctx.text_cache.data, new_font_data);
} }
} }
static struct font_data *get_font_data(const char *font_path, int height_px) { static FontData *get_font_data(const char *font_path, int height_px) {
struct font_data *font_data = NULL; FontData *font_data = NULL;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
font_data = 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) { 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) { void render_text(const TextPrimitive *text) {
struct font_data *font_data = get_font_data(text->font, text->height_px); FontData *font_data = get_font_data(text->font, text->height_px);
text_draw_with(font_data, text->text, text->position, text->color); 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); 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) { for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
text_destroy_font_data(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); ensure_font_cache(font_path, height_px);
/* the string might not be around by the time it's used, so copy it */ /* 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); char *dup_string = cmalloc(strlen(string) + 1);
strcpy(dup_string, string); strcpy(dup_string, string);
struct text_primitive text = { TextPrimitive text = {
.color = color, .color = color,
.position = position, .position = position,
.text = dup_string, .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, .height_px = height_px,
}; };
struct primitive_2d primitive = { Primitive2D primitive = {
.type = PRIMITIVE_2D_TEXT, .type = PRIMITIVE_2D_TEXT,
.text = 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) { int get_text_width(char *string, int height_px, const char *font_path) {
ensure_font_cache(font_path, height_px); 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; int length = 0;
for (const char *p = string; *p != '\0'; ++p) { for (const char *p = string; *p != '\0'; ++p) {

View File

@ -1,3 +1,10 @@
#ifndef TWN_TEXT_C_H
#define TWN_TEXT_C_H
#include "twn_gpu_texture_c.h"
#include <stb_truetype.h> #include <stb_truetype.h>
@ -6,7 +13,7 @@
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1) #define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
struct font_data { typedef struct FontData {
stbtt_packedchar char_data[NUM_DISPLAY_ASCII]; stbtt_packedchar char_data[NUM_DISPLAY_ASCII];
stbtt_fontinfo info; stbtt_fontinfo info;
@ -14,11 +21,14 @@ struct font_data {
unsigned char *file_bytes; unsigned char *file_bytes;
size_t file_bytes_len; size_t file_bytes_len;
gpu_texture texture; GPUTexture texture;
int height_px; int height_px;
float scale_factor; float scale_factor;
int ascent; int ascent;
int descent; int descent;
int line_gap; int line_gap;
}; } FontData;
#endif

View File

@ -9,42 +9,42 @@
/* TODO: automatic handling of repeating textures */ /* TODO: automatic handling of repeating textures */
/* for that we could allocate a loner texture */ /* for that we could allocate a loner texture */
void unfurl_triangle(const char *path, void unfurl_triangle(const char *path,
t_fvec3 v0, Vec3 v0,
t_fvec3 v1, Vec3 v1,
t_fvec3 v2, Vec3 v2,
t_shvec2 uv0, Vec2sh uv0,
t_shvec2 uv1, Vec2sh uv1,
t_shvec2 uv2) 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) { if (!batch_p) {
struct mesh_batch item = {0}; struct MeshBatch item = {0};
hmput(ctx.uncolored_mesh_batches, texture_key, item); 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? */ 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, .v0 = v0,
.v1 = v1, .v1 = v1,
.v2 = v2, .v2 = v2,
.uv1 = m_to_fvec2(uv1), .uv1 = m_vec2_from(uv1),
.uv0 = m_to_fvec2(uv0), .uv0 = m_vec2_from(uv0),
.uv2 = m_to_fvec2(uv2), .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); arrpush(triangles, triangle);
batch_p->value.primitives = (uint8_t *)triangles; batch_p->value.primitives = (uint8_t *)triangles;
} }
void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, void draw_uncolored_space_traingle_batch(struct MeshBatch *batch,
t_texture_key texture_key) TextureKey texture_key)
{ {
static vertex_buffer vertex_array = 0; static VertexBuffer vertex_array = 0;
if (vertex_array == 0) if (vertex_array == 0)
vertex_array = create_vertex_buffer(); vertex_array = create_vertex_buffer();
@ -54,8 +54,8 @@ void draw_uncolored_space_traingle_batch(struct mesh_batch *batch,
if (primitives_len == 0) if (primitives_len == 0)
return; return;
const t_frect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); const Rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key);
const t_frect dims = textures_get_dims(&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 wr = srcrect.w / dims.w;
const float hr = srcrect.h / dims.h; 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 */ /* update pixel-based uvs to correspond with texture atlases */
for (size_t i = 0; i < primitives_len; ++i) { for (size_t i = 0; i < primitives_len; ++i) {
struct uncolored_space_triangle_payload *payload = struct UncoloredSpaceTrianglePayload *payload =
&((union uncolored_space_triangle *)batch->primitives)[i].payload; &((union UncoloredSpaceTriangle *)batch->primitives)[i].payload;
payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr; payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr;
payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr; 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; 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); finally_draw_uncolored_space_traingle_batch(batch, texture_key, vertex_array);
} }

View File

@ -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 frames without use, free the memory when threshold is met */
/* TODO: count repeated usages for sound effect cases with rendering to ram? */ /* TODO: count repeated usages for sound effect cases with rendering to ram? */
/* stores path to data hash, useful for sound effects */ /* stores path to data hash, useful for sound effects */
static struct audio_file_cache { static struct AudioFileCache {
char *key; char *key;
struct audio_file_cache_value { struct AudioFileCacheValue {
unsigned char *data; unsigned char *data;
size_t len; size_t len;
} value; } value;
@ -34,7 +34,7 @@ static struct audio_file_cache {
static int64_t get_audio_data(const char *path, unsigned char **data) { 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) { if (!cache) {
unsigned char *file; unsigned char *file;
int64_t len = file_to_bytes(path, &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; 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); shput(audio_file_cache, path, value);
*data = file; *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) { void audio_play(const char *path, const char *channel) {
const struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); const AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
if (!pair) if (!pair)
play_audio_ex(path, channel, get_default_audio_args()); audio_play_ex(path, channel, audio_get_default_args());
else 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); size_t path_len = strlen(path);
for (int i = 0; i < audio_file_type_count; ++i) { 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; continue;
if (strcmp(&path[path_len - ext_length], audio_exts[i]) == 0) 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; 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? */ /* TODO: error propagation and clearing of resources on partial success? */
/* or should we expect things to simply fail? */ /* 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) { switch (type) {
case audio_file_type_ogg: { case audio_file_type_ogg: {
unsigned char *data; 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); stb_vorbis_info info = stb_vorbis_get_info(handle);
return (union audio_context) { return (union AudioContext) {
.vorbis = { .vorbis = {
.data = data, .data = data,
.handle = handle, .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); xm_set_max_loop_count(handle, 1);
return (union audio_context) { return (union AudioContext) {
.xm = { .handle = handle } .xm = { .handle = handle }
}; };
} }
default: default:
CRY("Audio error", "Unhandled audio format (in init)"); 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) { switch (channel->file_type) {
case audio_file_type_ogg: { case audio_file_type_ogg: {
stb_vorbis_seek_start(channel->context.vorbis.handle); 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) { void audio_play_ex(const char *path, const char *channel, PlayAudioArgs args) {
struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
/* create a channel if it doesn't exist */ /* create a channel if it doesn't exist */
if (!pair) { if (!pair) {
t_audio_file_type file_type = infer_audio_file_type(path); AudioFileType file_type = infer_audio_file_type(path);
struct audio_channel new_channel = { AudioChannel new_channel = {
.args = args, .args = args,
.file_type = file_type, .file_type = file_type,
.context = init_audio_context(path, 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) { PlayAudioArgs *audio_get_args(const char *channel) {
struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
if (!pair) if (!pair)
return NULL; 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) { PlayAudioArgs audio_get_default_args(void) {
return (t_play_audio_args){ return (PlayAudioArgs){
.repeat = false, .repeat = false,
.crossfade = false, .crossfade = false,
.volume = 1.0f, .volume = 1.0f,
@ -209,7 +209,7 @@ t_play_audio_args get_default_audio_args(void) {
/* this assumes int16_t based streams */ /* 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 a,
uint8_t *restrict b, uint8_t *restrict b,
size_t frames) 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 */ /* 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, uint8_t *stream,
int len) 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) if (channel->args.volume < 0.0f || channel->args.volume > 1.0f)
log_warn("Volume argument is out of range for channel (%s)", channel->name); log_warn("Volume argument is out of range for channel (%s)", channel->name);

View File

@ -13,15 +13,15 @@
#include <stdint.h> #include <stdint.h>
typedef enum audio_file_type { typedef enum AudioFileType {
audio_file_type_ogg, audio_file_type_ogg,
audio_file_type_xm, audio_file_type_xm,
audio_file_type_count, audio_file_type_count,
audio_file_type_unknown, audio_file_type_unknown,
} t_audio_file_type; } AudioFileType;
union audio_context { union AudioContext {
struct { struct {
stb_vorbis *handle; stb_vorbis *handle;
unsigned char *data; unsigned char *data;
@ -35,19 +35,19 @@ union audio_context {
}; };
struct audio_channel { typedef struct AudioChannel {
t_play_audio_args args; PlayAudioArgs args;
enum audio_file_type file_type; AudioFileType file_type;
union audio_context context; /* interpreted by `file_type` value */ union AudioContext context; /* interpreted by `file_type` value */
const char *path; const char *path;
const char *name; const char *name;
}; } AudioChannel;
struct audio_channel_item { typedef struct AudioChannelItem {
char *key; char *key;
struct audio_channel value; struct AudioChannel value;
}; } AudioChannelItem;
void audio_callback(void *userdata, uint8_t *stream, int len); void audio_callback(void *userdata, uint8_t *stream, int len);

View File

@ -8,12 +8,12 @@
#define CAMERA_FAR_Z 100.0f #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 */ /* from cglm */
const t_fvec3 r = m_vec_norm(m_vec_cross(camera->target, camera->up)); const Vec3 r = m_vec_norm(m_vec_cross(camera->target, camera->up));
const t_fvec3 u = m_vec_cross(r, camera->target); const Vec3 u = m_vec_cross(r, camera->target);
t_matrix4 result; Matrix4 result;
result.row[0].x = r.x; result.row[0].x = r.x;
result.row[0].y = u.x; result.row[0].y = u.x;
@ -33,9 +33,9 @@ t_matrix4 camera_look_at(const t_camera *const camera) {
return result; return result;
} }
t_matrix4 camera_perspective(const t_camera *const camera) { Matrix4 camera_perspective(const Camera *const camera) {
/* from cglm */ /* from cglm */
t_matrix4 result = {0}; Matrix4 result = {0};
const float aspect = RENDER_BASE_RATIO; const float aspect = RENDER_BASE_RATIO;

View File

@ -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 */ /* 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 */ /* engine parts should never mix twn_engine_context_c.h with twn_context.h */
t_engine_ctx ctx = {0}; EngineContext ctx = {0};

View File

@ -3,6 +3,7 @@
#include "twn_context.h" #include "twn_context.h"
#include "twn_textures_c.h" #include "twn_textures_c.h"
#include "twn_audio_c.h"
#include "twn_engine_api.h" #include "twn_engine_api.h"
#include "twn_input.h" #include "twn_input.h"
#include "rendering/twn_rendering_c.h" #include "rendering/twn_rendering_c.h"
@ -13,20 +14,20 @@
#include <stdint.h> #include <stdint.h>
typedef struct engine_context { typedef struct EngineContext {
/* user code facing context */ /* user code facing context */
t_ctx game; Context game;
/* the program's actual argc and argv */ /* the program's actual argc and argv */
int argc; int argc;
char **argv; char **argv;
struct primitive_2d *render_queue_2d; struct Primitive2D *render_queue_2d;
struct mesh_batch_item *uncolored_mesh_batches; MeshBatchItem *uncolored_mesh_batches;
struct text_cache text_cache; struct TextCache text_cache;
struct texture_cache texture_cache; TextureCache texture_cache;
struct audio_channel_item *audio_channels; AudioChannelItem *audio_channels;
SDL_AudioDeviceID audio_device; SDL_AudioDeviceID audio_device;
int audio_stream_frequency; int audio_stream_frequency;
SDL_AudioFormat audio_stream_format; SDL_AudioFormat audio_stream_format;
@ -46,8 +47,8 @@ typedef struct engine_context {
bool resync_flag; bool resync_flag;
bool was_successful; bool was_successful;
} t_engine_ctx; } EngineContext;
TWN_API extern t_engine_ctx ctx; TWN_API extern EngineContext ctx;
#endif #endif

View File

@ -8,7 +8,7 @@
#include <stdlib.h> #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) { for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) {
switch (action->bindings[i].source) { switch (action->bindings[i].source) {
case BUTTON_SOURCE_NOT_SET: 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, char *action_name,
enum button_source source, ButtonSource source,
union button_code code) 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) { if (action_item == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return; return;
} }
struct action *action = &action_item->value; Action *action = &action_item->value;
/* check every binding to make sure this code isn't already bound */ /* check every binding to make sure this code isn't already bound */
for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) { 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) if (binding->source != source)
break; break;
@ -111,30 +111,30 @@ static void input_bind_code_to_action(struct input_state *input,
memmove(action->bindings, action->bindings + 1, shifted_size); memmove(action->bindings, action->bindings + 1, shifted_size);
} }
action->bindings[action->num_bindings++] = (struct button) { action->bindings[action->num_bindings++] = (Button) {
.source = source, .source = source,
.code = code, .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, char *action_name,
enum button_source source, ButtonSource source,
union button_code code) 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) { if (action_item == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return; return;
} }
struct action *action = &action_item->value; Action *action = &action_item->value;
/* check every binding to make sure this code is bound */ /* check every binding to make sure this code is bound */
size_t index = 0; size_t index = 0;
bool is_bound = false; bool is_bound = false;
for (index = 0; index < SDL_arraysize(action->bindings); ++index) { for (index = 0; index < SDL_arraysize(action->bindings); ++index) {
struct button *binding = &action->bindings[index]; Button *binding = &action->bindings[index];
if (binding->source != source) if (binding->source != source)
continue; 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); sh_new_strdup(input->action_hash);
} }
void input_state_deinit(struct input_state *input) { void input_state_deinit(InputState *input) {
shfree(input->action_hash); 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->keyboard_state = SDL_GetKeyboardState(NULL);
input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x, input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x,
&input->mouse_window_position.y); &input->mouse_window_position.y);
@ -193,74 +193,74 @@ void input_state_update(struct input_state *input) {
&input->mouse_relative_position.y); &input->mouse_relative_position.y);
for (size_t i = 0; i < shlenu(input->action_hash); ++i) { 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); 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, char *action_name,
SDL_Scancode scancode) SDL_Scancode scancode)
{ {
input_bind_code_to_action(input, input_bind_code_to_action(input,
action_name, action_name,
BUTTON_SOURCE_KEYBOARD_PHYSICAL, 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, char *action_name,
SDL_Scancode scancode) SDL_Scancode scancode)
{ {
input_unbind_code_from_action(input, input_unbind_code_from_action(input,
action_name, action_name,
BUTTON_SOURCE_KEYBOARD_PHYSICAL, 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, char *action_name,
uint8_t mouse_button) uint8_t mouse_button)
{ {
input_bind_code_to_action(input, input_bind_code_to_action(input,
action_name, action_name,
BUTTON_SOURCE_MOUSE, 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, char *action_name,
uint8_t mouse_button) uint8_t mouse_button)
{ {
input_unbind_code_from_action(input, input_unbind_code_from_action(input,
action_name, action_name,
BUTTON_SOURCE_MOUSE, 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) { if (shgeti(input->action_hash, action_name) >= 0) {
log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name); log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name);
return; 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) if (shdel(input->action_hash, action_name) == 0)
log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name); log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name);
} }
bool input_is_action_pressed(struct input_state *input, char *action_name) { bool input_is_action_pressed(InputState *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name); ActionHashItem *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) { if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return false; 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) { bool input_is_action_just_pressed(InputState *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name); ActionHashItem *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) { if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return false; 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) { bool input_is_action_just_released(InputState *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name); ActionHashItem *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) { if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return false; 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) { Vec2 input_get_action_position(InputState *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name); ActionHashItem *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) { if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return (t_fvec2) { 0 }; return (Vec2) { 0 };
} }
return action->value.position; 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; (void)input;
/* TODO: returns -1 if not supported, but like... do we care? */ /* TODO: returns -1 if not supported, but like... do we care? */
SDL_SetRelativeMouseMode(enabled); SDL_SetRelativeMouseMode(enabled);
} }
bool input_is_mouse_captured(struct input_state *input) { bool input_is_mouse_captured(InputState *input) {
(void)input; (void)input;
return SDL_GetRelativeMouseMode(); return SDL_GetRelativeMouseMode();
} }
void input_reset_state(struct input_state *input) { void input_reset_state(InputState *input) {
stbds_shfree(input->action_hash); stbds_shfree(input->action_hash);
} }

View File

@ -3,6 +3,6 @@
#include "twn_input.h" #include "twn_input.h"
void input_reset_state(struct input_state *input); void input_reset_state(InputState *input);
#endif #endif

View File

@ -167,7 +167,7 @@ static void main_loop(void) {
poll_events(); poll_events();
if (ctx.game.window_size_has_changed) { if (ctx.game.window_size_has_changed) {
t_vec2 size; Vec2i size;
SDL_GetWindowSize(ctx.window, &size.x, &size.y); SDL_GetWindowSize(ctx.window, &size.x, &size.y);
ctx.game.window_w = size.x; ctx.game.window_w = size.x;
ctx.game.window_h = size.y; ctx.game.window_h = size.y;

View File

@ -113,14 +113,14 @@ static SDL_Surface *create_surface(int width, int height) {
/* adds a new, blank atlas surface to the cache */ /* 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); SDL_Surface *new_atlas = create_surface(TEXTURE_ATLAS_SIZE, TEXTURE_ATLAS_SIZE);
arrput(cache->atlas_surfaces, new_atlas); arrput(cache->atlas_surfaces, new_atlas);
arrput(cache->atlas_textures, create_gpu_texture(TEXTURE_FILTER_NEAREAST, true)); 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); SDL_LockSurface(surface);
upload_gpu_texture(texture, surface->pixels, surface->format->BytesPerPixel, surface->w, surface->h); 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? */ /* 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 */ /* 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]; 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 */ /* 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; stbrp_rect *rects = NULL;
for (size_t i = 0; i < shlenu(cache->hash); ++i) { for (size_t i = 0; i < shlenu(cache->hash); ++i) {
if (cache->hash[i].value.loner_texture != 0) 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 */ /* updates the original rects array with the data from packed_rects */
/* returns true if all rects were packed successfully */ /* 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 !!! */ /* !!! do not grow either of the arrays !!! */
/* the reallocation will try to reassign the array pointer, to no effect. */ /* the reallocation will try to reassign the array pointer, to no effect. */
/* see stb_ds.h */ /* 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 */ /* 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; int r = 0;
for (size_t i = 0; i < shlenu(cache->hash); ++i) { for (size_t i = 0; i < shlenu(cache->hash); ++i) {
if (cache->hash[i].value.loner_texture != 0) if (cache->hash[i].value.loner_texture != 0)
continue; continue;
cache->hash[i].value.srcrect = (t_frect) { cache->hash[i].value.srcrect = (Rect) {
.x = (float)rects[r].x, .x = (float)rects[r].x,
.y = (float)rects[r].y, .y = (float)rects[r].y,
.w = (float)rects[r].w, .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; cache->window = window;
sh_new_arena(cache->hash); 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 */ /* free atlas textures */
for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) { for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) {
delete_gpu_texture(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"); PHYSFS_mkdir("/dump");
const char string_template[] = "/dump/atlas%zd.png"; 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; const uint32_t amask = surface->format->Amask;
if (amask == 0) if (amask == 0)
return TEXTURE_MODE_OPAQUE; return TEXTURE_MODE_OPAQUE;
enum texture_mode result = TEXTURE_MODE_OPAQUE; enum TextureMode result = TEXTURE_MODE_OPAQUE;
SDL_LockSurface(surface); SDL_LockSurface(surface);
for (int i = 0; i < surface->w * surface->h; ++i) { for (int i = 0; i < surface->w * surface->h; ++i) {
/* TODO: don't assume 32 bit depth ? */ /* 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); SDL_GetRGBA(((uint32_t *)surface->pixels)[i], surface->format, &color.r, &color.g, &color.b, &color.a);
if (color.a == 0) 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 */ /* no need to do anything if it was loaded already */
const ptrdiff_t i = shgeti(cache->hash, path); const ptrdiff_t i = shgeti(cache->hash, path);
if (i >= 0) if (i >= 0)
return (t_texture_key){ (uint16_t)i }; return (TextureKey){ (uint16_t)i };
SDL_Surface *surface = image_to_surface(path); SDL_Surface *surface = image_to_surface(path);
struct texture new_texture = { Texture new_texture = {
.data = surface, .data = surface,
.mode = infer_texture_mode(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); new_texture.loner_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, true);
upload_texture_from_surface(new_texture.loner_texture, surface); 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 { } else {
/* will be fully populated as the atlas updates */ /* will be fully populated as the atlas updates */
new_texture.atlas_index = cache->atlas_index; new_texture.atlas_index = cache->atlas_index;
cache->is_dirty = true; cache->is_dirty = true;
} }
shput(cache->hash, path, new_texture); 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) if (!cache->is_dirty)
return; return;
@ -421,14 +421,14 @@ static const char *rodata_start;
static const char *rodata_stop; static const char *rodata_stop;
static const char *last_path = NULL; static const char *last_path = NULL;
static t_texture_key last_texture; static TextureKey last_texture;
static struct ptr_to_texture { static struct PtrToTexture {
const void *key; const void *key;
t_texture_key value; TextureKey value;
} *ptr_to_texture; } *ptr_to_texture;
/* TODO: separate and reuse */ /* 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 (rodata_stop == NULL)
if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop)) if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop))
CRY("Section inference", ".rodata section lookup failed"); 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 #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 */ /* hash tables are assumed to be stable, so we just return indices */
const ptrdiff_t texture = shgeti(cache->hash, path); 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) { if (texture == -1) {
return textures_load(cache, path); return textures_load(cache, path);
} else } else
return (t_texture_key){ (uint16_t)texture }; return (TextureKey){ (uint16_t)texture };
} }
#endif /* generic implementation of textures_get_key() */ #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 (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture != 0) if (cache->hash[key.id].value.loner_texture != 0)
return -cache->hash[key.id].value.loner_texture; 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)) { if (m_texture_key_is_valid(key)) {
return cache->hash[key.id].value.srcrect; return cache->hash[key.id].value.srcrect;
} else { } else {
CRY("Texture lookup failed.", CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded."); "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 (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture != 0) if (cache->hash[key.id].value.loner_texture != 0)
return cache->hash[key.id].value.srcrect; return cache->hash[key.id].value.srcrect;
else else
return (t_frect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; return (Rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE };
} else { } else {
CRY("Texture lookup failed.", CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded."); "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 (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture == 0) if (cache->hash[key.id].value.loner_texture == 0)
bind_gpu_texture(cache->atlas_textures[cache->hash[key.id].value.atlas_index]); 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 */ /* 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 (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture == 0) { 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; 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); 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)) { if (m_texture_key_is_valid(key)) {
return cache->hash[key.id].value.mode; return cache->hash[key.id].value.mode;
} else { } 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; return cache->atlas_index + 1;
} }
void textures_reset_state(void) { void textures_reset_state(void) {
#if defined(__linux__) && !defined(HOT_RELOAD_SUPPORT) #if defined(__linux__) && !defined(HOT_RELOAD_SUPPORT)
last_path = NULL; last_path = NULL;
last_texture = (t_texture_key){0}; last_texture = (TextureKey){0};
shfree(ptr_to_texture); shfree(ptr_to_texture);
#endif #endif

View File

@ -11,47 +11,47 @@
#include <stdbool.h> #include <stdbool.h>
struct texture { typedef struct Texture {
t_frect srcrect; /* position in atlas */ Rect srcrect; /* position in atlas */
SDL_Surface *data; /* original image data */ SDL_Surface *data; /* original image data */
int atlas_index; int atlas_index;
gpu_texture loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */ GPUTexture loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */
gpu_texture repeating_texture; /* separately allocated texture, for loners == loner_texture */ GPUTexture repeating_texture; /* separately allocated Texture, for loners == loner_texture */
enum texture_mode mode; enum TextureMode mode;
}; } Texture;
struct texture_cache_item { typedef struct TextureCacheItem {
char *key; char *key;
struct texture value; struct Texture value;
}; } TextureCacheItem;
struct texture_cache { typedef struct TextureCache {
SDL_Window *window; /* from context */ SDL_Window *window; /* from context */
struct texture_cache_item *hash; struct TextureCacheItem *hash;
stbrp_node *node_buffer; /* used internally by stb_rect_pack */ stbrp_node *node_buffer; /* used internally by stb_rect_pack */
SDL_Surface **atlas_surfaces; 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 */ int atlas_index; /* atlas that is currently being built */
bool is_dirty; /* current atlas needs to be recreated */ bool is_dirty; /* current atlas needs to be recreated */
}; } TextureCache;
/* type safe structure for persistent texture handles */ /* 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 */ /* tests whether given key structure corresponds to any texture */
#define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1) #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_init(struct TextureCache *cache, SDL_Window *window);
void textures_cache_deinit(struct texture_cache *cache); void textures_cache_deinit(struct TextureCache *cache);
/* for debugging */ /* 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. */ /* 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 */ /* 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 */ /* repacks the current texture atlas based on the texture cache if needed */
/* any previously returned srcrect results are invalidated after that */ /* any previously returned srcrect results are invalidated after that */
/* call it every time before rendering */ /* 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 */ /* returns a persistent handle to some texture in cache, loading it if needed */
/* check the result with m_texture_key_is_valid() */ /* 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 */ /* 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) */ /* 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 */ /* 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 */ /* 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 */ /* 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 */ /* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */
void textures_reset_state(void); void textures_reset_state(void);

View File

@ -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 a_sdl = { a->x, a->y, a->w, a->h };
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h }; SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
SDL_Rect result_sdl = { 0 }; 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); bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl);
if (result != NULL) 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; 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 a_sdl = { a->x, a->y, a->w, a->h };
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h }; SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
SDL_FRect result_sdl = { 0 }; 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); bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
if (result != NULL) 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; 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 a_sdl = { a->x, a->y, a->w, a->h };
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h }; SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
return SDL_HasIntersection(&a_sdl, &b_sdl); 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 a_sdl = { a->x, a->y, a->w, a->h };
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h }; SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
return SDL_HasIntersectionF(&a_sdl, &b_sdl); return SDL_HasIntersectionF(&a_sdl, &b_sdl);
} }
t_frect to_frect(t_rect rect) { Rect to_frect(Recti rect) {
return (t_frect) { return (Rect) {
.h = (float)rect.h, .h = (float)rect.h,
.w = (float)rect.w, .w = (float)rect.w,
.x = (float)rect.x, .x = (float)rect.x,
@ -223,8 +223,8 @@ t_frect to_frect(t_rect rect) {
} }
t_fvec2 frect_center(t_frect rect) { Vec2 frect_center(Rect rect) {
return (t_fvec2){ return (Vec2){
.x = rect.x + rect.w / 2, .x = rect.x + rect.w / 2,
.y = rect.y + rect.h / 2, .y = rect.y + rect.h / 2,
}; };