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

View File

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

View File

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

View File

@ -8,8 +8,8 @@
#include <stb_perlin.h>
static void ingame_tick(struct state *state) {
struct scene_ingame *scn = (struct scene_ingame *)state->scene;
static void ingame_tick(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene;
world_drawdef(scn->world);
player_calc(scn->player);
@ -23,26 +23,26 @@ static void ingame_tick(struct state *state) {
const float yaw_rad = scn->yaw * (float)DEG2RAD;
const float pitch_rad = scn->pitch * (float)DEG2RAD;
scn->cam.target = m_vec_norm(((t_fvec3){
scn->cam.target = m_vec_norm(((Vec3){
cosf(yaw_rad) * cosf(pitch_rad),
sinf(pitch_rad),
sinf(yaw_rad) * cosf(pitch_rad)
}));
}
const t_fvec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up));
const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up));
const float speed = 0.04f; /* TODO: put this in a better place */
if (input_is_action_pressed(&ctx.input, "player_left"))
scn->cam.pos = fvec3_sub(scn->cam.pos, m_vec_scale(right, speed));
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed));
if (input_is_action_pressed(&ctx.input, "player_right"))
scn->cam.pos = fvec3_add(scn->cam.pos, m_vec_scale(right, speed));
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed));
if (input_is_action_pressed(&ctx.input, "player_forward"))
scn->cam.pos = fvec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
if (input_is_action_pressed(&ctx.input, "player_backward"))
scn->cam.pos = fvec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
if (input_is_action_pressed(&ctx.input, "player_jump"))
scn->cam.pos.y += speed;
@ -56,27 +56,27 @@ static void ingame_tick(struct state *state) {
}
m_sprite(m_set(path, "/assets/9slice.png"),
m_set(rect, ((t_frect){ 16, 16, 128, 128 })),
m_opt(texture_region, ((t_frect){ 0, 0, (float)(ctx.tick_count % 48), (float)(ctx.tick_count % 48) })));
m_set(rect, ((Rect){ 16, 16, 128, 128 })),
m_opt(texture_region, ((Rect){ 0, 0, (float)(ctx.tick_count % 48), (float)(ctx.tick_count % 48) })));
m_sprite(m_set(path, "/assets/light.png"),
m_set(rect, ((t_frect){ 48, 64, 64, 64 })),
m_opt(color, ((t_color){ 255, 0, 0, 255 })));
m_set(rect, ((Rect){ 48, 64, 64, 64 })),
m_opt(color, ((Color){ 255, 0, 0, 255 })));
m_sprite(m_set(path, "/assets/light.png"),
m_set(rect, ((t_frect){ 64, 64, 64, 64 })),
m_opt(color, ((t_color){ 0, 255, 0, 255 })));
m_set(rect, ((Rect){ 64, 64, 64, 64 })),
m_opt(color, ((Color){ 0, 255, 0, 255 })));
m_sprite(m_set(path, "/assets/light.png"),
m_set(rect, ((t_frect){ 80, 64, 64, 64 })),
m_opt(color, ((t_color){ 0, 0, 255, 255 })));
m_set(rect, ((Rect){ 80, 64, 64, 64 })),
m_opt(color, ((Color){ 0, 0, 255, 255 })));
m_sprite(m_set(path, "/assets/player/baron-walk.png"),
m_set(rect, ((t_frect){ 32, 32, 64, 64 })),
m_set(rect, ((Rect){ 32, 32, 64, 64 })),
m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 ));
m_sprite(m_set(path, "/assets/player/baron-walk.png"),
m_set(rect, ((t_frect){ 128, 32, 128, 64 })),
m_set(rect, ((Rect){ 128, 32, 128, 64 })),
m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 ));
set_camera(&scn->cam);
@ -91,51 +91,51 @@ static void ingame_tick(struct state *state) {
float d3 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 20 - 6;
unfurl_triangle("/assets/grass.gif",
(t_fvec3){ (float)x, d0, (float)y },
(t_fvec3){ (float)x + 1, d1, (float)y },
(t_fvec3){ (float)x, d3, (float)y - 1 },
(t_shvec2){ 1024, 768 },
(t_shvec2){ 1024, 0 },
(t_shvec2){ 0, 768 });
(Vec3){ (float)x, d0, (float)y },
(Vec3){ (float)x + 1, d1, (float)y },
(Vec3){ (float)x, d3, (float)y - 1 },
(Vec2sh){ 1024, 768 },
(Vec2sh){ 1024, 0 },
(Vec2sh){ 0, 768 });
unfurl_triangle("/assets/grass.gif",
(t_fvec3){ (float)x + 1, d1, (float)y },
(t_fvec3){ (float)x + 1, d2, (float)y - 1 },
(t_fvec3){ (float)x, d3, (float)y - 1 },
(t_shvec2){ 1024, 0 },
(t_shvec2){ 0, 0 },
(t_shvec2){ 0, 768 });
(Vec3){ (float)x + 1, d1, (float)y },
(Vec3){ (float)x + 1, d2, (float)y - 1 },
(Vec3){ (float)x, d3, (float)y - 1 },
(Vec2sh){ 1024, 0 },
(Vec2sh){ 0, 0 },
(Vec2sh){ 0, 768 });
}
}
}
static void ingame_end(struct state *state) {
struct scene_ingame *scn = (struct scene_ingame *)state->scene;
static void ingame_end(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene;
player_destroy(scn->player);
world_destroy(scn->world);
free(state->scene);
}
struct scene *ingame_scene(struct state *state) {
Scene *ingame_scene(State *state) {
(void)state;
struct scene_ingame *new_scene = ccalloc(1, sizeof *new_scene);
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene);
new_scene->base.tick = ingame_tick;
new_scene->base.end = ingame_end;
new_scene->world = world_create();
new_scene->player = player_create(new_scene->world);
new_scene->cam = (t_camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
play_audio_ex("music/mod65.xm", "soundtrack", (t_play_audio_args){
audio_play_ex("music/mod65.xm", "soundtrack", (PlayAudioArgs){
.repeat = true,
.volume = 1.0f
});
input_set_mouse_captured(&ctx.input, true);
return (struct scene *)new_scene;
return (Scene *)new_scene;
}

View File

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

View File

@ -2,7 +2,7 @@
#include "../state.h"
void switch_to(struct state *state, struct scene *(*scene_func)(struct state *)) {
void switch_to(State *state, Scene *(*scene_func)(State *)) {
state->next_scene = scene_func(state);
state->is_scene_switching = true;
}

View File

@ -2,15 +2,15 @@
#define SCENE_H
struct state;
struct scene {
typedef struct State State;
typedef struct Scene {
char *id;
void (*tick)(struct state *);
void (*end)(struct state *);
};
void (*tick)(State *);
void (*end)(State *);
} Scene;
void switch_to(struct state *state, struct scene *(*scene_func)(struct state *));
void switch_to(State *state, Scene *(*scene_func)(State *));
#endif

View File

@ -8,8 +8,8 @@
#include <stdio.h>
static void title_tick(struct state *state) {
struct scene_title *scn = (struct scene_title *)state->scene;
static void title_tick(State *state) {
SceneTitle *scn = (SceneTitle *)state->scene;
(void)scn;
if (input_is_action_just_pressed(&state->ctx->input, "ui_accept")) {
@ -17,7 +17,7 @@ static void title_tick(struct state *state) {
}
m_sprite("/assets/title.png", ((t_frect) {
m_sprite("/assets/title.png", ((Rect) {
((float)RENDER_BASE_WIDTH / 2) - ((float)320 / 2), 64, 320, 128 }));
@ -31,42 +31,42 @@ static void title_tick(struct state *state) {
int text_w = get_text_width(text_str, text_h, font);
push_rectangle(
(t_frect) {
(Rect) {
.x = 0,
.y = 0,
.w = (float)text_w,
.h = (float)text_h,
},
(t_color) { 0, 0, 0, 255 }
(Color) { 0, 0, 0, 255 }
);
push_text(
text_str,
(t_fvec2){ 0, 0 },
(Vec2){ 0, 0 },
text_h,
(t_color) { 255, 255, 255, 255 },
(Color) { 255, 255, 255, 255 },
font
);
free(text_str);
}
static void title_end(struct state *state) {
struct scene_title *scn = (struct scene_title *)state->scene;
static void title_end(State *state) {
SceneTitle *scn = (SceneTitle *)state->scene;
player_destroy(scn->player);
world_destroy(scn->world);
free(state->scene);
}
struct scene *title_scene(struct state *state) {
Scene *title_scene(State *state) {
(void)state;
struct scene_title *new_scene = ccalloc(1, sizeof *new_scene);
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene);
new_scene->base.tick = title_tick;
new_scene->base.end = title_end;
new_scene->world = world_create();
new_scene->player = player_create(new_scene->world);
return (struct scene *)new_scene;
return (Scene *)new_scene;
}

View File

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

View File

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

View File

@ -8,11 +8,11 @@
#include <tgmath.h>
static void update_tiles(struct world *world) {
static void update_tiles(struct World *world) {
for (size_t row = 0; row < world->tilemap_height; ++row) {
for (size_t col = 0; col < world->tilemap_width; ++col) {
world->tiles[(row * world->tilemap_width) + col] = (struct tile) {
.rect = (t_rect) {
world->tiles[(row * world->tilemap_width) + col] = (struct Tile) {
.rect = (Recti) {
.x = (int)col * world->tile_size,
.y = (int)row * world->tile_size,
.w = world->tile_size,
@ -25,30 +25,30 @@ static void update_tiles(struct world *world) {
}
static t_vec2 to_grid_location(struct world *world, float x, float y) {
return (t_vec2) {
static Vec2i to_grid_location(struct World *world, float x, float y) {
return (Vec2i) {
.x = (int)floor(x / (float)world->tile_size),
.y = (int)floor(y / (float)world->tile_size),
};
}
static void drawdef_debug(struct world *world) {
static void drawdef_debug(struct World *world) {
if (!ctx.debug) return;
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID) continue;
push_rectangle(to_frect(world->tiles[i].rect),
(t_color) { 255, 0, 255, 128 });
(Color) { 255, 0, 255, 128 });
}
}
struct world *world_create(void) {
struct world *world = cmalloc(sizeof *world);
struct World *world_create(void) {
struct World *world = cmalloc(sizeof *world);
*world = (struct world) {
*world = (struct World) {
.tiles = NULL,
.tile_size = 42,
.tilemap_width = 20,
@ -89,7 +89,7 @@ struct world *world_create(void) {
}
void world_destroy(struct world *world) {
void world_destroy(struct World *world) {
free(world->tiles);
for (size_t i = 0; i < world->tilemap_height; ++i) {
@ -101,7 +101,7 @@ void world_destroy(struct world *world) {
}
void world_drawdef(struct world *world) {
void world_drawdef(struct World *world) {
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID)
continue;
@ -113,7 +113,7 @@ void world_drawdef(struct world *world) {
}
bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *intersection) {
bool world_find_intersect_frect(struct World *world, Rect rect, Rect *intersection) {
bool is_intersecting = false;
const size_t tile_count = world->tilemap_height * world->tilemap_width;
@ -121,7 +121,7 @@ bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *inte
if (world->tiles[i].type == TILE_TYPE_VOID)
continue;
t_frect tile_frect = {
Rect tile_frect = {
.x = (float)(world->tiles[i].rect.x),
.y = (float)(world->tiles[i].rect.y),
.w = (float)(world->tiles[i].rect.w),
@ -129,7 +129,7 @@ bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *inte
};
if (intersection == NULL) {
t_frect temp;
Rect temp;
is_intersecting = overlap_frect(&rect, &tile_frect, &temp);
} else {
is_intersecting = overlap_frect(&rect, &tile_frect, intersection);
@ -143,7 +143,7 @@ bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *inte
}
bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *intersection) {
bool world_find_intersect_rect(struct World *world, Recti rect, Recti *intersection) {
bool is_intersecting = false;
const size_t tile_count = world->tilemap_height * world->tilemap_width;
@ -151,10 +151,10 @@ bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *interse
if (world->tiles[i].type == TILE_TYPE_VOID)
continue;
t_rect *tile_rect = &world->tiles[i].rect;
Recti *tile_rect = &world->tiles[i].rect;
if (intersection == NULL) {
t_rect temp;
Recti temp;
is_intersecting = overlap_rect(&rect, tile_rect, &temp);
} else {
is_intersecting = overlap_rect(&rect, tile_rect, intersection);
@ -168,21 +168,21 @@ bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *interse
}
bool world_is_tile_at(struct world *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y);
bool world_is_tile_at(struct World *world, float x, float y) {
Vec2i position_in_grid = to_grid_location(world, x, y);
return world->tilemap[position_in_grid.y][position_in_grid.x] != TILE_TYPE_VOID;
}
void world_place_tile(struct world *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y);
void world_place_tile(struct World *world, float x, float y) {
Vec2i position_in_grid = to_grid_location(world, x, y);
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_SOLID;
update_tiles(world);
}
void world_remove_tile(struct world *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y);
void world_remove_tile(struct World *world, float x, float y) {
Vec2i position_in_grid = to_grid_location(world, x, y);
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_VOID;
update_tiles(world);
}

View File

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

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

Binary file not shown.

View File

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

View File

@ -8,15 +8,15 @@
/* for example, perspective matrix only needs recaluclation on FOV change */
/* first person camera class */
typedef struct camera {
t_fvec3 pos; /* eye position */
t_fvec3 target; /* normalized target vector */
t_fvec3 up; /* normalized up vector */
typedef struct Camera {
Vec3 pos; /* eye position */
Vec3 target; /* normalized target vector */
Vec3 up; /* normalized up vector */
float fov; /* field of view, in radians */
} t_camera;
} Camera;
TWN_API t_matrix4 camera_look_at(const t_camera *camera);
TWN_API Matrix4 camera_look_at(const Camera *camera);
TWN_API t_matrix4 camera_perspective(const t_camera *const camera);
TWN_API Matrix4 camera_perspective(const Camera *const camera);
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
#include "twn_util.h"
gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps) {
GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps) {
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
@ -29,12 +29,12 @@ gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps
}
void delete_gpu_texture(gpu_texture texture) {
void delete_gpu_texture(GPUTexture texture) {
glDeleteTextures(1, &texture);
}
void upload_gpu_texture(gpu_texture texture, void *pixels, int channels, int width, int height) {
void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height) {
glBindTexture(GL_TEXTURE_2D, texture);
int format_internal, format;
@ -66,6 +66,6 @@ void upload_gpu_texture(gpu_texture texture, void *pixels, int channels, int wid
}
void bind_gpu_texture(gpu_texture texture) {
void bind_gpu_texture(GPUTexture texture) {
glBindTexture(GL_TEXTURE_2D, texture);
}

View File

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

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

View File

@ -3,19 +3,19 @@
#include <stdbool.h>
typedef GLuint gpu_texture;
typedef GLuint GPUTexture;
enum texture_filter {
typedef enum TextureFilter {
TEXTURE_FILTER_NEAREAST,
TEXTURE_FILTER_LINEAR,
};
} TextureFilter;
gpu_texture create_gpu_texture(enum texture_filter filter, bool generate_mipmaps);
GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps);
void delete_gpu_texture(gpu_texture texture);
void delete_gpu_texture(GPUTexture texture);
void upload_gpu_texture(gpu_texture texture, void *pixels, int channels, int width, int height);
void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height);
void bind_gpu_texture(gpu_texture texture);
void bind_gpu_texture(GPUTexture texture);
#endif

View File

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

View File

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

View File

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

View File

@ -12,26 +12,8 @@
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
struct font_data {
stbtt_packedchar char_data[NUM_DISPLAY_ASCII];
stbtt_fontinfo info;
const char *file_path;
unsigned char *file_bytes;
size_t file_bytes_len;
gpu_texture texture;
int height_px;
float scale_factor;
int ascent;
int descent;
int line_gap;
};
static struct font_data *text_load_font_data(const char *path, int height_px) {
struct font_data *font_data = ccalloc(1, sizeof *font_data);
static FontData *text_load_font_data(const char *path, int height_px) {
FontData *font_data = ccalloc(1, sizeof *font_data);
font_data->file_path = path;
font_data->height_px = height_px;
@ -77,21 +59,21 @@ static struct font_data *text_load_font_data(const char *path, int height_px) {
}
static void text_destroy_font_data(struct font_data *font_data) {
static void text_destroy_font_data(FontData *font_data) {
free(font_data->file_bytes);
delete_gpu_texture(font_data->texture);
free(font_data);
}
static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 position, t_color color) {
static vertex_buffer vertex_array = 0;
static void text_draw_with(FontData* font_data, char* text, Vec2 position, Color color) {
VertexBuffer vertex_array = 0;
if (vertex_array == 0)
vertex_array = create_vertex_buffer();
const size_t len = SDL_strlen(text);
vertex_buffer_builder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len);
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len);
for (size_t i = 0; i < len; ++i) {
const char c = text[i];
@ -131,21 +113,21 @@ static void ensure_font_cache(const char *font_path, int height_px) {
/* HACK: stupid, bad, don't do this */
bool is_cached = false;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
struct font_data *font_data = ctx.text_cache.data[i];
FontData *font_data = ctx.text_cache.data[i];
if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
is_cached = true;
break;
}
}
if (!is_cached) {
struct font_data *new_font_data = text_load_font_data(font_path, height_px);
FontData *new_font_data = text_load_font_data(font_path, height_px);
arrput(ctx.text_cache.data, new_font_data);
}
}
static struct font_data *get_font_data(const char *font_path, int height_px) {
struct font_data *font_data = NULL;
static FontData *get_font_data(const char *font_path, int height_px) {
FontData *font_data = NULL;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
font_data = ctx.text_cache.data[i];
if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
@ -156,18 +138,18 @@ static struct font_data *get_font_data(const char *font_path, int height_px) {
}
void render_text(const struct text_primitive *text) {
struct font_data *font_data = get_font_data(text->font, text->height_px);
void render_text(const TextPrimitive *text) {
FontData *font_data = get_font_data(text->font, text->height_px);
text_draw_with(font_data, text->text, text->position, text->color);
}
void text_cache_init(struct text_cache *cache) {
void text_cache_init(TextCache *cache) {
arrsetlen(cache->data, 0);
}
void text_cache_deinit(struct text_cache *cache) {
void text_cache_deinit(TextCache *cache) {
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
text_destroy_font_data(ctx.text_cache.data[i]);
}
@ -176,7 +158,7 @@ void text_cache_deinit(struct text_cache *cache) {
}
void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path) {
void push_text(char *string, Vec2 position, int height_px, Color color, const char *font_path) {
ensure_font_cache(font_path, height_px);
/* the string might not be around by the time it's used, so copy it */
@ -185,7 +167,7 @@ void push_text(char *string, t_fvec2 position, int height_px, t_color color, con
char *dup_string = cmalloc(strlen(string) + 1);
strcpy(dup_string, string);
struct text_primitive text = {
TextPrimitive text = {
.color = color,
.position = position,
.text = dup_string,
@ -193,7 +175,7 @@ void push_text(char *string, t_fvec2 position, int height_px, t_color color, con
.height_px = height_px,
};
struct primitive_2d primitive = {
Primitive2D primitive = {
.type = PRIMITIVE_2D_TEXT,
.text = text,
};
@ -204,7 +186,7 @@ void push_text(char *string, t_fvec2 position, int height_px, t_color color, con
int get_text_width(char *string, int height_px, const char *font_path) {
ensure_font_cache(font_path, height_px);
struct font_data *font_data = get_font_data(font_path, height_px);
FontData *font_data = get_font_data(font_path, height_px);
int length = 0;
for (const char *p = string; *p != '\0'; ++p) {

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>
@ -6,7 +13,7 @@
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
struct font_data {
typedef struct FontData {
stbtt_packedchar char_data[NUM_DISPLAY_ASCII];
stbtt_fontinfo info;
@ -14,11 +21,14 @@ struct font_data {
unsigned char *file_bytes;
size_t file_bytes_len;
gpu_texture texture;
GPUTexture texture;
int height_px;
float scale_factor;
int ascent;
int descent;
int line_gap;
};
} FontData;
#endif

View File

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

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@ -113,14 +113,14 @@ static SDL_Surface *create_surface(int width, int height) {
/* adds a new, blank atlas surface to the cache */
static void add_new_atlas(struct texture_cache *cache) {
static void add_new_atlas(TextureCache *cache) {
SDL_Surface *new_atlas = create_surface(TEXTURE_ATLAS_SIZE, TEXTURE_ATLAS_SIZE);
arrput(cache->atlas_surfaces, new_atlas);
arrput(cache->atlas_textures, create_gpu_texture(TEXTURE_FILTER_NEAREAST, true));
}
static void upload_texture_from_surface(gpu_texture texture, SDL_Surface *surface) {
static void upload_texture_from_surface(GPUTexture texture, SDL_Surface *surface) {
SDL_LockSurface(surface);
upload_gpu_texture(texture, surface->pixels, surface->format->BytesPerPixel, surface->w, surface->h);
@ -129,7 +129,7 @@ static void upload_texture_from_surface(gpu_texture texture, SDL_Surface *surfac
}
static void recreate_current_atlas_texture(struct texture_cache *cache) {
static void recreate_current_atlas_texture(TextureCache *cache) {
/* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */
/* for example, if full page of 64x64 tiles was already filled, there's no real reason to process them further */
SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index];
@ -164,7 +164,7 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) {
/* uses the textures currently in the cache to create an array of stbrp_rects */
static stbrp_rect *create_rects_from_cache(struct texture_cache *cache) {
static stbrp_rect *create_rects_from_cache(TextureCache *cache) {
stbrp_rect *rects = NULL;
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
if (cache->hash[i].value.loner_texture != 0)
@ -203,7 +203,7 @@ static stbrp_rect *filter_unpacked_rects(stbrp_rect *rects) {
/* updates the original rects array with the data from packed_rects */
/* returns true if all rects were packed successfully */
static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) {
static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) {
/* !!! do not grow either of the arrays !!! */
/* the reallocation will try to reassign the array pointer, to no effect. */
/* see stb_ds.h */
@ -229,13 +229,13 @@ static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_r
/* updates the atlas location of every rect in the cache */
static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) {
static void update_texture_rects_in_atlas(TextureCache *cache, stbrp_rect *rects) {
int r = 0;
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
if (cache->hash[i].value.loner_texture != 0)
continue;
cache->hash[i].value.srcrect = (t_frect) {
cache->hash[i].value.srcrect = (Rect) {
.x = (float)rects[r].x,
.y = (float)rects[r].y,
.w = (float)rects[r].w,
@ -247,7 +247,7 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec
}
void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
void textures_cache_init(TextureCache *cache, SDL_Window *window) {
cache->window = window;
sh_new_arena(cache->hash);
@ -258,7 +258,7 @@ void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
}
void textures_cache_deinit(struct texture_cache *cache) {
void textures_cache_deinit(TextureCache *cache) {
/* free atlas textures */
for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) {
delete_gpu_texture(cache->atlas_textures[i]);
@ -282,7 +282,7 @@ void textures_cache_deinit(struct texture_cache *cache) {
}
void textures_dump_atlases(struct texture_cache *cache) {
void textures_dump_atlases(TextureCache *cache) {
PHYSFS_mkdir("/dump");
const char string_template[] = "/dump/atlas%zd.png";
@ -307,18 +307,18 @@ void textures_dump_atlases(struct texture_cache *cache) {
}
static enum texture_mode infer_texture_mode(SDL_Surface *surface) {
static enum TextureMode infer_texture_mode(SDL_Surface *surface) {
const uint32_t amask = surface->format->Amask;
if (amask == 0)
return TEXTURE_MODE_OPAQUE;
enum texture_mode result = TEXTURE_MODE_OPAQUE;
enum TextureMode result = TEXTURE_MODE_OPAQUE;
SDL_LockSurface(surface);
for (int i = 0; i < surface->w * surface->h; ++i) {
/* TODO: don't assume 32 bit depth ? */
t_color color;
Color color;
SDL_GetRGBA(((uint32_t *)surface->pixels)[i], surface->format, &color.r, &color.g, &color.b, &color.a);
if (color.a == 0)
@ -335,14 +335,14 @@ static enum texture_mode infer_texture_mode(SDL_Surface *surface) {
}
static t_texture_key textures_load(struct texture_cache *cache, const char *path) {
static TextureKey textures_load(TextureCache *cache, const char *path) {
/* no need to do anything if it was loaded already */
const ptrdiff_t i = shgeti(cache->hash, path);
if (i >= 0)
return (t_texture_key){ (uint16_t)i };
return (TextureKey){ (uint16_t)i };
SDL_Surface *surface = image_to_surface(path);
struct texture new_texture = {
Texture new_texture = {
.data = surface,
.mode = infer_texture_mode(surface),
};
@ -357,18 +357,18 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path
}
new_texture.loner_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, true);
upload_texture_from_surface(new_texture.loner_texture, surface);
new_texture.srcrect = (t_frect) { .w = (float)surface->w, .h = (float)surface->h };
new_texture.srcrect = (Rect) { .w = (float)surface->w, .h = (float)surface->h };
} else {
/* will be fully populated as the atlas updates */
new_texture.atlas_index = cache->atlas_index;
cache->is_dirty = true;
}
shput(cache->hash, path, new_texture);
return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) };
return (TextureKey){ (uint16_t)shgeti(cache->hash, path) };
}
void textures_update_atlas(struct texture_cache *cache) {
void textures_update_atlas(TextureCache *cache) {
if (!cache->is_dirty)
return;
@ -421,14 +421,14 @@ static const char *rodata_start;
static const char *rodata_stop;
static const char *last_path = NULL;
static t_texture_key last_texture;
static struct ptr_to_texture {
static TextureKey last_texture;
static struct PtrToTexture {
const void *key;
t_texture_key value;
TextureKey value;
} *ptr_to_texture;
/* TODO: separate and reuse */
t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
TextureKey textures_get_key(TextureCache *cache, const char *path) {
if (rodata_stop == NULL)
if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop))
CRY("Section inference", ".rodata section lookup failed");
@ -458,7 +458,7 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
}
#else
t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
TextureKey textures_get_key(TextureCache *cache, const char *path) {
/* hash tables are assumed to be stable, so we just return indices */
const ptrdiff_t texture = shgeti(cache->hash, path);
@ -466,12 +466,12 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
if (texture == -1) {
return textures_load(cache, path);
} else
return (t_texture_key){ (uint16_t)texture };
return (TextureKey){ (uint16_t)texture };
}
#endif /* generic implementation of textures_get_key() */
int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key) {
int32_t textures_get_atlas_id(const TextureCache *cache, TextureKey key) {
if (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture != 0)
return -cache->hash[key.id].value.loner_texture;
@ -484,32 +484,32 @@ int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key k
}
}
t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) {
Rect textures_get_srcrect(const TextureCache *cache, TextureKey key) {
if (m_texture_key_is_valid(key)) {
return cache->hash[key.id].value.srcrect;
} else {
CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded.");
return (t_frect){ 0, 0, 0, 0 };
return (Rect){ 0, 0, 0, 0 };
}
}
t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key) {
Rect textures_get_dims(const TextureCache *cache, TextureKey key) {
if (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture != 0)
return cache->hash[key.id].value.srcrect;
else
return (t_frect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE };
return (Rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE };
} else {
CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded.");
return (t_frect){ 0, 0, 0, 0 };
return (Rect){ 0, 0, 0, 0 };
}
}
void textures_bind(const struct texture_cache *cache, t_texture_key key) {
void textures_bind(const TextureCache *cache, TextureKey key) {
if (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture == 0)
bind_gpu_texture(cache->atlas_textures[cache->hash[key.id].value.atlas_index]);
@ -523,7 +523,7 @@ void textures_bind(const struct texture_cache *cache, t_texture_key key) {
/* TODO: alternative schemes, such as: array texture, fragment shader and geometry division */
void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key) {
void textures_bind_repeating(const TextureCache *cache, TextureKey key) {
if (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture == 0) {
@ -533,9 +533,9 @@ void textures_bind_repeating(const struct texture_cache *cache, t_texture_key ke
return;
}
const struct texture texture = cache->hash[key.id].value;
const Texture texture = cache->hash[key.id].value;
const gpu_texture repeating_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, false);
const GPUTexture repeating_texture = create_gpu_texture(TEXTURE_FILTER_NEAREAST, false);
SDL_LockSurface(texture.data);
@ -561,7 +561,7 @@ void textures_bind_repeating(const struct texture_cache *cache, t_texture_key ke
}
enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key) {
TextureMode textures_get_mode(const TextureCache *cache, TextureKey key) {
if (m_texture_key_is_valid(key)) {
return cache->hash[key.id].value.mode;
} else {
@ -572,14 +572,14 @@ enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture
}
size_t textures_get_num_atlases(const struct texture_cache *cache) {
size_t textures_get_num_atlases(const TextureCache *cache) {
return cache->atlas_index + 1;
}
void textures_reset_state(void) {
#if defined(__linux__) && !defined(HOT_RELOAD_SUPPORT)
last_path = NULL;
last_texture = (t_texture_key){0};
last_texture = (TextureKey){0};
shfree(ptr_to_texture);
#endif

View File

@ -11,47 +11,47 @@
#include <stdbool.h>
struct texture {
t_frect srcrect; /* position in atlas */
typedef struct Texture {
Rect srcrect; /* position in atlas */
SDL_Surface *data; /* original image data */
int atlas_index;
gpu_texture loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */
gpu_texture repeating_texture; /* separately allocated texture, for loners == loner_texture */
enum texture_mode mode;
};
GPUTexture loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */
GPUTexture repeating_texture; /* separately allocated Texture, for loners == loner_texture */
enum TextureMode mode;
} Texture;
struct texture_cache_item {
typedef struct TextureCacheItem {
char *key;
struct texture value;
};
struct Texture value;
} TextureCacheItem;
struct texture_cache {
typedef struct TextureCache {
SDL_Window *window; /* from context */
struct texture_cache_item *hash;
struct TextureCacheItem *hash;
stbrp_node *node_buffer; /* used internally by stb_rect_pack */
SDL_Surface **atlas_surfaces;
gpu_texture *atlas_textures; /* shared by atlas textures */
GPUTexture *atlas_textures; /* shared by atlas textures */
int atlas_index; /* atlas that is currently being built */
bool is_dirty; /* current atlas needs to be recreated */
};
} TextureCache;
/* type safe structure for persistent texture handles */
typedef struct { uint16_t id; } t_texture_key;
typedef struct TextureKey { uint16_t id; } TextureKey;
/* tests whether given key structure corresponds to any texture */
#define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1)
void textures_cache_init(struct texture_cache *cache, SDL_Window *window);
void textures_cache_deinit(struct texture_cache *cache);
void textures_cache_init(struct TextureCache *cache, SDL_Window *window);
void textures_cache_deinit(struct TextureCache *cache);
/* for debugging */
void textures_dump_atlases(struct texture_cache *cache);
void textures_dump_atlases(struct TextureCache *cache);
/* loads an image if it isn't in the cache, otherwise a no-op. */
/* can be called from anywhere at any time after init, useful if you want to */
@ -61,30 +61,30 @@ void textures_dump_atlases(struct texture_cache *cache);
/* repacks the current texture atlas based on the texture cache if needed */
/* any previously returned srcrect results are invalidated after that */
/* call it every time before rendering */
void textures_update_atlas(struct texture_cache *cache);
void textures_update_atlas(TextureCache *cache);
/* returns a persistent handle to some texture in cache, loading it if needed */
/* check the result with m_texture_key_is_valid() */
t_texture_key textures_get_key(struct texture_cache *cache, const char *path);
TextureKey textures_get_key(TextureCache *cache, const char *path);
/* returns a rect in a texture cache of the given key */
t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key);
Rect textures_get_srcrect(const TextureCache *cache, TextureKey key);
/* returns a rect of dimensions of the whole texture (whole atlas) */
t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key);
Rect textures_get_dims(const TextureCache *cache, TextureKey key);
/* returns an identifier that is equal for all textures placed in the same atlas */
int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key);
int32_t textures_get_atlas_id(const TextureCache *cache, TextureKey key);
void textures_bind(const struct texture_cache *cache, t_texture_key key);
void textures_bind(const TextureCache *cache, TextureKey key);
void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key);
void textures_bind_repeating(const TextureCache *cache, TextureKey key);
/* returns helpful information about contents of alpha channel in given texture */
enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key);
TextureMode textures_get_mode(const TextureCache *cache, TextureKey key);
/* returns the number of atlases in the cache */
size_t textures_get_num_atlases(const struct texture_cache *cache);
size_t textures_get_num_atlases(const TextureCache *cache);
/* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */
void textures_reset_state(void);

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