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