Compare commits
No commits in common. "e7ed72dfc0d5ae7d3fef0d176a0e83a8ede10793" and "446402c2e00ef9f80136b75a3af9c170d278b29a" have entirely different histories.
e7ed72dfc0
...
446402c2e0
@ -1,15 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.{c,h,py}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[CMakeListst.txt]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 8
|
|
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -2,5 +2,3 @@
|
|||||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.xm filter=lfs diff=lfs merge=lfs -text
|
*.xm filter=lfs diff=lfs merge=lfs -text
|
||||||
*.tga filter=lfs diff=lfs merge=lfs -text
|
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
text=auto eol=lf
|
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -22,8 +22,8 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
.cache/
|
.cache/
|
||||||
build/
|
.build/
|
||||||
build-web/
|
.build-web/
|
||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
@ -2,9 +2,6 @@ cmake_minimum_required(VERSION 3.21)
|
|||||||
|
|
||||||
project(townengine LANGUAGES C)
|
project(townengine LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
|
|
||||||
set(CMAKE_INSTALL_MESSAGE NEVER)
|
|
||||||
|
|
||||||
# SDL dependencies
|
# SDL dependencies
|
||||||
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
||||||
if(NOT EMSCRIPTEN)
|
if(NOT EMSCRIPTEN)
|
||||||
@ -159,13 +156,14 @@ function(give_options_without_warnings target)
|
|||||||
|
|
||||||
set(BUILD_FLAGS_RELEASE
|
set(BUILD_FLAGS_RELEASE
|
||||||
-O3
|
-O3
|
||||||
-flto=$<IF:$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>,thin,auto>
|
-flto=auto
|
||||||
-mavx -mavx2
|
-mavx -mavx2
|
||||||
|
-Wl,--gc-sections
|
||||||
-fdata-sections
|
-fdata-sections
|
||||||
-ffunction-sections
|
-ffunction-sections
|
||||||
-funroll-loops
|
-funroll-loops
|
||||||
-fomit-frame-pointer
|
-fomit-frame-pointer
|
||||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-s>)
|
-s)
|
||||||
|
|
||||||
set(BUILD_FLAGS_DEBUG
|
set(BUILD_FLAGS_DEBUG
|
||||||
-O0
|
-O0
|
||||||
@ -175,19 +173,6 @@ function(give_options_without_warnings target)
|
|||||||
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>
|
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:-gsource-map>)
|
$<$<BOOL:${EMSCRIPTEN}>:-gsource-map>)
|
||||||
|
|
||||||
if (CMAKE_C_COMPILER_LINKER_ID MATCHES GNU OR CMAKE_C_COMPILER_LINKER_ID MATCHES GNUgold)
|
|
||||||
set(THINLTO_USAGE "-plugin-opt,")
|
|
||||||
endif()
|
|
||||||
if (CMAKE_C_COMPILER_LINKER_ID MATCHES LLD)
|
|
||||||
set(THINLTO_USAGE "--thinlto-")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (THINLTO_USAGE)
|
|
||||||
set(BUILD_SHARED_LIBRARY_FLAGS_RELEASE
|
|
||||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,${THINLTO_USAGE}cache-dir=${CMAKE_CURRENT_BINARY_DIR}/linker-cache/>
|
|
||||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,${THINLTO_USAGE}cache-policy=prune_after=30m>)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_options(${target} PUBLIC
|
target_compile_options(${target} PUBLIC
|
||||||
${BUILD_FLAGS}
|
${BUILD_FLAGS}
|
||||||
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
|
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
|
||||||
@ -201,15 +186,6 @@ function(give_options_without_warnings target)
|
|||||||
-Bsymbolic-functions
|
-Bsymbolic-functions
|
||||||
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
|
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
|
||||||
|
|
||||||
get_target_property(target_type ${target} TYPE)
|
|
||||||
if (target_type MATCHES SHARED_LIBRARY)
|
|
||||||
target_compile_options(${target} PUBLIC
|
|
||||||
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
|
|
||||||
|
|
||||||
target_link_options(${target} PUBLIC
|
|
||||||
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_definitions(${target} PRIVATE
|
target_compile_definitions(${target} PRIVATE
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>
|
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>
|
||||||
$<$<BOOL:${LINUX}>:_GNU_SOURCE>)
|
$<$<BOOL:${LINUX}>:_GNU_SOURCE>)
|
||||||
@ -340,3 +316,8 @@ include_deps(${TWN_TARGET})
|
|||||||
link_deps(${TWN_TARGET})
|
link_deps(${TWN_TARGET})
|
||||||
target_link_libraries(${TWN_TARGET} PUBLIC twn_third_parties)
|
target_link_libraries(${TWN_TARGET} PUBLIC twn_third_parties)
|
||||||
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
|
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
|
||||||
|
|
||||||
|
# move compie_commands.json into root directory so that it plays nicer with code editors without any configuration
|
||||||
|
add_custom_target(copy-compile-commands ALL
|
||||||
|
${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/compile_commands.json
|
||||||
|
${TWN_ROOT_DIR})
|
||||||
|
@ -20,13 +20,13 @@ static void handle_input(void)
|
|||||||
if (ctx.mouse_position.y <= 60)
|
if (ctx.mouse_position.y <= 60)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (input_action_pressed("add_a_bit"))
|
if (input_is_action_pressed("add_a_bit"))
|
||||||
{ // Left click
|
{ // Left click
|
||||||
for (int i = 0; i < LEFT_CLICK_ADD; i++)
|
for (int i = 0; i < LEFT_CLICK_ADD; i++)
|
||||||
{
|
{
|
||||||
if (state->bunniesCount < MAX_BUNNIES)
|
if (state->bunniesCount < MAX_BUNNIES)
|
||||||
{
|
{
|
||||||
state->bunnies[state->bunniesCount].position = input_action_position("add_a_bit");
|
state->bunnies[state->bunniesCount].position = input_get_action_position("add_a_bit");
|
||||||
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0f;
|
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0f;
|
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].color =
|
state->bunnies[state->bunniesCount].color =
|
||||||
@ -38,13 +38,13 @@ static void handle_input(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_pressed("add_a_lot"))
|
if (input_is_action_pressed("add_a_lot"))
|
||||||
{ // Right click
|
{ // Right click
|
||||||
for (int i = 0; i < RIGHT_CLICK_ADD; i++)
|
for (int i = 0; i < RIGHT_CLICK_ADD; i++)
|
||||||
{
|
{
|
||||||
if (state->bunniesCount < MAX_BUNNIES)
|
if (state->bunniesCount < MAX_BUNNIES)
|
||||||
{
|
{
|
||||||
state->bunnies[state->bunniesCount].position = input_action_position("add_a_lot");
|
state->bunnies[state->bunniesCount].position = input_get_action_position("add_a_lot");
|
||||||
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0f;
|
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0f;
|
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].color =
|
state->bunnies[state->bunniesCount].color =
|
||||||
@ -67,10 +67,13 @@ void game_tick(void)
|
|||||||
// Allocating State struct to store data there
|
// Allocating State struct to store data there
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof(State));
|
ctx.udata = ccalloc(1, sizeof(State));
|
||||||
}
|
|
||||||
|
|
||||||
input_action("add_a_bit", CONTROL_LEFT_MOUSE);
|
input_add_action("add_a_bit");
|
||||||
input_action("add_a_lot", CONTROL_RIGHT_MOUSE);
|
input_bind_action_control("add_a_bit", CONTROL_LEFT_MOUSE);
|
||||||
|
|
||||||
|
input_add_action("add_a_lot");
|
||||||
|
input_bind_action_control("add_a_lot", CONTROL_RIGHT_MOUSE);
|
||||||
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
@ -94,7 +97,7 @@ void game_tick(void)
|
|||||||
|
|
||||||
for (int i = 0; i < state->bunniesCount; i++)
|
for (int i = 0; i < state->bunniesCount; i++)
|
||||||
{ // Draw each bunny based on their position and color, also scale accordingly
|
{ // Draw each bunny based on their position and color, also scale accordingly
|
||||||
m_sprite(m_set(texture, "wabbit_alpha.png"),
|
m_sprite(m_set(path, "wabbit_alpha.png"),
|
||||||
m_set(rect, ((Rect){.x = state->bunnies[i].position.x,
|
m_set(rect, ((Rect){.x = state->bunnies[i].position.x,
|
||||||
.y = state->bunnies[i].position.y,
|
.y = state->bunnies[i].position.y,
|
||||||
.w = BUNNY_W * SPRITE_SCALE,
|
.w = BUNNY_W * SPRITE_SCALE,
|
||||||
|
@ -18,18 +18,45 @@ void game_tick(void) {
|
|||||||
state->ctx = &ctx;
|
state->ctx = &ctx;
|
||||||
state->scene = title_scene(state);
|
state->scene = title_scene(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_add_action("debug_toggle");
|
||||||
|
input_bind_action_control("debug_toggle", CONTROL_BACKSPACE);
|
||||||
|
|
||||||
|
input_add_action("debug_dump_atlases");
|
||||||
|
input_bind_action_control("debug_dump_atlases", CONTROL_HOME);
|
||||||
|
|
||||||
|
input_add_action("player_left");
|
||||||
|
input_bind_action_control("player_left", CONTROL_A);
|
||||||
|
|
||||||
|
input_add_action("player_right");
|
||||||
|
input_bind_action_control("player_right", CONTROL_D);
|
||||||
|
|
||||||
|
input_add_action("player_forward");
|
||||||
|
input_bind_action_control("player_forward", CONTROL_W);
|
||||||
|
|
||||||
|
input_add_action("player_backward");
|
||||||
|
input_bind_action_control("player_backward", CONTROL_S);
|
||||||
|
|
||||||
|
input_add_action("player_jump");
|
||||||
|
input_bind_action_control("player_jump", CONTROL_SPACE);
|
||||||
|
|
||||||
|
input_add_action("player_run");
|
||||||
|
input_bind_action_control("player_run", CONTROL_LSHIFT);
|
||||||
|
|
||||||
|
input_add_action("ui_accept");
|
||||||
|
input_bind_action_control("ui_accept", CONTROL_RETURN);
|
||||||
|
|
||||||
|
input_add_action("mouse_capture_toggle");
|
||||||
|
input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
input_action("debug_toggle", CONTROL_BACKSPACE);
|
if (input_is_action_just_pressed("debug_toggle")) {
|
||||||
input_action("debug_dump_atlases", CONTROL_HOME);
|
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_toggle")) {
|
|
||||||
ctx.debug = !ctx.debug;
|
ctx.debug = !ctx.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_dump_atlases")) {
|
if (input_is_action_just_pressed("debug_dump_atlases")) {
|
||||||
textures_dump_atlases();
|
textures_dump_atlases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,28 +11,28 @@
|
|||||||
|
|
||||||
|
|
||||||
static void update_timers(Player *player) {
|
static void update_timers(Player *player) {
|
||||||
player->jump_air_timer = timer_tick_frames(player->jump_air_timer);
|
tick_timer(&player->jump_air_timer);
|
||||||
player->jump_coyote_timer = timer_tick_frames(player->jump_coyote_timer);
|
tick_timer(&player->jump_coyote_timer);
|
||||||
player->jump_buffer_timer = timer_tick_frames(player->jump_buffer_timer);
|
tick_timer(&player->jump_buffer_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void input_move(Player *player) {
|
static void input_move(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_action_pressed("player_left") &&
|
if (!input_is_action_pressed("player_left") &&
|
||||||
!input_action_pressed("player_right"))
|
!input_is_action_pressed("player_right"))
|
||||||
{
|
{
|
||||||
player->dx *= player->horizontal_damping;
|
player->dx *= player->horizontal_damping;
|
||||||
}
|
}
|
||||||
|
|
||||||
int input_dir = 0;
|
int input_dir = 0;
|
||||||
if (input_action_pressed("player_left"))
|
if (input_is_action_pressed("player_left"))
|
||||||
input_dir = -1;
|
input_dir = -1;
|
||||||
if (input_action_pressed("player_right"))
|
if (input_is_action_pressed("player_right"))
|
||||||
input_dir = 1;
|
input_dir = 1;
|
||||||
if (input_action_pressed("player_left") &&
|
if (input_is_action_pressed("player_left") &&
|
||||||
input_action_pressed("player_right"))
|
input_is_action_pressed("player_right"))
|
||||||
input_dir = 0;
|
input_dir = 0;
|
||||||
|
|
||||||
player->dx += (float)input_dir * player->run_horizontal_speed;
|
player->dx += (float)input_dir * player->run_horizontal_speed;
|
||||||
@ -56,7 +56,7 @@ static void jump(Player *player) {
|
|||||||
static void input_jump(Player *player) {
|
static void input_jump(Player *player) {
|
||||||
player->current_gravity_multiplier = player->jump_default_multiplier;
|
player->current_gravity_multiplier = player->jump_default_multiplier;
|
||||||
|
|
||||||
if (input_action_just_pressed("player_jump")) {
|
if (input_is_action_just_pressed("player_jump")) {
|
||||||
player->jump_air_timer = 0;
|
player->jump_air_timer = 0;
|
||||||
player->jump_buffer_timer = player->jump_buffer_ticks;
|
player->jump_buffer_timer = player->jump_buffer_ticks;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ static void input_jump(Player *player) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_pressed("player_jump")) {
|
if (input_is_action_pressed("player_jump")) {
|
||||||
if (player->action != PLAYER_ACTION_GROUND && player->jump_air_timer > 0) {
|
if (player->action != PLAYER_ACTION_GROUND && player->jump_air_timer > 0) {
|
||||||
player->current_gravity_multiplier = player->jump_boosted_multiplier;
|
player->current_gravity_multiplier = player->jump_boosted_multiplier;
|
||||||
player->dy += player->jump_force_increase;
|
player->dy += player->jump_force_increase;
|
||||||
@ -147,7 +147,7 @@ static bool corner_correct(Player *player, Rect collision) {
|
|||||||
|
|
||||||
static void calc_collisions_x(Player *player) {
|
static void calc_collisions_x(Player *player) {
|
||||||
Rect collision;
|
Rect collision;
|
||||||
bool is_colliding = world_find_rect_intersects(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);
|
||||||
@ -164,7 +164,7 @@ static void calc_collisions_x(Player *player) {
|
|||||||
|
|
||||||
static void calc_collisions_y(Player *player) {
|
static void calc_collisions_y(Player *player) {
|
||||||
Rect collision;
|
Rect collision;
|
||||||
bool is_colliding = world_find_rect_intersects(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);
|
||||||
@ -255,10 +255,6 @@ static void drawdef(Player *player) {
|
|||||||
draw_circle((Vec2) { 256, 128 },
|
draw_circle((Vec2) { 256, 128 },
|
||||||
24,
|
24,
|
||||||
(Color) { 255, 0, 0, 255 });
|
(Color) { 255, 0, 0, 255 });
|
||||||
|
|
||||||
draw_circle((Vec2) { 304, 128 },
|
|
||||||
24,
|
|
||||||
(Color) { 255, 0, 0, 255 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,15 +11,28 @@
|
|||||||
static void ingame_tick(State *state) {
|
static void ingame_tick(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
input_action("player_left", CONTROL_A);
|
|
||||||
input_action("player_right", CONTROL_D);
|
|
||||||
input_action("player_forward", CONTROL_W);
|
|
||||||
input_action("player_backward", CONTROL_S);
|
|
||||||
input_action("player_jump", CONTROL_SPACE);
|
|
||||||
input_action("player_run", CONTROL_LSHIFT);
|
|
||||||
|
|
||||||
world_drawdef(scn->world);
|
world_drawdef(scn->world);
|
||||||
player_calc(scn->player);
|
player_calc(scn->player);
|
||||||
|
|
||||||
|
const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up));
|
||||||
|
const float speed = 0.04f; /* TODO: put this in a better place */
|
||||||
|
if (input_is_action_pressed("player_left"))
|
||||||
|
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
|
if (input_is_action_pressed("player_right"))
|
||||||
|
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
|
if (input_is_action_pressed("player_forward"))
|
||||||
|
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
||||||
|
|
||||||
|
if (input_is_action_pressed("player_backward"))
|
||||||
|
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
||||||
|
|
||||||
|
if (input_is_action_pressed("player_jump"))
|
||||||
|
scn->cam.pos.y += speed;
|
||||||
|
|
||||||
|
if (input_is_action_pressed("player_run"))
|
||||||
|
scn->cam.pos.y -= speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -41,5 +54,7 @@ Scene *ingame_scene(State *state) {
|
|||||||
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 = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
|
||||||
|
|
||||||
return (Scene *)new_scene;
|
return (Scene *)new_scene;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ typedef struct SceneIngame {
|
|||||||
World *world;
|
World *world;
|
||||||
Player *player;
|
Player *player;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
|
||||||
/* TODO: put this in a better place */
|
/* TODO: put this in a better place */
|
||||||
float yaw;
|
float yaw;
|
||||||
float pitch;
|
float pitch;
|
||||||
|
@ -14,9 +14,7 @@ static void title_tick(State *state) {
|
|||||||
SceneTitle *scn = (SceneTitle *)state->scene;
|
SceneTitle *scn = (SceneTitle *)state->scene;
|
||||||
(void)scn;
|
(void)scn;
|
||||||
|
|
||||||
input_action("ui_accept", CONTROL_RETURN);
|
if (input_is_action_just_pressed("ui_accept")) {
|
||||||
|
|
||||||
if (input_action_just_pressed("ui_accept")) {
|
|
||||||
switch_to(state, ingame_scene);
|
switch_to(state, ingame_scene);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -25,13 +23,13 @@ static void title_tick(State *state) {
|
|||||||
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
|
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
|
||||||
|
|
||||||
/* draw the tick count as an example of dynamic text */
|
/* draw the tick count as an example of dynamic text */
|
||||||
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
|
size_t text_str_len = snprintf(NULL, 0, "%lu", state->ctx->frame_number) + 1;
|
||||||
char *text_str = cmalloc(text_str_len);
|
char *text_str = cmalloc(text_str_len);
|
||||||
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
|
snprintf(text_str, text_str_len, "%lu", state->ctx->frame_number);
|
||||||
|
|
||||||
const char *font = "fonts/kenney-pixel.ttf";
|
const char *font = "fonts/kenney-pixel.ttf";
|
||||||
float text_h = 32;
|
int text_h = 32;
|
||||||
float text_w = draw_text_width(text_str, text_h, font);
|
int text_w = draw_text_width(text_str, text_h, font);
|
||||||
|
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
(Rect) {
|
(Rect) {
|
||||||
|
@ -12,11 +12,11 @@ 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 = (Rect) {
|
.rect = (Recti) {
|
||||||
.x = (float)(col * world->tile_size),
|
.x = (int)col * world->tile_size,
|
||||||
.y = (float)(row * world->tile_size),
|
.y = (int)row * world->tile_size,
|
||||||
.w = (float)world->tile_size,
|
.w = world->tile_size,
|
||||||
.h = (float)world->tile_size,
|
.h = world->tile_size,
|
||||||
},
|
},
|
||||||
.type = world->tilemap[row][col],
|
.type = world->tilemap[row][col],
|
||||||
};
|
};
|
||||||
@ -25,10 +25,10 @@ static void update_tiles(struct World *world) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Vec2 to_grid_location(struct World *world, float x, float y) {
|
static Vec2i to_grid_location(struct World *world, float x, float y) {
|
||||||
return (Vec2) {
|
return (Vec2i) {
|
||||||
.x = floor(x / (float)world->tile_size),
|
.x = (int)floor(x / (float)world->tile_size),
|
||||||
.y = floor(y / (float)world->tile_size),
|
.y = (int)floor(y / (float)world->tile_size),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,8 @@ static void drawdef_debug(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) continue;
|
if (world->tiles[i].type == TILE_TYPE_VOID) continue;
|
||||||
|
|
||||||
draw_rectangle(world->tiles[i].rect, (Color) { 255, 0, 255, 128 });
|
draw_rectangle(to_frect(world->tiles[i].rect),
|
||||||
|
(Color) { 255, 0, 255, 128 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,14 +106,14 @@ void world_drawdef(struct World *world) {
|
|||||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_sprite("/assets/white.png", world->tiles[i].rect);
|
m_sprite("/assets/white.png", to_frect(world->tiles[i].rect));
|
||||||
}
|
}
|
||||||
|
|
||||||
drawdef_debug(world);
|
drawdef_debug(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool world_find_rect_intersects(struct World *world, Rect rect, Rect *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;
|
||||||
@ -120,12 +121,19 @@ bool world_find_rect_intersects(struct World *world, Rect rect, Rect *intersecti
|
|||||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Rect const tile_frect = world->tiles[i].rect;
|
Rect tile_frect = {
|
||||||
|
.x = (float)(world->tiles[i].rect.x),
|
||||||
|
.y = (float)(world->tiles[i].rect.y),
|
||||||
|
.w = (float)(world->tiles[i].rect.w),
|
||||||
|
.h = (float)(world->tiles[i].rect.h),
|
||||||
|
};
|
||||||
|
|
||||||
is_intersecting = rect_intersects(rect, tile_frect);
|
if (intersection == NULL) {
|
||||||
|
Rect temp;
|
||||||
if (intersection)
|
is_intersecting = overlap_frect(&rect, &tile_frect, &temp);
|
||||||
*intersection = rect_overlap(rect, tile_frect);
|
} else {
|
||||||
|
is_intersecting = overlap_frect(&rect, &tile_frect, intersection);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_intersecting)
|
if (is_intersecting)
|
||||||
break;
|
break;
|
||||||
@ -135,21 +143,46 @@ bool world_find_rect_intersects(struct World *world, Rect rect, Rect *intersecti
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool world_find_intersect_rect(struct World *world, Recti rect, Recti *intersection) {
|
||||||
|
bool is_intersecting = false;
|
||||||
|
|
||||||
|
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
||||||
|
for (size_t i = 0; i < tile_count; ++i) {
|
||||||
|
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Recti *tile_rect = &world->tiles[i].rect;
|
||||||
|
|
||||||
|
if (intersection == NULL) {
|
||||||
|
Recti temp;
|
||||||
|
is_intersecting = overlap_rect(&rect, tile_rect, &temp);
|
||||||
|
} else {
|
||||||
|
is_intersecting = overlap_rect(&rect, tile_rect, intersection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_intersecting)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_intersecting;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool world_is_tile_at(struct World *world, float x, float y) {
|
bool world_is_tile_at(struct World *world, float x, float y) {
|
||||||
Vec2 position_in_grid = to_grid_location(world, x, y);
|
Vec2i position_in_grid = to_grid_location(world, x, y);
|
||||||
return world->tilemap[(int32_t)position_in_grid.y][(int32_t)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) {
|
||||||
Vec2 position_in_grid = to_grid_location(world, x, y);
|
Vec2i position_in_grid = to_grid_location(world, x, y);
|
||||||
world->tilemap[(int32_t)position_in_grid.y][(int32_t)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) {
|
||||||
Vec2 position_in_grid = to_grid_location(world, x, y);
|
Vec2i position_in_grid = to_grid_location(world, x, y);
|
||||||
world->tilemap[(int32_t)position_in_grid.y][(int32_t)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);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ typedef enum TileType {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct Tile {
|
typedef struct Tile {
|
||||||
Rect rect;
|
Recti rect;
|
||||||
TileType type;
|
TileType type;
|
||||||
} Tile;
|
} Tile;
|
||||||
|
|
||||||
@ -34,7 +34,8 @@ typedef struct World {
|
|||||||
World *world_create(void);
|
World *world_create(void);
|
||||||
void world_destroy(World *world);
|
void world_destroy(World *world);
|
||||||
void world_drawdef(World *world);
|
void world_drawdef(World *world);
|
||||||
bool world_find_rect_intersects(World *world, Rect rect, Rect *intersection);
|
bool world_find_intersect_frect(World *world, Rect rect, Rect *intersection);
|
||||||
|
bool world_find_intersect_rect(World *world, Recti rect, Recti *intersection);
|
||||||
bool world_is_tile_at(World *world, float x, float y);
|
bool world_is_tile_at(World *world, float x, float y);
|
||||||
void world_place_tile(World *world, float x, float y);
|
void world_place_tile(World *world, float x, float y);
|
||||||
void world_remove_tile(World *world, float x, float y);
|
void world_remove_tile(World *world, float x, float y);
|
||||||
|
@ -19,18 +19,45 @@ void game_tick(void) {
|
|||||||
state->ctx = &ctx;
|
state->ctx = &ctx;
|
||||||
state->scene = title_scene(state);
|
state->scene = title_scene(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_add_action("debug_toggle");
|
||||||
|
input_bind_action_control("debug_toggle", CONTROL_BACKSPACE);
|
||||||
|
|
||||||
|
input_add_action("debug_dump_atlases");
|
||||||
|
input_bind_action_control("debug_dump_atlases", CONTROL_HOME);
|
||||||
|
|
||||||
|
input_add_action("player_left");
|
||||||
|
input_bind_action_control("player_left", CONTROL_A);
|
||||||
|
|
||||||
|
input_add_action("player_right");
|
||||||
|
input_bind_action_control("player_right", CONTROL_D);
|
||||||
|
|
||||||
|
input_add_action("player_forward");
|
||||||
|
input_bind_action_control("player_forward", CONTROL_W);
|
||||||
|
|
||||||
|
input_add_action("player_backward");
|
||||||
|
input_bind_action_control("player_backward", CONTROL_S);
|
||||||
|
|
||||||
|
input_add_action("player_jump");
|
||||||
|
input_bind_action_control("player_jump", CONTROL_SPACE);
|
||||||
|
|
||||||
|
input_add_action("player_run");
|
||||||
|
input_bind_action_control("player_run", CONTROL_LSHIFT);
|
||||||
|
|
||||||
|
input_add_action("ui_accept");
|
||||||
|
input_bind_action_control("ui_accept", CONTROL_RETURN);
|
||||||
|
|
||||||
|
input_add_action("mouse_capture_toggle");
|
||||||
|
input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
input_action("debug_toggle", CONTROL_BACKSPACE);
|
if (input_is_action_just_pressed("debug_toggle")) {
|
||||||
input_action("debug_dump_atlases", CONTROL_HOME);
|
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_toggle")) {
|
|
||||||
ctx.debug = !ctx.debug;
|
ctx.debug = !ctx.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_dump_atlases")) {
|
if (input_is_action_just_pressed("debug_dump_atlases")) {
|
||||||
textures_dump_atlases();
|
textures_dump_atlases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,59 +15,55 @@
|
|||||||
static void ingame_tick(State *state) {
|
static void ingame_tick(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
input_action("player_left", CONTROL_A);
|
if (input_is_mouse_captured()) {
|
||||||
input_action("player_right", CONTROL_D);
|
const float sensitivity = 0.6f; /* TODO: put this in a better place */
|
||||||
input_action("player_forward", CONTROL_W);
|
|
||||||
input_action("player_backward", CONTROL_S);
|
|
||||||
input_action("player_jump", CONTROL_SPACE);
|
|
||||||
input_action("player_run", CONTROL_LSHIFT);
|
|
||||||
input_action("mouse_capture_toggle", CONTROL_ESCAPE);
|
|
||||||
|
|
||||||
if (scn->mouse_captured) {
|
|
||||||
const float sensitivity = 0.4f * (float)DEG2RAD; /* TODO: put this in a better place */
|
|
||||||
scn->yaw += (float)ctx.mouse_movement.x * sensitivity;
|
scn->yaw += (float)ctx.mouse_movement.x * sensitivity;
|
||||||
scn->pitch -= (float)ctx.mouse_movement.y * sensitivity;
|
scn->pitch -= (float)ctx.mouse_movement.y * sensitivity;
|
||||||
scn->pitch = clampf(scn->pitch, (float)-M_PI * 0.49f, (float)M_PI * 0.49f);
|
scn->pitch = clampf(scn->pitch, -89.0f, 89.0f);
|
||||||
|
|
||||||
|
const float yaw_rad = scn->yaw * (float)DEG2RAD;
|
||||||
|
const float pitch_rad = scn->pitch * (float)DEG2RAD;
|
||||||
|
|
||||||
|
scn->cam.target = m_vec_norm(((Vec3){
|
||||||
|
cosf(yaw_rad) * cosf(pitch_rad),
|
||||||
|
sinf(pitch_rad),
|
||||||
|
sinf(yaw_rad) * cosf(pitch_rad)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawCameraFromPrincipalAxesResult dir_and_up =
|
const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up));
|
||||||
draw_camera_from_principal_axes(scn->pos, (float)M_PI_2, scn->roll, scn->pitch, scn->yaw);
|
|
||||||
|
|
||||||
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.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_action_pressed("player_left"))
|
if (input_is_action_pressed("player_left"))
|
||||||
scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed));
|
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
if (input_action_pressed("player_right"))
|
if (input_is_action_pressed("player_right"))
|
||||||
scn->pos = vec3_add(scn->pos, m_vec_scale(right, speed));
|
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
if (input_action_pressed("player_forward"))
|
if (input_is_action_pressed("player_forward"))
|
||||||
scn->pos = vec3_add(scn->pos, m_vec_scale(dir_and_up.direction, speed));
|
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
||||||
|
|
||||||
if (input_action_pressed("player_backward"))
|
if (input_is_action_pressed("player_backward"))
|
||||||
scn->pos = vec3_sub(scn->pos, m_vec_scale(dir_and_up.direction, speed));
|
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
||||||
|
|
||||||
if (input_action_pressed("player_jump"))
|
if (input_is_action_pressed("player_jump"))
|
||||||
scn->pos.y += speed;
|
scn->cam.pos.y += speed;
|
||||||
|
|
||||||
if (input_action_pressed("player_run"))
|
if (input_is_action_pressed("player_run"))
|
||||||
scn->pos.y -= speed;
|
scn->cam.pos.y -= speed;
|
||||||
|
|
||||||
/* toggle mouse capture with end key */
|
/* toggle mouse capture with end key */
|
||||||
if (input_action_just_pressed("mouse_capture_toggle"))
|
if (input_is_action_just_pressed("mouse_capture_toggle")) {
|
||||||
scn->mouse_captured = !scn->mouse_captured;
|
input_set_mouse_captured(!input_is_mouse_captured());
|
||||||
|
}
|
||||||
|
|
||||||
input_mouse_captured(scn->mouse_captured);
|
draw_camera(&scn->cam);
|
||||||
|
|
||||||
#define TERRAIN_FREQUENCY 0.1f
|
#define TERRAIN_FREQUENCY 0.1f
|
||||||
#define TERRAIN_DISTANCE 64
|
|
||||||
|
|
||||||
float const half_terrain_distance = (float)TERRAIN_DISTANCE / 2;
|
for (int ly = 64; ly--;) {
|
||||||
|
for (int lx = 64; lx--;) {
|
||||||
for (int ly = TERRAIN_DISTANCE; ly--;) {
|
float x = SDL_truncf(scn->cam.pos.x + 32 - (float)lx);
|
||||||
for (int lx = TERRAIN_DISTANCE; lx--;) {
|
float y = SDL_truncf(scn->cam.pos.z + 32 - (float)ly);
|
||||||
float x = SDL_truncf(scn->pos.x + half_terrain_distance - (float)lx);
|
|
||||||
float y = SDL_truncf(scn->pos.z + half_terrain_distance - (float)ly);
|
|
||||||
|
|
||||||
float d0 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
float d0 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
||||||
float d1 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
float d1 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
||||||
@ -109,11 +105,13 @@ Scene *ingame_scene(State *state) {
|
|||||||
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->mouse_captured = true;
|
new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
|
||||||
|
|
||||||
m_audio(m_set(path, "music/mod65.xm"),
|
m_audio(m_set(path, "music/mod65.xm"),
|
||||||
m_opt(channel, "soundtrack"),
|
m_opt(channel, "soundtrack"),
|
||||||
m_opt(repeat, true));
|
m_opt(repeat, true));
|
||||||
|
|
||||||
|
input_set_mouse_captured(true);
|
||||||
|
|
||||||
return (Scene *)new_scene;
|
return (Scene *)new_scene;
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,16 @@
|
|||||||
#include "../state.h"
|
#include "../state.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct SceneIngame {
|
typedef struct SceneIngame {
|
||||||
Scene base;
|
Scene base;
|
||||||
|
|
||||||
Vec3 pos;
|
Camera cam;
|
||||||
|
|
||||||
|
/* TODO: put this in a better place */
|
||||||
float yaw;
|
float yaw;
|
||||||
float pitch;
|
float pitch;
|
||||||
float roll;
|
float roll;
|
||||||
|
|
||||||
bool mouse_captured;
|
|
||||||
} SceneIngame;
|
} SceneIngame;
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,9 +11,7 @@ static void title_tick(State *state) {
|
|||||||
SceneTitle *scn = (SceneTitle *)state->scene;
|
SceneTitle *scn = (SceneTitle *)state->scene;
|
||||||
(void)scn;
|
(void)scn;
|
||||||
|
|
||||||
input_action("ui_accept", CONTROL_RETURN);
|
if (input_is_action_just_pressed("ui_accept")) {
|
||||||
|
|
||||||
if (input_action_just_pressed("ui_accept")) {
|
|
||||||
switch_to(state, ingame_scene);
|
switch_to(state, ingame_scene);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -22,20 +20,20 @@ static void title_tick(State *state) {
|
|||||||
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
|
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
|
||||||
|
|
||||||
/* draw the tick count as an example of dynamic text */
|
/* draw the tick count as an example of dynamic text */
|
||||||
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
|
size_t text_str_len = snprintf(NULL, 0, "%llu", state->ctx->frame_number) + 1;
|
||||||
char *text_str = cmalloc(text_str_len);
|
char *text_str = cmalloc(text_str_len);
|
||||||
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
|
snprintf(text_str, text_str_len, "%llu", state->ctx->frame_number);
|
||||||
|
|
||||||
const char *font = "/fonts/kenney-pixel.ttf";
|
const char *font = "/fonts/kenney-pixel.ttf";
|
||||||
float text_h = 32;
|
int text_h = 32;
|
||||||
float text_w = draw_text_width(text_str, text_h, font);
|
int text_w = draw_text_width(text_str, text_h, font);
|
||||||
|
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
(Rect) {
|
(Rect) {
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
.w = text_w,
|
.w = (float)text_w,
|
||||||
.h = text_h,
|
.h = (float)text_h,
|
||||||
},
|
},
|
||||||
(Color) { 0, 0, 0, 255 }
|
(Color) { 0, 0, 0, 255 }
|
||||||
);
|
);
|
||||||
|
@ -8,19 +8,10 @@ endif()
|
|||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
|
||||||
COMMAND ${PYTHON3} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
|
||||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
|
||||||
|
|
||||||
lua/src/lapi.c
|
lua/src/lapi.c
|
||||||
lua/src/lapi.h
|
lua/src/lapi.h
|
||||||
lua/src/lauxlib.c
|
lua/src/lauxlib.c
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
#!/bin/env python3
|
|
||||||
|
|
||||||
import sys, json
|
|
||||||
|
|
||||||
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
|
|
||||||
api_source = f.read()
|
|
||||||
|
|
||||||
api = json.loads(api_source)
|
|
||||||
|
|
||||||
|
|
||||||
def default(parameter):
|
|
||||||
basetype = parameter["type"].rsplit(' *', 1)[0]
|
|
||||||
if parameter["type"] == "float":
|
|
||||||
return parameter["default"]
|
|
||||||
elif parameter["type"] == "bool":
|
|
||||||
return "true" if parameter["default"] else "false"
|
|
||||||
elif parameter["type"] == "char *":
|
|
||||||
if parameter["default"] == {}:
|
|
||||||
return "NULL"
|
|
||||||
else: return '"' + parameter["default"] + '"'
|
|
||||||
elif basetype in api["types"]:
|
|
||||||
if parameter["type"].endswith("*"):
|
|
||||||
if parameter["default"] == {}:
|
|
||||||
return "NULL"
|
|
||||||
else:
|
|
||||||
return "&(%s){\n%s\n }" % \
|
|
||||||
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
|
|
||||||
else:
|
|
||||||
return "(%s){\n%s\n }" % \
|
|
||||||
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
|
|
||||||
raise BaseException("Unhandled default value of type '%s'" % parameter["type"])
|
|
||||||
|
|
||||||
|
|
||||||
print('#include "twn_game_api.h"')
|
|
||||||
print('#include <lua.h>')
|
|
||||||
print('#include <lualib.h>')
|
|
||||||
print('#include <lauxlib.h>\n')
|
|
||||||
|
|
||||||
|
|
||||||
for typename, typedesc in api["types"].items():
|
|
||||||
converter = "static %s table_to_%s(lua_State *L, int idx) {\n" % (typename, typename.lower())
|
|
||||||
converter += " %s %s;\n" % (typename, typename.lower());
|
|
||||||
|
|
||||||
if not "fields" in typedesc:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for field in typedesc["fields"]:
|
|
||||||
converter += " lua_getfield(L, idx, \"%s\");\n" % (field["name"]);
|
|
||||||
if field["type"] == "float":
|
|
||||||
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
|
|
||||||
elif field["type"] == "uint8_t":
|
|
||||||
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
|
|
||||||
else:
|
|
||||||
raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
|
|
||||||
converter += " lua_pop(L, 1);\n";
|
|
||||||
|
|
||||||
converter += " return %s;\n}\n" % (typename.lower())
|
|
||||||
print(converter)
|
|
||||||
|
|
||||||
|
|
||||||
for procedure, procedure_desc in api["procedures"].items():
|
|
||||||
binding = "static int binding_%s(lua_State *L) {\n" % procedure
|
|
||||||
binding += " luaL_checktype(L, 1, LUA_TTABLE);\n"
|
|
||||||
|
|
||||||
if "params" in procedure_desc:
|
|
||||||
for parameter in procedure_desc["params"]:
|
|
||||||
basetype = parameter["type"].rsplit(' *', 1)[0]
|
|
||||||
|
|
||||||
if parameter["type"].endswith("*"):
|
|
||||||
binding += " %s %s_value;\n" % (basetype, parameter["name"])
|
|
||||||
binding += " %s %s;\n" % (parameter["type"], parameter["name"])
|
|
||||||
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
|
|
||||||
|
|
||||||
if "default" in parameter:
|
|
||||||
binding += " if (lua_isnoneornil(L, -1))\n"
|
|
||||||
binding += " %s = %s;\n" % (parameter["name"], default(parameter))
|
|
||||||
binding += " else\n "
|
|
||||||
|
|
||||||
if parameter["type"] == "float":
|
|
||||||
binding += " %s = (float)lua_tonumber(L, -1);\n" % (parameter["name"]);
|
|
||||||
elif parameter["type"] == "bool":
|
|
||||||
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
|
|
||||||
elif parameter["type"] == "char *":
|
|
||||||
binding += " %s = lua_tostring(L, -1);\n" % (parameter["name"]);
|
|
||||||
elif basetype in api["types"]:
|
|
||||||
if "enums" in api["types"][basetype]:
|
|
||||||
binding += " %s = lua_tointeger(L, -1);\n" % (parameter["name"]);
|
|
||||||
elif parameter["type"].endswith("*"):
|
|
||||||
binding += " { %s_value = table_to_%s(L, -1); %s = &%s_value; }\n" % (parameter["name"], basetype.lower(), parameter["name"], parameter["name"]);
|
|
||||||
else:
|
|
||||||
binding += " %s = table_to_%s(L, -1);\n" % (parameter["name"], basetype.lower());
|
|
||||||
else:
|
|
||||||
raise BaseException("Unhandled parameter type '%s'" % (parameter["type"]))
|
|
||||||
|
|
||||||
binding += " %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
|
||||||
binding += "}\n"
|
|
||||||
print(binding)
|
|
||||||
|
|
||||||
|
|
||||||
loader = "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
|
|
||||||
modules = set(api["procedures"][procedure]["module"] for procedure in api["procedures"])
|
|
||||||
for module in modules:
|
|
||||||
loader += " lua_newtable(L);\n"
|
|
||||||
for procedure, procedure_desc in api["procedures"].items():
|
|
||||||
if procedure_desc["module"] == module:
|
|
||||||
loader += " lua_pushstring(L, \"%s\");\n" % procedure_desc["symbol"]
|
|
||||||
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
|
|
||||||
loader += " lua_settable(L, -3);\n"
|
|
||||||
loader += " lua_setglobal(L, \"%s\");\n" % module
|
|
||||||
|
|
||||||
loader += "}"
|
|
||||||
print(loader)
|
|
@ -4,12 +4,12 @@ offset = { x = 0, y = 0 }
|
|||||||
angle = 0
|
angle = 0
|
||||||
|
|
||||||
function game_tick()
|
function game_tick()
|
||||||
draw.rectangle {
|
rectangle {
|
||||||
rect = { x = 0, y = 0, w = 640, h = 360 },
|
rect = { x = 0, y = 0, w = 640, h = 360 },
|
||||||
color = { r = 127, g = 0, b = 127, a = 255 },
|
color = { r = 127, g = 0, b = 127, a = 255 },
|
||||||
}
|
}
|
||||||
|
|
||||||
draw.sprite {
|
sprite {
|
||||||
path = "/assets/title.png",
|
path = "/assets/title.png",
|
||||||
rect = {
|
rect = {
|
||||||
x = 320 - (320 / 2),
|
x = 320 - (320 / 2),
|
||||||
@ -19,8 +19,8 @@ function game_tick()
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
draw.text {
|
text {
|
||||||
string = "it never happened",
|
string = "IT KEEPS HAPPENING",
|
||||||
position = offset,
|
position = offset,
|
||||||
font = "/fonts/kenney-pixel.ttf",
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
|
||||||
|
|
||||||
/* generated by bindgen.py */
|
|
||||||
void bindgen_load_twn(lua_State *L);
|
|
||||||
|
|
||||||
|
|
||||||
/* require will go through physicsfs exclusively so that scripts can be in the data dir */
|
/* require will go through physicsfs exclusively so that scripts can be in the data dir */
|
||||||
static int physfs_loader(lua_State *L) {
|
static int physfs_loader(lua_State *L) {
|
||||||
const char *name = luaL_checkstring(L, 1);
|
const char *name = luaL_checkstring(L, 1);
|
||||||
@ -41,6 +37,201 @@ static int physfs_loader(lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Rect table_to_rect(lua_State *L, int idx, const char *name) {
|
||||||
|
/* types are checked here to help prevent unexpected results */
|
||||||
|
Rect rect;
|
||||||
|
int is_num;
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "x");
|
||||||
|
rect.x = (float)lua_tonumberx(L, -1, &is_num);
|
||||||
|
if (!is_num)
|
||||||
|
luaL_error(L, "bad field 'x' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "y");
|
||||||
|
rect.y = (float)lua_tonumberx(L, -1, &is_num);
|
||||||
|
if (!is_num)
|
||||||
|
luaL_error(L, "bad field 'y' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "w");
|
||||||
|
rect.w = (float)lua_tonumberx(L, -1, &is_num);
|
||||||
|
if (!is_num)
|
||||||
|
luaL_error(L, "bad field 'w' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "h");
|
||||||
|
rect.h = (float)lua_tonumberx(L, -1, &is_num);
|
||||||
|
if (!is_num)
|
||||||
|
luaL_error(L, "bad field 'h' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Color table_to_color(lua_State *L, int idx) {
|
||||||
|
Color color;
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "r");
|
||||||
|
color.r = (uint8_t)lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "g");
|
||||||
|
color.g = (uint8_t)lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "b");
|
||||||
|
color.b = (uint8_t)lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, idx, "a");
|
||||||
|
color.a = (uint8_t)lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* sprite(data [table]) */
|
||||||
|
/* data should contain the following fields: */
|
||||||
|
/*
|
||||||
|
* path [string]
|
||||||
|
* rect [table]
|
||||||
|
* texture_region [table], optional
|
||||||
|
* color [table], optional
|
||||||
|
* rotation [number], optional
|
||||||
|
* flip_x [boolean], optional
|
||||||
|
* flip_y [boolean], optional
|
||||||
|
* stretch [boolean], optional
|
||||||
|
*/
|
||||||
|
static int b_sprite(lua_State *L) {
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
|
DrawSpriteArgs args = { 0 };
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "path");
|
||||||
|
const char *field_path = lua_tostring(L, -1);
|
||||||
|
if (field_path == NULL)
|
||||||
|
luaL_error(L, "bad field 'path' in 'data' (string expected, got %s)", luaL_typename(L, -1));
|
||||||
|
args.path = field_path;
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "rect");
|
||||||
|
if (!lua_istable(L, -1))
|
||||||
|
luaL_error(L, "bad field 'rect' in 'data' (table expected, got %s)", luaL_typename(L, -1));
|
||||||
|
args.rect = table_to_rect(L, -1, "data.rect");
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "texture_region");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
args.texture_region_opt = table_to_rect(L, -1, "data.texture_region");
|
||||||
|
args.texture_region_opt_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "color");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
args.color_opt = table_to_color(L, -1);
|
||||||
|
args.color_opt_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "rotation");
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
args.rotation_opt = (float)lua_tonumber(L, -1);
|
||||||
|
args.rotation_opt_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "flip_x");
|
||||||
|
if (lua_isboolean(L, -1)) {
|
||||||
|
args.flip_x_opt = lua_toboolean(L, -1);
|
||||||
|
args.flip_x_opt_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "flip_y");
|
||||||
|
if (lua_isboolean(L, -1)) {
|
||||||
|
args.flip_y_opt = lua_toboolean(L, -1);
|
||||||
|
args.flip_y_opt_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "stretch");
|
||||||
|
if (lua_isboolean(L, -1)) {
|
||||||
|
args.stretch_opt = lua_toboolean(L, -1);
|
||||||
|
args.stretch_opt_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_sprite_args(args);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* rectangle(data [table]) */
|
||||||
|
/* data should contain the following fields: */
|
||||||
|
/*
|
||||||
|
* rect [table]
|
||||||
|
* color [table], optional, defaults to white
|
||||||
|
*/
|
||||||
|
static int b_rectangle(lua_State *L) {
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "rect");
|
||||||
|
if (!lua_istable(L, -1))
|
||||||
|
luaL_error(L, "bad field 'rect' in 'data' (table expected, got %s)", luaL_typename(L, -1));
|
||||||
|
Rect rect = table_to_rect(L, -1, "data.rect");
|
||||||
|
|
||||||
|
Color color = { 255, 255, 255, 255 };
|
||||||
|
lua_getfield(L, 1, "color");
|
||||||
|
if (lua_istable(L, -1))
|
||||||
|
color = table_to_color(L, -1);
|
||||||
|
|
||||||
|
draw_rectangle(rect, color);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* text(data [table]) */
|
||||||
|
/* data should contain the following fields: */
|
||||||
|
/*
|
||||||
|
* string [string]
|
||||||
|
* position [table]
|
||||||
|
* height_px [number], optional, defaults to 22
|
||||||
|
* color [table], optional, defaults to black
|
||||||
|
* font [string]
|
||||||
|
*/
|
||||||
|
static int b_text(lua_State *L) {
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "string");
|
||||||
|
const char *string = lua_tostring(L, -1);
|
||||||
|
if (string == NULL)
|
||||||
|
luaL_error(L, "bad field 'string' in 'data' (string expected, got %s)", luaL_typename(L, -1));
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "position");
|
||||||
|
if (!lua_istable(L, -1))
|
||||||
|
luaL_error(L, "bad field 'position' in 'data' (table expected, got %s)", luaL_typename(L, -1));
|
||||||
|
lua_getfield(L, -1, "x");
|
||||||
|
float x = (float)lua_tonumber(L, -1);
|
||||||
|
lua_getfield(L, -2, "y");
|
||||||
|
float y = (float)lua_tonumber(L, -1);
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "height_px");
|
||||||
|
int is_num;
|
||||||
|
int height_px = (int)lua_tointegerx(L, -1, &is_num);
|
||||||
|
if (!is_num)
|
||||||
|
height_px = 22;
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "color");
|
||||||
|
Color color = { 0, 0, 0, 255 };
|
||||||
|
if (lua_istable(L, -1))
|
||||||
|
color = table_to_color(L, -1);
|
||||||
|
|
||||||
|
lua_getfield(L, 1, "font");
|
||||||
|
const char *font = lua_tostring(L, -1);
|
||||||
|
if (font == NULL)
|
||||||
|
luaL_error(L, "bad field 'font' in 'data' (string expected, got %s)", luaL_typename(L, -1));
|
||||||
|
|
||||||
|
draw_text(string, (Vec2) { x, y }, height_px, color, font);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
@ -55,13 +246,14 @@ void game_tick(void) {
|
|||||||
}
|
}
|
||||||
state->L = luaL_newstate();
|
state->L = luaL_newstate();
|
||||||
|
|
||||||
/* fakey version of luaL_openlibs() that excludes file i/o and os stuff */
|
/* fakey version of luaL_openlibs() that excludes file i/o */
|
||||||
{
|
{
|
||||||
static const luaL_Reg loaded_libs[] = {
|
static const luaL_Reg loaded_libs[] = {
|
||||||
{ LUA_GNAME, luaopen_base },
|
{ LUA_GNAME, luaopen_base },
|
||||||
{ LUA_LOADLIBNAME, luaopen_package },
|
{ LUA_LOADLIBNAME, luaopen_package },
|
||||||
{ LUA_COLIBNAME, luaopen_coroutine },
|
{ LUA_COLIBNAME, luaopen_coroutine },
|
||||||
{ LUA_TABLIBNAME, luaopen_table },
|
{ LUA_TABLIBNAME, luaopen_table },
|
||||||
|
{ LUA_OSLIBNAME, luaopen_os },
|
||||||
{ LUA_STRLIBNAME, luaopen_string },
|
{ LUA_STRLIBNAME, luaopen_string },
|
||||||
{ LUA_MATHLIBNAME, luaopen_math },
|
{ LUA_MATHLIBNAME, luaopen_math },
|
||||||
{ LUA_UTF8LIBNAME, luaopen_utf8 },
|
{ LUA_UTF8LIBNAME, luaopen_utf8 },
|
||||||
@ -88,11 +280,9 @@ void game_tick(void) {
|
|||||||
lua_pop(state->L, 2);
|
lua_pop(state->L, 2);
|
||||||
|
|
||||||
/* binding */
|
/* binding */
|
||||||
// lua_register(state->L, "sprite", b_sprite);
|
lua_register(state->L, "sprite", b_sprite);
|
||||||
// lua_register(state->L, "rectangle", b_rectangle);
|
lua_register(state->L, "rectangle", b_rectangle);
|
||||||
// lua_register(state->L, "text", b_text);
|
lua_register(state->L, "text", b_text);
|
||||||
|
|
||||||
bindgen_load_twn(state->L);
|
|
||||||
|
|
||||||
/* now finally get to running the code */
|
/* now finally get to running the code */
|
||||||
unsigned char *game_buf = NULL;
|
unsigned char *game_buf = NULL;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef STATE_H
|
#ifndef STATE_H
|
||||||
#define STATE_H
|
#define STATE_H
|
||||||
|
|
||||||
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,13 +5,8 @@ if [ -x "$(command -v ninja)" ]; then
|
|||||||
generator="-G Ninja"
|
generator="-G Ninja"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# check whether clang is around (it's just better)
|
|
||||||
if [ -x "$(command -v clang)" ]; then
|
|
||||||
cc="-DCMAKE_C_COMPILER=clang"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" = "web" ]; then
|
if [ "$1" = "web" ]; then
|
||||||
emcmake cmake $generator $cc -B build-web "${@:2}" && cmake --build build-web --parallel
|
emcmake cmake $generator -B .build-web "${@:2}" && cmake --build .build-web --parallel
|
||||||
else
|
else
|
||||||
cmake $generator $cc -B build "$@" && cmake --build build --parallel
|
cmake $generator -B .build "$@" && cmake --build .build --parallel
|
||||||
fi
|
fi
|
||||||
|
2
bin/twn
2
bin/twn
@ -6,7 +6,7 @@ set +e
|
|||||||
exe="$(basename $PWD)"
|
exe="$(basename $PWD)"
|
||||||
toolpath="$(dirname -- "${BASH_SOURCE[0]}")"
|
toolpath="$(dirname -- "${BASH_SOURCE[0]}")"
|
||||||
export TWNROOT=$(realpath "$toolpath"/../)
|
export TWNROOT=$(realpath "$toolpath"/../)
|
||||||
export TWNBUILDDIR=$(realpath "$toolpath"/../build)
|
export TWNBUILDDIR=$(realpath "$toolpath"/../.build)
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
build ) "$toolpath"/build.sh "${@:2}"
|
build ) "$toolpath"/build.sh "${@:2}"
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
# interoperability
|
|
||||||
api needs to facilitate easy interoperability with other languages and tools,
|
|
||||||
for that certain considerations are taken:
|
|
||||||
|
|
||||||
* number of public api calls is kept at the minimum
|
|
||||||
* procedure parameters can only use basic types, no aggregates, with exception of Vec/Matrix types and alike,
|
|
||||||
with no expectation on new additions (see [/include/twn_types.h](../include/twn_types.h))
|
|
||||||
* optionals can be expressed via pointer passage of value primitives, assumed immutable, with the NULL expressing default
|
|
||||||
* no opaque types, only keys
|
|
||||||
* when mutation on input is done, - it shouldn't be achieved from a mutable pointer, but the return value
|
|
||||||
* return value could be a simple aggregate that is translatable to pure data dictionary
|
|
||||||
* module prefix is used for namespacing, actual symbols come after the prefix (`<module>_<symbol>`)
|
|
||||||
* symbols should not contain numerics at the start nor after the namespace prefix
|
|
||||||
* 32 bit floating point is the only numeric type
|
|
||||||
* [/include/twn_api.json](../include/twn_api.json) file is hand-kept with a schema to aid automatic binding generation and tooling
|
|
10
docs/interop.txt
Normal file
10
docs/interop.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
api needs to facilitate easy interoperability with other languages and tools,
|
||||||
|
for that certain steps are taken:
|
||||||
|
|
||||||
|
* number of public api calls is kept at the minimum
|
||||||
|
* procedure signatures can only use basic types, no aggregates, with exception of Vec/Matrix types and alike,
|
||||||
|
with no expectation on new additions (see /include/twn_types.h)
|
||||||
|
* optionals can be expressed via pointer passage of value primitives, with NULL expressive default
|
||||||
|
* /include/twn_game_api.json file is hand-kept with a schema to aid automatic generation and other tooling
|
||||||
|
|
||||||
|
one of main inspirations for that is opengl model
|
@ -15,8 +15,13 @@ TWN_API void audio_play(const char *path,
|
|||||||
float volume, /* default: 1.0f, range: 0.0f to 1.0f */
|
float volume, /* default: 1.0f, range: 0.0f to 1.0f */
|
||||||
float panning); /* default: 0.0f, range: -1.0 to 1.0f */
|
float panning); /* default: 0.0f, range: -1.0 to 1.0f */
|
||||||
|
|
||||||
/* possible parameter options: "volume", "panning", "repeat" */
|
typedef enum {
|
||||||
TWN_API void audio_parameter(const char *channel, const char *parameter, float value);
|
AUDIO_PARAM_REPEAT,
|
||||||
|
AUDIO_PARAM_VOLUME,
|
||||||
|
AUDIO_PARAM_PANNING,
|
||||||
|
} AudioParam;
|
||||||
|
|
||||||
|
TWN_API void audio_set(const char *channel, AudioParam param, float value);
|
||||||
|
|
||||||
/* TODO */
|
/* TODO */
|
||||||
// TWN_API bool audio_ended(const char *channel);
|
// TWN_API bool audio_ended(const char *channel);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define TWN_CAMERA_H
|
#define TWN_CAMERA_H
|
||||||
|
|
||||||
#include "twn_types.h"
|
#include "twn_types.h"
|
||||||
|
#include "twn_engine_api.h"
|
||||||
|
|
||||||
/* TODO: make it cached? */
|
/* TODO: make it cached? */
|
||||||
/* for example, perspective matrix only needs recaluclation on FOV change */
|
/* for example, perspective matrix only needs recaluclation on FOV change */
|
||||||
@ -11,11 +12,11 @@ typedef struct Camera {
|
|||||||
Vec3 pos; /* eye position */
|
Vec3 pos; /* eye position */
|
||||||
Vec3 target; /* normalized target vector */
|
Vec3 target; /* normalized target vector */
|
||||||
Vec3 up; /* normalized up vector */
|
Vec3 up; /* normalized up vector */
|
||||||
float fov; /* field of view, in radians */
|
float fov; /* field of view, in radians */
|
||||||
} Camera;
|
} Camera;
|
||||||
|
|
||||||
Matrix4 camera_look_at(const Camera *camera);
|
TWN_API Matrix4 camera_look_at(const Camera *camera);
|
||||||
|
|
||||||
Matrix4 camera_perspective(const Camera *const camera);
|
TWN_API Matrix4 camera_perspective(const Camera *const camera);
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -25,9 +25,9 @@ typedef struct Context {
|
|||||||
|
|
||||||
/* resolution is set from config and dictates both logical and drawing space, as they're related */
|
/* resolution is set from config and dictates both logical and drawing space, as they're related */
|
||||||
/* even if scaling is done, game logic should never change over that */
|
/* even if scaling is done, game logic should never change over that */
|
||||||
Vec2 resolution;
|
Vec2i resolution;
|
||||||
Vec2 mouse_position;
|
Vec2i mouse_position;
|
||||||
Vec2 mouse_movement;
|
Vec2i mouse_movement;
|
||||||
|
|
||||||
/* is set on startup, should be used as source of randomness */
|
/* is set on startup, should be used as source of randomness */
|
||||||
uint64_t random_seed;
|
uint64_t random_seed;
|
||||||
|
@ -3,19 +3,20 @@
|
|||||||
|
|
||||||
#include "twn_types.h"
|
#include "twn_types.h"
|
||||||
#include "twn_option.h"
|
#include "twn_option.h"
|
||||||
|
#include "twn_camera.h"
|
||||||
#include "twn_engine_api.h"
|
#include "twn_engine_api.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
/* pushes a sprite onto the sprite render queue */
|
/* pushes a sprite onto the sprite render queue */
|
||||||
TWN_API void draw_sprite(char const *texture,
|
TWN_API void draw_sprite(char const *path,
|
||||||
Rect rect,
|
Rect rect,
|
||||||
Rect const *texture_region, /* optional, default: NULL */
|
Rect const *texture_region, /* optional, default: NULL */
|
||||||
Color color, /* optional, default: all 255 */
|
Color color, /* optional, default: all 255 */
|
||||||
float rotation, /* optional, default: 0 */
|
float rotation, /* optional, default: 0 */
|
||||||
bool flip_x, /* optional, default: false */
|
bool flip_x, /* optional, default: false */
|
||||||
bool flip_y, /* optional, default: false */
|
bool flip_y, /* optional, default: false */
|
||||||
bool stretch); /* optional, default: true */
|
bool stretch); /* optional, default: false */
|
||||||
|
|
||||||
/* pushes a filled rectangle onto the rectangle render queue */
|
/* pushes a filled rectangle onto the rectangle render queue */
|
||||||
TWN_API void draw_rectangle(Rect rect, Color color);
|
TWN_API void draw_rectangle(Rect rect, Color color);
|
||||||
@ -27,24 +28,26 @@ TWN_API void draw_circle(Vec2 position, float radius, Color color);
|
|||||||
/* TODO: have font optional, with something minimal coming embedded */
|
/* TODO: have font optional, with something minimal coming embedded */
|
||||||
TWN_API void draw_text(char const *string,
|
TWN_API void draw_text(char const *string,
|
||||||
Vec2 position,
|
Vec2 position,
|
||||||
float height, /* optional, default: 22 */
|
int height_px, /* optional, default: 22 */
|
||||||
Color color, /* optional, default: all 0 */
|
Color color, /* optional, default: all 0 */
|
||||||
char const *font); /* optional, default: NULL */
|
char const *font);
|
||||||
|
|
||||||
|
|
||||||
TWN_API float draw_text_width(char const *string,
|
TWN_API int draw_text_width(char const *string,
|
||||||
float height, /* optional, default: 22 */
|
int height_px, /* TODO: make optional */
|
||||||
char const *font); /* optional, default: NULL */
|
char const *font);
|
||||||
|
|
||||||
TWN_API void draw_nine_slice(char const *texture,
|
TWN_API void draw_9slice(char const *texture_path,
|
||||||
Vec2 corners,
|
int texture_w,
|
||||||
Rect rect,
|
int texture_h,
|
||||||
float border_thickness, /* optional, default: 0 */
|
int border_thickness,
|
||||||
Color color); /* optional, default: all 255 */
|
Rect rect,
|
||||||
|
Color color); /* TODO: make optional */
|
||||||
|
|
||||||
/* 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 */
|
||||||
/* texture coordinates are in pixels */
|
/* texture coordinates are in pixels */
|
||||||
TWN_API void draw_triangle(char const *texture,
|
TWN_API void draw_triangle(char const *path,
|
||||||
Vec3 v0,
|
Vec3 v0,
|
||||||
Vec3 v1,
|
Vec3 v1,
|
||||||
Vec3 v2,
|
Vec3 v2,
|
||||||
@ -66,25 +69,15 @@ TWN_API void draw_triangle(char const *texture,
|
|||||||
// Color c1,
|
// Color c1,
|
||||||
// Color c2);
|
// Color c2);
|
||||||
|
|
||||||
TWN_API void draw_billboard(const char *path,
|
// TODO:
|
||||||
Vec3 position,
|
// http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2
|
||||||
Vec2 size);
|
// void unfurl_billboard(const char *path,
|
||||||
|
// Vec2 position,
|
||||||
|
// Vec2 scaling,
|
||||||
|
// Rect uvs);
|
||||||
|
|
||||||
/* sets a perspective 3d camera to be used for all 3d commands */
|
/* pushes a camera state to be used for all future unfurl_* commands */
|
||||||
TWN_API void draw_camera(Vec3 position, float fov, Vec3 up, Vec3 direction);
|
TWN_API void draw_camera(const Camera *camera);
|
||||||
|
|
||||||
/* same as draw_camera(), but with specific use case */
|
|
||||||
/* direction and up vectors are inferred from roll, pitch and yaw parameters (in radians) */
|
|
||||||
/* return value is direction and up vectors, so that you can use them in logic (such as controllers) */
|
|
||||||
typedef struct DrawCameraFromPrincipalAxesResult {
|
|
||||||
Vec3 direction;
|
|
||||||
Vec3 up;
|
|
||||||
} DrawCameraFromPrincipalAxesResult;
|
|
||||||
TWN_API DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position,
|
|
||||||
float fov,
|
|
||||||
float roll,
|
|
||||||
float pitch,
|
|
||||||
float yaw);
|
|
||||||
|
|
||||||
/* expects '*' masks that will be expanded to 6 names: 'up', 'down', 'east', 'west', 'north' and 'south' */
|
/* expects '*' masks that will be expanded to 6 names: 'up', 'down', 'east', 'west', 'north' and 'south' */
|
||||||
TWN_API void draw_skybox(const char *paths);
|
TWN_API void draw_skybox(const char *paths);
|
||||||
@ -95,7 +88,7 @@ TWN_API void draw_fog(float start, float end, float density, Color color);
|
|||||||
#ifndef TWN_NOT_C
|
#ifndef TWN_NOT_C
|
||||||
|
|
||||||
typedef struct DrawSpriteArgs {
|
typedef struct DrawSpriteArgs {
|
||||||
char const *texture;
|
char const *path;
|
||||||
Rect rect;
|
Rect rect;
|
||||||
|
|
||||||
m_option_list(
|
m_option_list(
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#ifndef TWN_ENGINE_API_H
|
#ifndef TWN_ENGINE_API_H
|
||||||
#define TWN_ENGINE_API_H
|
#define TWN_ENGINE_API_H
|
||||||
|
|
||||||
#if defined(TWN_NOT_C)
|
#if defined(__WIN32)
|
||||||
#define TWN_API
|
|
||||||
#elif defined(__WIN32)
|
|
||||||
#define TWN_API __declspec(dllexport)
|
#define TWN_API __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
#define TWN_API __attribute__((visibility("default")))
|
#define TWN_API __attribute__((visibility("default")))
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
#define TWN_GAME_API_H
|
#define TWN_GAME_API_H
|
||||||
|
|
||||||
#include "twn_input.h"
|
#include "twn_input.h"
|
||||||
|
#include "twn_context.h"
|
||||||
#include "twn_draw.h"
|
#include "twn_draw.h"
|
||||||
#include "twn_audio.h"
|
#include "twn_audio.h"
|
||||||
#include "twn_engine_api.h"
|
#include "twn_engine_api.h"
|
||||||
#include "twn_util.h"
|
#include "twn_util.h"
|
||||||
#include "twn_context.h"
|
|
||||||
|
|
||||||
#ifndef TWN_NOT_C
|
#ifndef TWN_NOT_C
|
||||||
|
|
||||||
|
@ -10,11 +10,19 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
TWN_API void input_action(const char *name, Control control);
|
TWN_API void input_bind_action_control(const char *action_name, Control control);
|
||||||
TWN_API bool input_action_pressed(const char *name);
|
TWN_API void input_unbind_action_control(const char *action_name, Control control);
|
||||||
TWN_API bool input_action_just_pressed(const char *name);
|
|
||||||
TWN_API bool input_action_just_released(const char *name);
|
TWN_API void input_add_action(const char *action_name);
|
||||||
TWN_API Vec2 input_action_position(const char *name);
|
TWN_API void input_delete_action(const char *action_name);
|
||||||
TWN_API void input_mouse_captured(bool enabled);
|
|
||||||
|
TWN_API bool input_is_action_pressed(const char *action_name);
|
||||||
|
TWN_API bool input_is_action_just_pressed(const char *action_name);
|
||||||
|
TWN_API bool input_is_action_just_released(const char *action_name);
|
||||||
|
|
||||||
|
TWN_API Vec2 input_get_action_position(const char *action_name);
|
||||||
|
|
||||||
|
TWN_API void input_set_mouse_captured(bool enabled);
|
||||||
|
TWN_API bool input_is_mouse_captured(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -9,7 +9,6 @@ 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 */
|
||||||
TEXTURE_MODE_UNKNOWN = -1, /* a sentinel */
|
|
||||||
} TextureMode;
|
} TextureMode;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,8 +6,17 @@
|
|||||||
/* plain data aggregates that are accepted between public procedure boundaries */
|
/* plain data aggregates that are accepted between public procedure boundaries */
|
||||||
|
|
||||||
|
|
||||||
|
/* a point in some space (integer) */
|
||||||
|
typedef struct Vec2i {
|
||||||
|
_Alignas(8)
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
} Vec2i;
|
||||||
|
|
||||||
|
|
||||||
/* a point in some space (floating point) */
|
/* a point in some space (floating point) */
|
||||||
typedef struct Vec2 {
|
typedef struct Vec2 {
|
||||||
|
_Alignas(8)
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
} Vec2;
|
} Vec2;
|
||||||
@ -16,6 +25,7 @@ typedef struct 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 Vec3 {
|
typedef struct Vec3 {
|
||||||
|
_Alignas(16)
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float z;
|
float z;
|
||||||
@ -25,6 +35,7 @@ typedef struct 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 Vec4 {
|
typedef struct Vec4 {
|
||||||
|
_Alignas(16)
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float z;
|
float z;
|
||||||
@ -34,6 +45,7 @@ typedef struct Vec4 {
|
|||||||
|
|
||||||
/* 32-bit color data */
|
/* 32-bit color data */
|
||||||
typedef struct Color {
|
typedef struct Color {
|
||||||
|
_Alignas(4)
|
||||||
uint8_t r;
|
uint8_t r;
|
||||||
uint8_t g;
|
uint8_t g;
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
@ -41,8 +53,19 @@ typedef struct Color {
|
|||||||
} Color;
|
} Color;
|
||||||
|
|
||||||
|
|
||||||
|
/* a rectangle with the origin at the upper left (integer) */
|
||||||
|
typedef struct Recti {
|
||||||
|
_Alignas(16)
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
int32_t w;
|
||||||
|
int32_t h;
|
||||||
|
} 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 Rect {
|
typedef struct Rect {
|
||||||
|
_Alignas(16)
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float w;
|
float w;
|
||||||
|
@ -24,60 +24,74 @@
|
|||||||
TWN_API void *crealloc(void *ptr, size_t size);
|
TWN_API void *crealloc(void *ptr, size_t size);
|
||||||
TWN_API void *ccalloc(size_t num, size_t size);
|
TWN_API void *ccalloc(size_t num, size_t size);
|
||||||
|
|
||||||
TWN_API void log_info(const char *restrict format, ...);
|
|
||||||
TWN_API void log_critical(const char *restrict format, ...);
|
|
||||||
TWN_API void log_warn(const char *restrict format, ...);
|
|
||||||
|
|
||||||
/* saves all texture atlases as BMP files in the write directory */
|
|
||||||
TWN_API void textures_dump_atlases(void);
|
|
||||||
|
|
||||||
/* returns true if str ends with suffix */
|
|
||||||
TWN_API bool strends(const char *str, const char *suffix);
|
|
||||||
|
|
||||||
/* TODO: this is why generics were invented. sorry, i'm tired today */
|
|
||||||
TWN_API double clamp(double d, double min, double max);
|
|
||||||
TWN_API float clampf(float f, float min, float max);
|
|
||||||
TWN_API int clampi(int i, int min, int max);
|
|
||||||
|
|
||||||
/* sets buf_out to a pointer to a byte buffer which must be freed. */
|
|
||||||
/* returns the size of this buffer. */
|
|
||||||
TWN_API int64_t file_to_bytes(const char *path, unsigned char **buf_out);
|
|
||||||
|
|
||||||
/* returns a pointer to a string which must be freed */
|
|
||||||
TWN_API char *file_to_str(const char *path);
|
|
||||||
|
|
||||||
/* returns true if the file exists in the filesystem */
|
|
||||||
TWN_API bool file_exists(const char *path);
|
|
||||||
|
|
||||||
#endif /* TWN_NOT_C */
|
#endif /* TWN_NOT_C */
|
||||||
|
|
||||||
/* calculates the overlap of two rectangles */
|
|
||||||
TWN_API Rect rect_overlap(Rect a, Rect b);
|
|
||||||
/* returns true if two rectangles are intersecting */
|
|
||||||
TWN_API bool rect_intersects(Rect a, Rect b);
|
|
||||||
TWN_API Vec2 rect_center(Rect rect);
|
|
||||||
|
|
||||||
/* decrements an integer value, stopping at 0 */
|
TWN_API void log_info(const char *restrict format, ...);
|
||||||
|
TWN_API void log_critical(const char *restrict format, ...);
|
||||||
|
TWN_API void log_warn(const char *restrict format, ...);
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: this is why generics were invented. sorry, i'm tired today */
|
||||||
|
TWN_API double clamp(double d, double min, double max);
|
||||||
|
TWN_API float clampf(float f, float min, float max);
|
||||||
|
TWN_API int clampi(int i, int min, int max);
|
||||||
|
|
||||||
|
|
||||||
|
/* sets buf_out to a pointer to a byte buffer which must be freed. */
|
||||||
|
/* returns the size of this buffer. */
|
||||||
|
TWN_API int64_t file_to_bytes(const char *path, unsigned char **buf_out);
|
||||||
|
|
||||||
|
/* returns a pointer to a string which must be freed */
|
||||||
|
TWN_API char *file_to_str(const char *path);
|
||||||
|
|
||||||
|
/* returns true if the file exists in the filesystem */
|
||||||
|
TWN_API bool file_exists(const char *path);
|
||||||
|
|
||||||
|
|
||||||
|
/* saves all texture atlases as BMP files in the write directory */
|
||||||
|
TWN_API void textures_dump_atlases(void);
|
||||||
|
|
||||||
|
|
||||||
|
/* returns true if str ends with suffix */
|
||||||
|
TWN_API TWN_API bool strends(const char *str, const char *suffix);
|
||||||
|
|
||||||
|
|
||||||
|
/* */
|
||||||
|
/* GAME LOGIC UTILITIES */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
/* calculates the overlap of two rectangles and places it in result. */
|
||||||
|
/* result may be NULL. if this is the case, it will simply be ignored. */
|
||||||
|
/* returns true if the rectangles are indeed intersecting. */
|
||||||
|
TWN_API bool overlap_rect(const Recti *a, const Recti *b, Recti *result);
|
||||||
|
TWN_API bool overlap_frect(const Rect *a, const Rect *b, Rect *result);
|
||||||
|
|
||||||
|
/* returns true if two rectangles are intersecting */
|
||||||
|
TWN_API bool intersect_rect(const Recti *a, const Recti *b);
|
||||||
|
TWN_API bool intersect_frect(const Rect *a, const Rect *b);
|
||||||
|
|
||||||
|
/* TODO: generics and specials (see m_vec2_from() for an example)*/
|
||||||
|
TWN_API Rect to_frect(Recti rect);
|
||||||
|
|
||||||
|
TWN_API Vec2 frect_center(Rect rect);
|
||||||
|
|
||||||
|
|
||||||
|
/* decrements an lvalue (which should be an int), stopping at 0 */
|
||||||
/* meant for tick-based timers in game logic */
|
/* meant for tick-based timers in game logic */
|
||||||
/*
|
/*
|
||||||
* example:
|
* example:
|
||||||
* tick_timer(&player->jump_air_timer);
|
* tick_timer(&player->jump_air_timer);
|
||||||
*/
|
*/
|
||||||
TWN_API int32_t timer_tick_frames(int32_t frames_left);
|
TWN_API void tick_timer(int *value);
|
||||||
|
|
||||||
/* decrements a floating point second-based timer, stopping at 0.0f */
|
/* decrements a floating point second-based timer, stopping at 0.0 */
|
||||||
/* meant for poll based real time logic in game logic */
|
/* meant for poll based real time logic in game logic */
|
||||||
/* note that it should be decremented only on the next tick after its creation */
|
/* note that it should be decremented only on the next tick after its creation */
|
||||||
TWN_API float timer_tick_seconds(float seconds_left);
|
TWN_API void tick_ftimer(float *value);
|
||||||
|
|
||||||
typedef struct TimerElapseFramesResult {
|
/* same as `tick_ftimer` but instead of clamping it repeats */
|
||||||
bool elapsed; int32_t frames_left;
|
/* returns true if value was cycled */
|
||||||
} TimerElapseFramesResult;
|
TWN_API bool repeat_ftimer(float *value, float at);
|
||||||
TWN_API TimerElapseFramesResult timer_elapse_frames(int32_t frames_left, int32_t interval);
|
|
||||||
|
|
||||||
typedef struct TimerElapseSecondsResult {
|
|
||||||
bool elapsed; float seconds_left;
|
|
||||||
} TimerElapseSecondsResult;
|
|
||||||
TWN_API TimerElapseSecondsResult timer_elapse_seconds(float seconds_left, float interval);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -9,6 +9,15 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* aren't macros to prevent double evaluation with side effects */
|
||||||
|
/* maybe could be inlined? i hope LTO will resolve this */
|
||||||
|
static inline Vec2 vec2_from_vec2i(Vec2i vec) {
|
||||||
|
return (Vec2) {
|
||||||
|
.x = (float)vec.x,
|
||||||
|
.y = (float)vec.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static inline Vec3 vec3_add(Vec3 a, Vec3 b) {
|
static inline Vec3 vec3_add(Vec3 a, Vec3 b) {
|
||||||
return (Vec3) { 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 };
|
||||||
}
|
}
|
||||||
|
@ -1,406 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "twn",
|
|
||||||
|
|
||||||
"procedures": {
|
|
||||||
"input_action": {
|
|
||||||
"module": "input",
|
|
||||||
"symbol": "bind_action",
|
|
||||||
"header": "twn_input.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "name", "type": "char *" },
|
|
||||||
{ "name": "control", "type": "Control" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"input_action_pressed": {
|
|
||||||
"module": "input",
|
|
||||||
"symbol": "action_pressed",
|
|
||||||
"header": "twn_input.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "name", "type": "char *" }
|
|
||||||
],
|
|
||||||
"return": "bool"
|
|
||||||
},
|
|
||||||
|
|
||||||
"input_action_just_pressed": {
|
|
||||||
"module": "input",
|
|
||||||
"symbol": "action_just_pressed",
|
|
||||||
"header": "twn_input.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "name", "type": "char *" }
|
|
||||||
],
|
|
||||||
"return": "bool"
|
|
||||||
},
|
|
||||||
|
|
||||||
"input_action_just_released": {
|
|
||||||
"module": "input",
|
|
||||||
"symbol": "action_just_released",
|
|
||||||
"header": "twn_input.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "name", "type": "char *" }
|
|
||||||
],
|
|
||||||
"return": "bool"
|
|
||||||
},
|
|
||||||
|
|
||||||
"input_action_position": {
|
|
||||||
"module": "input",
|
|
||||||
"symbol": "get_action_position",
|
|
||||||
"header": "twn_input.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "name", "type": "char *" }
|
|
||||||
],
|
|
||||||
"return": "Vec2"
|
|
||||||
},
|
|
||||||
|
|
||||||
"input_mouse_captured": {
|
|
||||||
"module": "input",
|
|
||||||
"symbol": "set_mouse_captured",
|
|
||||||
"header": "twn_input.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "enabled", "type": "bool" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"draw_sprite": {
|
|
||||||
"module": "draw",
|
|
||||||
"symbol": "sprite",
|
|
||||||
"header": "twn_draw.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "path", "type": "char *" },
|
|
||||||
{ "name": "rect", "type": "Rect" },
|
|
||||||
{ "name": "texture_region", "type": "Rect *", "default": {} },
|
|
||||||
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } },
|
|
||||||
{ "name": "rotation", "type": "float", "default": 0.0 },
|
|
||||||
{ "name": "flip_x", "type": "bool", "default": false },
|
|
||||||
{ "name": "flip_y", "type": "bool", "default": false },
|
|
||||||
{ "name": "stretch", "type": "bool", "default": true }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"draw_rectangle": {
|
|
||||||
"module": "draw",
|
|
||||||
"symbol": "rectangle",
|
|
||||||
"header": "twn_draw.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "rect", "type": "Rect" },
|
|
||||||
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"draw_circle": {
|
|
||||||
"module": "draw",
|
|
||||||
"symbol": "circle",
|
|
||||||
"header": "twn_draw.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "position", "type": "Vec2" },
|
|
||||||
{ "name": "radius", "type": "float" },
|
|
||||||
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"draw_text": {
|
|
||||||
"module": "draw",
|
|
||||||
"symbol": "text",
|
|
||||||
"header": "twn_draw.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "string", "type": "char *" },
|
|
||||||
{ "name": "position", "type": "Vec2" },
|
|
||||||
{ "name": "height", "type": "float", "default": 22 },
|
|
||||||
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } },
|
|
||||||
{ "name": "font", "type": "char *", "default": {} }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"draw_text_width": {
|
|
||||||
"module": "draw",
|
|
||||||
"symbol": "text_width",
|
|
||||||
"header": "twn_draw.h",
|
|
||||||
"params": [
|
|
||||||
{ "name": "string", "type": "char *" },
|
|
||||||
{ "name": "height", "type": "float", "default": 22 },
|
|
||||||
{ "name": "font", "type": "char *", "default": {} }
|
|
||||||
],
|
|
||||||
"return": "float"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"types": {
|
|
||||||
"Vec2": {
|
|
||||||
"fields": [
|
|
||||||
{ "name": "x", "type": "float" },
|
|
||||||
{ "name": "y", "type": "float" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"Vec3": {
|
|
||||||
"fields": [
|
|
||||||
{ "name": "x", "type": "float" },
|
|
||||||
{ "name": "y", "type": "float" },
|
|
||||||
{ "name": "z", "type": "float" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"Vec4": {
|
|
||||||
"fields": [
|
|
||||||
{ "name": "x", "type": "float" },
|
|
||||||
{ "name": "y", "type": "float" },
|
|
||||||
{ "name": "z", "type": "float" },
|
|
||||||
{ "name": "w", "type": "float" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"Color": {
|
|
||||||
"fields": [
|
|
||||||
{ "name": "r", "type": "uint8_t" },
|
|
||||||
{ "name": "g", "type": "uint8_t" },
|
|
||||||
{ "name": "b", "type": "uint8_t" },
|
|
||||||
{ "name": "a", "type": "uint8_t" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"Rect": {
|
|
||||||
"fields": [
|
|
||||||
{ "name": "x", "type": "float" },
|
|
||||||
{ "name": "y", "type": "float" },
|
|
||||||
{ "name": "w", "type": "float" },
|
|
||||||
{ "name": "h", "type": "float" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"Control": {
|
|
||||||
"enums": {
|
|
||||||
"A": 4,
|
|
||||||
"B": 5,
|
|
||||||
"C": 6,
|
|
||||||
"D": 7,
|
|
||||||
"E": 8,
|
|
||||||
"F": 9,
|
|
||||||
"G": 10,
|
|
||||||
"H": 11,
|
|
||||||
"I": 12,
|
|
||||||
"J": 13,
|
|
||||||
"K": 14,
|
|
||||||
"L": 15,
|
|
||||||
"M": 16,
|
|
||||||
"N": 17,
|
|
||||||
"O": 18,
|
|
||||||
"P": 19,
|
|
||||||
"Q": 20,
|
|
||||||
"R": 21,
|
|
||||||
"S": 22,
|
|
||||||
"T": 23,
|
|
||||||
"U": 24,
|
|
||||||
"V": 25,
|
|
||||||
"W": 26,
|
|
||||||
"X": 27,
|
|
||||||
"Y": 28,
|
|
||||||
"Z": 29,
|
|
||||||
"1": 30,
|
|
||||||
"2": 31,
|
|
||||||
"3": 32,
|
|
||||||
"4": 33,
|
|
||||||
"5": 34,
|
|
||||||
"6": 35,
|
|
||||||
"7": 36,
|
|
||||||
"8": 37,
|
|
||||||
"9": 38,
|
|
||||||
"0": 39,
|
|
||||||
"RETURN": 40,
|
|
||||||
"ESCAPE": 41,
|
|
||||||
"BACKSPACE": 42,
|
|
||||||
"TAB": 43,
|
|
||||||
"SPACE": 44,
|
|
||||||
"MINUS": 45,
|
|
||||||
"EQUALS": 46,
|
|
||||||
"LEFTBRACKET": 47,
|
|
||||||
"RIGHTBRACKET": 48,
|
|
||||||
"BACKSLASH": 49,
|
|
||||||
"NONUSHASH": 50,
|
|
||||||
"SEMICOLON": 51,
|
|
||||||
"APOSTROPHE": 52,
|
|
||||||
"GRAVE": 53,
|
|
||||||
"COMMA": 54,
|
|
||||||
"PERIOD": 55,
|
|
||||||
"SLASH": 56,
|
|
||||||
"CAPSLOCK": 57,
|
|
||||||
"F1": 58,
|
|
||||||
"F2": 59,
|
|
||||||
"F3": 60,
|
|
||||||
"F4": 61,
|
|
||||||
"F5": 62,
|
|
||||||
"F6": 63,
|
|
||||||
"F7": 64,
|
|
||||||
"F8": 65,
|
|
||||||
"F9": 66,
|
|
||||||
"F10": 67,
|
|
||||||
"F11": 68,
|
|
||||||
"F12": 69,
|
|
||||||
"PRINTSCREEN": 70,
|
|
||||||
"SCROLLLOCK": 71,
|
|
||||||
"PAUSE": 72,
|
|
||||||
"INSERT": 73,
|
|
||||||
"HOME": 74,
|
|
||||||
"PAGEUP": 75,
|
|
||||||
"DELETE": 76,
|
|
||||||
"END": 77,
|
|
||||||
"PAGEDOWN": 78,
|
|
||||||
"RIGHT": 79,
|
|
||||||
"LEFT": 80,
|
|
||||||
"DOWN": 81,
|
|
||||||
"UP": 82,
|
|
||||||
"NUMLOCKCLEAR": 83,
|
|
||||||
"KP_DIVIDE": 84,
|
|
||||||
"KP_MULTIPLY": 85,
|
|
||||||
"KP_MINUS": 86,
|
|
||||||
"KP_PLUS": 87,
|
|
||||||
"KP_ENTER": 88,
|
|
||||||
"KP_1": 89,
|
|
||||||
"KP_2": 90,
|
|
||||||
"KP_3": 91,
|
|
||||||
"KP_4": 92,
|
|
||||||
"KP_5": 93,
|
|
||||||
"KP_6": 94,
|
|
||||||
"KP_7": 95,
|
|
||||||
"KP_8": 96,
|
|
||||||
"KP_9": 97,
|
|
||||||
"KP_0": 98,
|
|
||||||
"KP_PERIOD": 99,
|
|
||||||
"NONUSBACKSLASH": 100,
|
|
||||||
"APPLICATION": 101,
|
|
||||||
"POWER": 102,
|
|
||||||
"KP_EQUALS": 103,
|
|
||||||
"F13": 104,
|
|
||||||
"F14": 105,
|
|
||||||
"F15": 106,
|
|
||||||
"F16": 107,
|
|
||||||
"F17": 108,
|
|
||||||
"F18": 109,
|
|
||||||
"F19": 110,
|
|
||||||
"F20": 111,
|
|
||||||
"F21": 112,
|
|
||||||
"F22": 113,
|
|
||||||
"F23": 114,
|
|
||||||
"F24": 115,
|
|
||||||
"EXECUTE": 116,
|
|
||||||
"HELP": 117,
|
|
||||||
"MENU": 118,
|
|
||||||
"SELECT": 119,
|
|
||||||
"STOP": 120,
|
|
||||||
"AGAIN": 121,
|
|
||||||
"UNDO": 122,
|
|
||||||
"CUT": 123,
|
|
||||||
"COPY": 124,
|
|
||||||
"PASTE": 125,
|
|
||||||
"FIND": 126,
|
|
||||||
"MUTE": 127,
|
|
||||||
"VOLUMEUP": 128,
|
|
||||||
"VOLUMEDOWN": 129,
|
|
||||||
"KP_COMMA": 133,
|
|
||||||
"KP_EQUALSAS400": 134,
|
|
||||||
"INTERNATIONAL1": 135,
|
|
||||||
"INTERNATIONAL2": 136,
|
|
||||||
"INTERNATIONAL3": 137,
|
|
||||||
"INTERNATIONAL4": 138,
|
|
||||||
"INTERNATIONAL5": 139,
|
|
||||||
"INTERNATIONAL6": 140,
|
|
||||||
"INTERNATIONAL7": 141,
|
|
||||||
"INTERNATIONAL8": 142,
|
|
||||||
"INTERNATIONAL9": 143,
|
|
||||||
"LANG1": 144,
|
|
||||||
"LANG2": 145,
|
|
||||||
"LANG3": 146,
|
|
||||||
"LANG4": 147,
|
|
||||||
"LANG5": 148,
|
|
||||||
"LANG6": 149,
|
|
||||||
"LANG7": 150,
|
|
||||||
"LANG8": 151,
|
|
||||||
"LANG9": 152,
|
|
||||||
"ALTERASE": 153,
|
|
||||||
"SYSREQ": 154,
|
|
||||||
"CANCEL": 155,
|
|
||||||
"CLEAR": 156,
|
|
||||||
"PRIOR": 157,
|
|
||||||
"RETURN2": 158,
|
|
||||||
"SEPARATOR": 159,
|
|
||||||
"OUT": 160,
|
|
||||||
"OPER": 161,
|
|
||||||
"CLEARAGAIN": 162,
|
|
||||||
"CRSEL": 163,
|
|
||||||
"EXSEL": 164,
|
|
||||||
"KP_00": 176,
|
|
||||||
"KP_000": 177,
|
|
||||||
"THOUSANDSSEPARATOR": 178,
|
|
||||||
"DECIMALSEPARATOR": 179,
|
|
||||||
"CURRENCYUNIT": 180,
|
|
||||||
"CURRENCYSUBUNIT": 181,
|
|
||||||
"KP_LEFTPAREN": 182,
|
|
||||||
"KP_RIGHTPAREN": 183,
|
|
||||||
"KP_LEFTBRACE": 184,
|
|
||||||
"KP_RIGHTBRACE": 185,
|
|
||||||
"KP_TAB": 186,
|
|
||||||
"KP_BACKSPACE": 187,
|
|
||||||
"KP_A": 188,
|
|
||||||
"KP_B": 189,
|
|
||||||
"KP_C": 190,
|
|
||||||
"KP_D": 191,
|
|
||||||
"KP_E": 192,
|
|
||||||
"KP_F": 193,
|
|
||||||
"KP_XOR": 194,
|
|
||||||
"KP_POWER": 195,
|
|
||||||
"KP_PERCENT": 196,
|
|
||||||
"KP_LESS": 197,
|
|
||||||
"KP_GREATER": 198,
|
|
||||||
"KP_AMPERSAND": 199,
|
|
||||||
"KP_DBLAMPERSAND": 200,
|
|
||||||
"KP_VERTICALBAR": 201,
|
|
||||||
"KP_DBLVERTICALBAR": 202,
|
|
||||||
"KP_COLON": 203,
|
|
||||||
"KP_HASH": 204,
|
|
||||||
"KP_SPACE": 205,
|
|
||||||
"KP_AT": 206,
|
|
||||||
"KP_EXCLAM": 207,
|
|
||||||
"KP_MEMSTORE": 208,
|
|
||||||
"KP_MEMRECALL": 209,
|
|
||||||
"KP_MEMCLEAR": 210,
|
|
||||||
"KP_MEMADD": 211,
|
|
||||||
"KP_MEMSUBTRACT": 212,
|
|
||||||
"KP_MEMMULTIPLY": 213,
|
|
||||||
"KP_MEMDIVIDE": 214,
|
|
||||||
"KP_PLUSMINUS": 215,
|
|
||||||
"KP_CLEAR": 216,
|
|
||||||
"KP_CLEARENTRY": 217,
|
|
||||||
"KP_BINARY": 218,
|
|
||||||
"KP_OCTAL": 219,
|
|
||||||
"KP_DECIMAL": 220,
|
|
||||||
"KP_HEXADECIMAL": 221,
|
|
||||||
"LCTRL": 224,
|
|
||||||
"LSHIFT": 225,
|
|
||||||
"LALT": 226,
|
|
||||||
"LGUI": 227,
|
|
||||||
"RCTRL": 228,
|
|
||||||
"RSHIFT": 229,
|
|
||||||
"RALT": 230,
|
|
||||||
"RGUI": 231,
|
|
||||||
"MODE": 257,
|
|
||||||
"KBDILLUMTOGGLE": 278,
|
|
||||||
"KBDILLUMDOWN": 279,
|
|
||||||
"KBDILLUMUP": 280,
|
|
||||||
"EJECT": 281,
|
|
||||||
"SLEEP": 282,
|
|
||||||
"APP1": 283,
|
|
||||||
"APP2": 284,
|
|
||||||
"AUDIOREWIND": 285,
|
|
||||||
"AUDIOFASTFORWARD": 286,
|
|
||||||
"SOFTLEFT": 287,
|
|
||||||
"SOFTRIGHT": 288,
|
|
||||||
"CALL": 289,
|
|
||||||
"ENDCALL": 290,
|
|
||||||
"LEFT_MOUSE": 513,
|
|
||||||
"RIGHT_MOUSE": 515,
|
|
||||||
"MIDDLE_MOUSE": 514,
|
|
||||||
"X1": 516,
|
|
||||||
"X2": 517
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
#include "twn_game_object_c.h"
|
#include "twn_game_object_c.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_util_c.h"
|
#include "twn_util_c.h"
|
||||||
#include "twn_util.h"
|
|
||||||
|
|
||||||
#include <x-watcher.h>
|
#include <x-watcher.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "twn_game_object_c.h"
|
#include "twn_game_object_c.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_util_c.h"
|
#include "twn_util_c.h"
|
||||||
#include "twn_util.h"
|
|
||||||
|
|
||||||
#include <errhandlingapi.h>
|
#include <errhandlingapi.h>
|
||||||
#include <libloaderapi.h>
|
#include <libloaderapi.h>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include "twn_util.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_draw_c.h"
|
#include "twn_draw_c.h"
|
||||||
#include "twn_draw.h"
|
#include "twn_draw.h"
|
||||||
@ -23,46 +24,58 @@ void draw_circle(Vec2 position, float radius, Color color) {
|
|||||||
|
|
||||||
|
|
||||||
void create_circle_geometry(Vec2 position,
|
void create_circle_geometry(Vec2 position,
|
||||||
|
Color color,
|
||||||
float radius,
|
float radius,
|
||||||
size_t num_vertices,
|
size_t num_vertices,
|
||||||
Vec2 vertices[])
|
SDL_Vertex vertices[],
|
||||||
|
int indices[])
|
||||||
{
|
{
|
||||||
SDL_assert(num_vertices <= CIRCLE_VERTICES_MAX);
|
|
||||||
|
|
||||||
/* the angle (in radians) to rotate by on each iteration */
|
/* the angle (in radians) to rotate by on each iteration */
|
||||||
float seg_rotation_angle = (360.0f / (float)(num_vertices - 2)) * ((float)M_PI / 180);
|
float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180);
|
||||||
|
|
||||||
vertices[0].x = (float)position.x;
|
vertices[0].position.x = (float)position.x;
|
||||||
vertices[0].y = (float)position.y;
|
vertices[0].position.y = (float)position.y;
|
||||||
|
vertices[0].color.r = color.r;
|
||||||
|
vertices[0].color.g = color.g;
|
||||||
|
vertices[0].color.b = color.b;
|
||||||
|
vertices[0].color.a = color.a;
|
||||||
|
vertices[0].tex_coord = (SDL_FPoint){ 0, 0 };
|
||||||
|
|
||||||
/* this point will rotate around the center */
|
/* this point will rotate around the center */
|
||||||
float start_x = 0.0f - radius;
|
float start_x = 0.0f - radius;
|
||||||
float start_y = 0.0f;
|
float start_y = 0.0f;
|
||||||
|
|
||||||
for (size_t i = 1; i < num_vertices - 1; ++i) {
|
for (size_t i = 1; i < num_vertices + 1; ++i) {
|
||||||
float final_seg_rotation_angle = (float)i * seg_rotation_angle;
|
float final_seg_rotation_angle = (float)i * seg_rotation_angle;
|
||||||
|
|
||||||
float c, s;
|
vertices[i].position.x =
|
||||||
sincosf(final_seg_rotation_angle, &s, &c);
|
cosf(final_seg_rotation_angle) * start_x -
|
||||||
|
sinf(final_seg_rotation_angle) * start_y;
|
||||||
|
vertices[i].position.y =
|
||||||
|
cosf(final_seg_rotation_angle) * start_y +
|
||||||
|
sinf(final_seg_rotation_angle) * start_x;
|
||||||
|
|
||||||
vertices[i].x = c * start_x - s * start_y;
|
vertices[i].position.x += position.x;
|
||||||
vertices[i].y = c * start_y + s * start_x;
|
vertices[i].position.y += position.y;
|
||||||
|
|
||||||
vertices[i].x += position.x;
|
vertices[i].color.r = color.r;
|
||||||
vertices[i].y += position.y;
|
vertices[i].color.g = color.g;
|
||||||
}
|
vertices[i].color.b = color.b;
|
||||||
|
vertices[i].color.a = color.a;
|
||||||
|
|
||||||
// place a redundant vertex to make proper circling over shared element buffer
|
vertices[i].tex_coord = (SDL_FPoint){ 0, 0 };
|
||||||
{
|
|
||||||
float final_seg_rotation_angle = (float)1 * seg_rotation_angle;
|
|
||||||
|
|
||||||
float c, s;
|
|
||||||
sincosf(final_seg_rotation_angle, &s, &c);
|
|
||||||
|
|
||||||
vertices[num_vertices - 1].x = c * start_x - s * start_y;
|
size_t triangle_offset = 3 * (i - 1);
|
||||||
vertices[num_vertices - 1].y = c * start_y + s * start_x;
|
|
||||||
|
|
||||||
vertices[num_vertices - 1].x += position.x;
|
/* center point index */
|
||||||
vertices[num_vertices - 1].y += position.y;
|
indices[triangle_offset] = 0;
|
||||||
|
/* generated point index */
|
||||||
|
indices[triangle_offset + 1] = (int)i;
|
||||||
|
|
||||||
|
size_t index = (i + 1) % num_vertices;
|
||||||
|
if (index == 0)
|
||||||
|
index = num_vertices;
|
||||||
|
indices[triangle_offset + 2] = (int)index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
#include "twn_draw_c.h"
|
#include "twn_draw_c.h"
|
||||||
#include "twn_draw.h"
|
#include "twn_draw.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_camera_c.h"
|
#include "twn_camera.h"
|
||||||
#include "twn_types.h"
|
#include "twn_types.h"
|
||||||
#include "twn_vec.h"
|
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <stb_ds.h>
|
#include <stb_ds.h>
|
||||||
@ -36,8 +35,8 @@ void render_queue_clear(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_thickness, Color color) {
|
void draw_9slice(const char *texture_path, int texture_w, int texture_h, int border_thickness, Rect rect, Color color) {
|
||||||
const float bt = border_thickness;
|
const float bt = (float)border_thickness; /* i know! */
|
||||||
const float bt2 = bt * 2; /* combined size of the two borders in an axis */
|
const float bt2 = bt * 2; /* combined size of the two borders in an axis */
|
||||||
|
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, top_left),
|
m_set(rect, top_left),
|
||||||
m_opt(texture_region, ((Rect) { 0, 0, bt, bt })),
|
m_opt(texture_region, ((Rect) { 0, 0, bt, bt })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
@ -64,9 +63,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, top_center),
|
m_set(rect, top_center),
|
||||||
m_opt(texture_region, ((Rect) { bt, 0, corners.x - bt2, bt })),
|
m_opt(texture_region, ((Rect) { bt, 0, (float)texture_w - bt2, bt })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -79,9 +78,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, top_right),
|
m_set(rect, top_right),
|
||||||
m_opt(texture_region, ((Rect) { corners.x - bt, 0, bt, bt })),
|
m_opt(texture_region, ((Rect) { (float)texture_w - bt, 0, bt, bt })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -94,9 +93,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, center_left),
|
m_set(rect, center_left),
|
||||||
m_opt(texture_region, ((Rect) { 0, bt, bt, corners.y - bt2 })),
|
m_opt(texture_region, ((Rect) { 0, bt, bt, (float)texture_h - bt2 })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -109,9 +108,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, center_right),
|
m_set(rect, center_right),
|
||||||
m_opt(texture_region, ((Rect) { corners.x - bt, bt, bt, corners.y - bt2 })),
|
m_opt(texture_region, ((Rect) { (float)texture_w - bt, bt, bt, (float)texture_h - bt2 })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -124,9 +123,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, bottom_left),
|
m_set(rect, bottom_left),
|
||||||
m_opt(texture_region, ((Rect) { 0, corners.y - bt, bt, bt })),
|
m_opt(texture_region, ((Rect) { 0, (float)texture_h - bt, bt, bt })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -139,9 +138,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, bottom_center),
|
m_set(rect, bottom_center),
|
||||||
m_opt(texture_region, ((Rect) { bt, corners.y - bt, corners.x - bt2, bt })),
|
m_opt(texture_region, ((Rect) { bt, (float)texture_h - bt, (float)texture_w - bt2, bt })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -154,9 +153,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, bottom_right),
|
m_set(rect, bottom_right),
|
||||||
m_opt(texture_region, ((Rect) { corners.x - bt, corners.y - bt, bt, bt })),
|
m_opt(texture_region, ((Rect) { (float)texture_w - bt, (float)texture_h - bt, bt, bt })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -169,9 +168,9 @@ void draw_nine_slice(const char *texture, Vec2 corners, Rect rect, float border_
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_sprite(
|
m_sprite(
|
||||||
m_set(texture, texture),
|
m_set(path, texture_path),
|
||||||
m_set(rect, center),
|
m_set(rect, center),
|
||||||
m_opt(texture_region, ((Rect) { bt, bt, corners.x - bt2, corners.y - bt2 })),
|
m_opt(texture_region, ((Rect) { bt, bt, (float)texture_w - bt2, (float)texture_h - bt2 })),
|
||||||
m_opt(color, color),
|
m_opt(color, color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -358,64 +357,33 @@ void render(void) {
|
|||||||
float ratio = (float)ctx.window_dims.y / (float)ctx.base_render_height;
|
float ratio = (float)ctx.window_dims.y / (float)ctx.base_render_height;
|
||||||
int w = (int)((float)ctx.base_render_width * ratio);
|
int w = (int)((float)ctx.base_render_width * ratio);
|
||||||
setup_viewport(
|
setup_viewport(
|
||||||
(int)ctx.window_dims.x / 2 - w / 2,
|
ctx.window_dims.x / 2 - w / 2,
|
||||||
0,
|
0,
|
||||||
w,
|
w,
|
||||||
(int)ctx.window_dims.y
|
ctx.window_dims.y
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
float ratio = (float)ctx.window_dims.x / (float)ctx.base_render_width;
|
float ratio = (float)ctx.window_dims.x / (float)ctx.base_render_width;
|
||||||
int h = (int)((float)ctx.base_render_height * ratio);
|
int h = (int)((float)ctx.base_render_height * ratio);
|
||||||
setup_viewport(
|
setup_viewport(
|
||||||
0,
|
0,
|
||||||
(int)ctx.window_dims.y / 2 - h / 2,
|
ctx.window_dims.y / 2 - h / 2,
|
||||||
(int)ctx.window_dims.x,
|
ctx.window_dims.x,
|
||||||
h
|
h
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start_render_frame(); {
|
render_space();
|
||||||
render_space();
|
render_skybox(); /* after space, as to use depth buffer for early rejection */
|
||||||
render_skybox(); /* after space, as to use depth buffer for early z rejection */
|
render_2d();
|
||||||
render_2d();
|
swap_buffers();
|
||||||
} end_render_frame();
|
clear_draw_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void draw_camera(Vec3 position, float fov, Vec3 up, Vec3 direction) {
|
void draw_camera(const Camera *const camera) {
|
||||||
Camera const camera = {
|
/* TODO: skip recaulculating if it's the same? */
|
||||||
.fov = fov,
|
camera_projection_matrix = camera_perspective(camera);
|
||||||
.pos = position,
|
camera_look_at_matrix = camera_look_at(camera);
|
||||||
.target = direction,
|
|
||||||
.up = up,
|
|
||||||
};
|
|
||||||
camera_projection_matrix = camera_perspective(&camera);
|
|
||||||
camera_look_at_matrix = camera_look_at(&camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* TODO: https://stackoverflow.com/questions/62493770/how-to-add-roll-in-camera-class */
|
|
||||||
DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position, float fov, float roll, float pitch, float yaw) {
|
|
||||||
(void)roll;
|
|
||||||
float yawc, yaws, pitchc, pitchs;
|
|
||||||
sincosf(yaw, &yaws, &yawc);
|
|
||||||
sincosf(pitch, &pitchs, &pitchc);
|
|
||||||
Camera const camera = {
|
|
||||||
.fov = fov,
|
|
||||||
.pos = position,
|
|
||||||
.target = m_vec_norm(((Vec3){
|
|
||||||
yawc * pitchc,
|
|
||||||
pitchs,
|
|
||||||
yaws * pitchc,
|
|
||||||
})),
|
|
||||||
.up = (Vec3){0, 1, 0},
|
|
||||||
};
|
|
||||||
camera_projection_matrix = camera_perspective(&camera);
|
|
||||||
camera_look_at_matrix = camera_look_at(&camera);
|
|
||||||
|
|
||||||
return (DrawCameraFromPrincipalAxesResult) {
|
|
||||||
.direction = camera.target,
|
|
||||||
.up = camera.up,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "twn_textures_c.h"
|
#include "twn_textures_c.h"
|
||||||
#include "twn_text_c.h"
|
#include "twn_text_c.h"
|
||||||
|
#include "twn_util.h"
|
||||||
#include "twn_option.h"
|
#include "twn_option.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
@ -19,9 +20,7 @@
|
|||||||
extern Matrix4 camera_projection_matrix;
|
extern Matrix4 camera_projection_matrix;
|
||||||
extern 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)
|
||||||
#define CIRCLE_VERTICES_MAX 2048
|
|
||||||
|
|
||||||
|
|
||||||
typedef GLuint VertexBuffer;
|
typedef GLuint VertexBuffer;
|
||||||
@ -83,22 +82,34 @@ typedef struct Primitive2D {
|
|||||||
} Primitive2D;
|
} Primitive2D;
|
||||||
|
|
||||||
/* union for in-place recalculation of texture coordinates */
|
/* union for in-place recalculation of texture coordinates */
|
||||||
/* needs to be later resolved in texture atlas */
|
union UncoloredSpaceTriangle {
|
||||||
typedef struct UncoloredSpaceTriangle {
|
/* pending for sending, uvs are not final as texture atlases could update */
|
||||||
Vec3 v0;
|
struct UncoloredSpaceTrianglePrimitive {
|
||||||
Vec2 uv0; /* in pixels */
|
Vec3 v0;
|
||||||
Vec3 v1;
|
Vec2 uv0; /* in pixels */
|
||||||
Vec2 uv1; /* in pixels */
|
Vec3 v1;
|
||||||
Vec3 v2;
|
Vec2 uv1; /* in pixels */
|
||||||
Vec2 uv2; /* in pixels */
|
Vec3 v2;
|
||||||
} UncoloredSpaceTriangle;
|
Vec2 uv2; /* in pixels */
|
||||||
|
} primitive;
|
||||||
|
|
||||||
|
/* TODO: have it packed? */
|
||||||
|
/* structure that is passed in opengl vertex array */
|
||||||
|
struct UncoloredSpaceTrianglePayload {
|
||||||
|
Vec3 v0;
|
||||||
|
Vec2 uv0;
|
||||||
|
Vec3 v1;
|
||||||
|
Vec2 uv1;
|
||||||
|
Vec3 v2;
|
||||||
|
Vec2 uv2;
|
||||||
|
} payload;
|
||||||
|
};
|
||||||
|
|
||||||
/* batch of primitives with overlapping properties */
|
/* batch of primitives with overlapping properties */
|
||||||
typedef struct MeshBatch {
|
typedef struct MeshBatch {
|
||||||
uint8_t *primitives; /* note: interpretation of it is arbitrary */
|
uint8_t *primitives;
|
||||||
} MeshBatch;
|
} MeshBatch;
|
||||||
|
|
||||||
/* TODO: use atlas id instead */
|
|
||||||
typedef struct MeshBatchItem {
|
typedef struct MeshBatchItem {
|
||||||
TextureKey key;
|
TextureKey key;
|
||||||
struct MeshBatch value;
|
struct MeshBatch value;
|
||||||
@ -118,13 +129,14 @@ void render_queue_clear(void);
|
|||||||
/* fills two existing arrays with the geometry data of a circle */
|
/* fills two existing arrays with the geometry data of a circle */
|
||||||
/* the size of indices must be at least 3 times the number of vertices */
|
/* the size of indices must be at least 3 times the number of vertices */
|
||||||
void create_circle_geometry(Vec2 position,
|
void create_circle_geometry(Vec2 position,
|
||||||
|
Color color,
|
||||||
float radius,
|
float radius,
|
||||||
size_t num_vertices,
|
size_t num_vertices,
|
||||||
Vec2 vertices[]);
|
SDL_Vertex vertices[],
|
||||||
|
int indices[]);
|
||||||
|
|
||||||
struct QuadBatch {
|
struct QuadBatch {
|
||||||
size_t size; /* how many primitives are in current batch */
|
size_t size; /* how many primitives are in current batch */
|
||||||
TextureKey texture_key;
|
|
||||||
TextureMode mode; /* how color should be applied */
|
TextureMode mode; /* how color should be applied */
|
||||||
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 */
|
||||||
@ -153,8 +165,6 @@ void text_cache_reset_arena(TextCache *cache);
|
|||||||
|
|
||||||
VertexBuffer create_vertex_buffer(void);
|
VertexBuffer create_vertex_buffer(void);
|
||||||
|
|
||||||
VertexBuffer get_scratch_vertex_array(void);
|
|
||||||
|
|
||||||
void delete_vertex_buffer(VertexBuffer buffer);
|
void delete_vertex_buffer(VertexBuffer buffer);
|
||||||
|
|
||||||
void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes);
|
void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes);
|
||||||
@ -177,9 +187,7 @@ void swap_buffers(void);
|
|||||||
|
|
||||||
void set_depth_range(double low, double high);
|
void set_depth_range(double low, double high);
|
||||||
|
|
||||||
VertexBuffer get_quad_element_buffer(void);
|
void bind_quad_element_buffer(void);
|
||||||
|
|
||||||
VertexBuffer get_circle_element_buffer(void);
|
|
||||||
|
|
||||||
void render_circle(const CirclePrimitive *circle);
|
void render_circle(const CirclePrimitive *circle);
|
||||||
|
|
||||||
@ -230,8 +238,4 @@ void pop_fog(void);
|
|||||||
|
|
||||||
void finally_pop_fog(void);
|
void finally_pop_fog(void);
|
||||||
|
|
||||||
void start_render_frame(void);
|
|
||||||
|
|
||||||
void end_render_frame(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "twn_draw.h"
|
#include "twn_draw.h"
|
||||||
#include "twn_draw_c.h"
|
#include "twn_draw_c.h"
|
||||||
|
#include "twn_util.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,6 @@
|
|||||||
#include "twn_draw_c.h"
|
#include "twn_draw_c.h"
|
||||||
|
#include "twn_engine_context_c.h"
|
||||||
|
#include "twn_util_c.h"
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
@ -30,57 +32,65 @@ void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VertexBuffer get_quad_element_buffer(void) {
|
void bind_quad_element_buffer(void) {
|
||||||
static VertexBuffer buffer = 0;
|
static GLuint buffer = 0;
|
||||||
|
|
||||||
/* it's only generated once at runtime */
|
/* it's only generated once at runtime */
|
||||||
/* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */
|
|
||||||
if (buffer == 0) {
|
if (buffer == 0) {
|
||||||
buffer = create_vertex_buffer();
|
glGenBuffers(1, &buffer);
|
||||||
VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 );
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||||
|
QUAD_ELEMENT_BUFFER_LENGTH * 6 * sizeof(uint16_t),
|
||||||
|
NULL,
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
|
||||||
for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) {
|
uint16_t *const indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER,
|
||||||
GLshort indices[6];
|
GL_WRITE_ONLY);
|
||||||
indices[0] = (GLshort)(i * 4 + 0);
|
if (!indices)
|
||||||
indices[1] = (GLshort)(i * 4 + 1);
|
CRY("Quad indices generation", "glMapBuffer() failed");
|
||||||
indices[2] = (GLshort)(i * 4 + 2);
|
|
||||||
indices[3] = (GLshort)(i * 4 + 2);
|
|
||||||
indices[4] = (GLshort)(i * 4 + 3);
|
|
||||||
indices[5] = (GLshort)(i * 4 + 0);
|
|
||||||
|
|
||||||
push_to_vertex_buffer_builder(&builder, indices, sizeof indices);
|
for (uint16_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) {
|
||||||
|
indices[i * 6 + 0] = (uint16_t)(i * 4 + 0);
|
||||||
|
indices[i * 6 + 1] = (uint16_t)(i * 4 + 1);
|
||||||
|
indices[i * 6 + 2] = (uint16_t)(i * 4 + 2);
|
||||||
|
indices[i * 6 + 3] = (uint16_t)(i * 4 + 2);
|
||||||
|
indices[i * 6 + 4] = (uint16_t)(i * 4 + 3);
|
||||||
|
indices[i * 6 + 5] = (uint16_t)(i * 4 + 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SDL_assert_always(buffer);
|
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
|
||||||
|
|
||||||
return buffer;
|
} else
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VertexBuffer get_circle_element_buffer(void) {
|
void clear_draw_buffer(void) {
|
||||||
static VertexBuffer buffer = 0;
|
/* TODO: we can optimize a rectangle drawn over whole window to a clear color call*/
|
||||||
|
glClearColor((1.0f / 255) * 230,
|
||||||
|
(1.0f / 255) * 230,
|
||||||
|
(1.0f / 255) * 230, 1);
|
||||||
|
|
||||||
if (buffer == 0) {
|
glDepthRange(0.0, 1.0);
|
||||||
buffer = create_vertex_buffer();
|
glDepthMask(GL_TRUE);
|
||||||
VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * (CIRCLE_VERTICES_MAX - 2) * 3);
|
|
||||||
|
|
||||||
for (size_t i = 1; i < CIRCLE_VERTICES_MAX - 1; ++i) {
|
/* TODO: don't clear color when skybox is applied? */
|
||||||
/* first one is center point index, always zero */
|
/* for that window should match framebuffer */
|
||||||
GLshort indices[3];
|
/* also it is driver dependent, from what i can gather */
|
||||||
|
/* INFO: also, based on below, driver might prefer it staying this way */
|
||||||
indices[0] = 0;
|
/* https://gamedev.stackexchange.com/questions/90344/render-with-const-depth-value */
|
||||||
|
/* we could optionally load ARB_invalidate_subdata extension if it's available instead */
|
||||||
/* generated point index */
|
glClear(GL_COLOR_BUFFER_BIT |
|
||||||
indices[1] = (GLshort)i;
|
GL_DEPTH_BUFFER_BIT |
|
||||||
|
GL_STENCIL_BUFFER_BIT);
|
||||||
indices[2] = (GLshort)i + 1;
|
}
|
||||||
|
|
||||||
push_to_vertex_buffer_builder(&builder, indices, sizeof indices);
|
|
||||||
}
|
void swap_buffers(void) {
|
||||||
}
|
SDL_GL_SwapWindow(ctx.window);
|
||||||
|
}
|
||||||
SDL_assert_always(buffer);
|
|
||||||
|
|
||||||
return buffer;
|
void set_depth_range(double low, double high) {
|
||||||
|
glDepthRange(low, high);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#include "twn_draw.h"
|
#include "twn_draw.h"
|
||||||
#include "twn_draw_c.h"
|
#include "twn_draw_c.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
|
#include "twn_util.h"
|
||||||
|
#include "twn_util_c.h"
|
||||||
|
#include "twn_textures_c.h"
|
||||||
|
#include "twn_option.h"
|
||||||
|
|
||||||
#include <stb_ds.h>
|
#include <stb_ds.h>
|
||||||
|
|
||||||
@ -32,7 +36,7 @@ struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len)
|
|||||||
.constant_colored = true,
|
.constant_colored = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32_t uniform_color = *(const uint32_t *)(void const *)&primitives[0].rect.color;
|
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].rect.color;
|
||||||
|
|
||||||
/* batch size is clamped so that reallocated short indices could be used */
|
/* batch size is clamped so that reallocated short indices could be used */
|
||||||
if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
|
if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
|
||||||
@ -50,7 +54,7 @@ struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* if all are modulated the same we can skip sending the color data */
|
/* if all are modulated the same we can skip sending the color data */
|
||||||
if (*(const uint32_t *)(void const *)¤t->rect.color != uniform_color)
|
if (*(const uint32_t *)¤t->rect.color != uniform_color)
|
||||||
batch.constant_colored = false;
|
batch.constant_colored = false;
|
||||||
|
|
||||||
++batch.size;
|
++batch.size;
|
||||||
@ -68,7 +72,9 @@ void render_rect_batch(const Primitive2D primitives[],
|
|||||||
SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
|
SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
|
||||||
|
|
||||||
/* 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 */
|
||||||
VertexBuffer const vertex_array = get_scratch_vertex_array();
|
static VertexBuffer vertex_array = 0;
|
||||||
|
if (vertex_array == 0)
|
||||||
|
vertex_array = create_vertex_buffer();
|
||||||
|
|
||||||
use_texture_mode(batch.mode);
|
use_texture_mode(batch.mode);
|
||||||
|
|
||||||
@ -81,8 +87,8 @@ void render_rect_batch(const Primitive2D primitives[],
|
|||||||
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 RectPrimitive rect = primitives[cur].rect;
|
const RectPrimitive rect = primitives[cur].rect;
|
||||||
|
|
||||||
Vec2 v0 = { rect.rect.x, rect.rect.y };
|
Vec2 v0 = { rect.rect.x, rect.rect.y };
|
||||||
Vec2 v1 = { rect.rect.x, rect.rect.y + rect.rect.h };
|
Vec2 v1 = { rect.rect.x, rect.rect.y + rect.rect.h };
|
||||||
Vec2 v2 = { rect.rect.x + rect.rect.w, rect.rect.y + rect.rect.h };
|
Vec2 v2 = { rect.rect.x + rect.rect.w, rect.rect.y + rect.rect.h };
|
||||||
Vec2 v3 = { rect.rect.x + rect.rect.w, rect.rect.y };
|
Vec2 v3 = { rect.rect.x + rect.rect.w, rect.rect.y };
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ void draw_sprite_args(const DrawSpriteArgs args) {
|
|||||||
bool const stretch = m_or(args, stretch, false);
|
bool const stretch = m_or(args, stretch, false);
|
||||||
Rect const *texture_region = m_is_set(args, texture_region) ? &args.texture_region_opt : NULL;
|
Rect const *texture_region = m_is_set(args, texture_region) ? &args.texture_region_opt : NULL;
|
||||||
|
|
||||||
draw_sprite(args.texture, args.rect, texture_region, color, rotation, flip_x, flip_y, stretch);
|
draw_sprite(args.path, args.rect, texture_region, color, rotation, flip_x, flip_y, stretch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,13 +69,12 @@ struct QuadBatch collect_sprite_batch(const Primitive2D primitives[], size_t len
|
|||||||
|
|
||||||
struct QuadBatch batch = {
|
struct QuadBatch 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),
|
||||||
.texture_key = primitives[0].sprite.texture_key,
|
|
||||||
.constant_colored = true,
|
.constant_colored = true,
|
||||||
.repeat = primitives[0].sprite.repeat,
|
.repeat = primitives[0].sprite.repeat,
|
||||||
.textured = true,
|
.textured = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32_t uniform_color = *(const uint32_t *)(void const*)&primitives[0].sprite.color;
|
const uint32_t uniform_color = *(const uint32_t *)&primitives[0].sprite.color;
|
||||||
|
|
||||||
/* batch size is clamped so that reallocated short indices could be used */
|
/* batch size is clamped so that reallocated short indices could be used */
|
||||||
if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
|
if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
|
||||||
@ -109,7 +108,7 @@ struct QuadBatch collect_sprite_batch(const Primitive2D primitives[], size_t len
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* if all are modulated the same we can skip sending the color data */
|
/* if all are modulated the same we can skip sending the color data */
|
||||||
if (*(const uint32_t *)(void const *)¤t->sprite.color != uniform_color)
|
if (*(const uint32_t *)¤t->sprite.color != uniform_color)
|
||||||
batch.constant_colored = false;
|
batch.constant_colored = false;
|
||||||
|
|
||||||
++batch.size;
|
++batch.size;
|
||||||
@ -126,15 +125,16 @@ void render_sprite_batch(const Primitive2D primitives[],
|
|||||||
SDL_assert(primitives && batch.size != 0);
|
SDL_assert(primitives && batch.size != 0);
|
||||||
SDL_assert(primitives[0].type == PRIMITIVE_2D_SPRITE);
|
SDL_assert(primitives[0].type == PRIMITIVE_2D_SPRITE);
|
||||||
|
|
||||||
VertexBuffer const vertex_array = get_scratch_vertex_array();
|
/* single vertex array is used for every batch with NULL glBufferData() trick at the end */
|
||||||
|
static VertexBuffer vertex_array = 0;
|
||||||
|
if (vertex_array == 0)
|
||||||
|
vertex_array = create_vertex_buffer();
|
||||||
|
|
||||||
|
use_texture_mode(batch.mode);
|
||||||
|
|
||||||
const Rect dims =
|
const Rect dims =
|
||||||
textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key);
|
textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key);
|
||||||
|
|
||||||
/* cached srcrect */
|
|
||||||
Rect cached_srcrect = {0};
|
|
||||||
TextureKey cached_srcrect_key = TEXTURE_KEY_INVALID;
|
|
||||||
|
|
||||||
/* vertex population over a vertex buffer builder interface */
|
/* vertex population over a vertex buffer builder interface */
|
||||||
{
|
{
|
||||||
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);
|
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);
|
||||||
@ -144,16 +144,13 @@ void render_sprite_batch(const Primitive2D primitives[],
|
|||||||
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 SpritePrimitive sprite = primitives[cur].sprite;
|
const SpritePrimitive sprite = primitives[cur].sprite;
|
||||||
|
|
||||||
if (primitives[cur].sprite.texture_key.id != cached_srcrect_key.id) {
|
/* TODO: try caching it */
|
||||||
cached_srcrect = textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key);
|
const Rect srcrect =
|
||||||
cached_srcrect_key = primitives[cur].sprite.texture_key;
|
textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key);
|
||||||
}
|
|
||||||
|
|
||||||
Rect const srcrect = cached_srcrect;
|
|
||||||
|
|
||||||
Vec2 uv0, uv1, uv2, uv3;
|
Vec2 uv0, uv1, uv2, uv3;
|
||||||
|
|
||||||
if (!batch.repeat) {
|
if (!sprite.repeat) {
|
||||||
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;
|
||||||
const float xr = srcrect.x / dims.w;
|
const float xr = srcrect.x / dims.w;
|
||||||
@ -216,7 +213,7 @@ void render_sprite_batch(const Primitive2D primitives[],
|
|||||||
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
const Vec2 c = rect_center(sprite.rect);
|
const Vec2 c = frect_center(sprite.rect);
|
||||||
const Vec2 t = fast_cossine(sprite.rotation + (float)M_PI_4);
|
const Vec2 t = fast_cossine(sprite.rotation + (float)M_PI_4);
|
||||||
const Vec2 d = {
|
const Vec2 d = {
|
||||||
.x = t.x * sprite.rect.w * (float)M_SQRT1_2,
|
.x = t.x * sprite.rect.w * (float)M_SQRT1_2,
|
||||||
@ -231,7 +228,7 @@ void render_sprite_batch(const Primitive2D primitives[],
|
|||||||
} else {
|
} else {
|
||||||
/* rotated non-square case*/
|
/* rotated non-square case*/
|
||||||
|
|
||||||
const Vec2 c = rect_center(sprite.rect);
|
const Vec2 c = frect_center(sprite.rect);
|
||||||
const Vec2 t = fast_cossine(sprite.rotation);
|
const Vec2 t = fast_cossine(sprite.rotation);
|
||||||
|
|
||||||
const Vec2 h = { sprite.rect.w / 2, sprite.rect.h / 2 };
|
const Vec2 h = { sprite.rect.w / 2, sprite.rect.h / 2 };
|
||||||
|
@ -171,7 +171,9 @@ static void text_destroy_font_data(FontData *font_data) {
|
|||||||
|
|
||||||
|
|
||||||
static void text_draw_with(FontData* font_data, char* text, Vec2 position, Color color) {
|
static void text_draw_with(FontData* font_data, char* text, Vec2 position, Color color) {
|
||||||
VertexBuffer const vertex_array = get_scratch_vertex_array();
|
VertexBuffer vertex_array = 0;
|
||||||
|
if (vertex_array == 0)
|
||||||
|
vertex_array = create_vertex_buffer();
|
||||||
|
|
||||||
const size_t len = SDL_strlen(text);
|
const size_t len = SDL_strlen(text);
|
||||||
|
|
||||||
@ -275,8 +277,8 @@ void text_cache_reset_arena(TextCache *cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void draw_text(const char *string, Vec2 position, float height, Color color, const char *font) {
|
void draw_text(const char *string, Vec2 position, int height_px, Color color, const char *font_path) {
|
||||||
ensure_font_cache(font, (int)height);
|
ensure_font_cache(font_path, height_px);
|
||||||
|
|
||||||
/* the original string might not be around by the time it's used, so copy it */
|
/* the original string might not be around by the time it's used, so copy it */
|
||||||
size_t str_length = SDL_strlen(string) + 1;
|
size_t str_length = SDL_strlen(string) + 1;
|
||||||
@ -290,8 +292,8 @@ void draw_text(const char *string, Vec2 position, float height, Color color, con
|
|||||||
.color = color,
|
.color = color,
|
||||||
.position = position,
|
.position = position,
|
||||||
.text = dup_string,
|
.text = dup_string,
|
||||||
.font = font,
|
.font = font_path,
|
||||||
.height_px = (int)height,
|
.height_px = height_px,
|
||||||
};
|
};
|
||||||
|
|
||||||
Primitive2D primitive = {
|
Primitive2D primitive = {
|
||||||
@ -303,9 +305,9 @@ void draw_text(const char *string, Vec2 position, float height, Color color, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float draw_text_width(const char *string, float height, const char *font) {
|
int draw_text_width(const char *string, int height_px, const char *font_path) {
|
||||||
ensure_font_cache(font, (int)height);
|
ensure_font_cache(font_path, height_px);
|
||||||
FontData *font_data = get_font_data(font, (int)height);
|
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) {
|
||||||
@ -316,5 +318,5 @@ float draw_text_width(const char *string, float height, const char *font) {
|
|||||||
length += advance_width;
|
length += advance_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (float)length * font_data->scale_factor;
|
return (int)((float)length * font_data->scale_factor);
|
||||||
}
|
}
|
||||||
|
@ -23,19 +23,19 @@ void draw_triangle(const char *path,
|
|||||||
if (!batch_p) {
|
if (!batch_p) {
|
||||||
struct MeshBatch 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];
|
batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */
|
||||||
}
|
}
|
||||||
|
|
||||||
UncoloredSpaceTriangle const triangle = {
|
union UncoloredSpaceTriangle triangle = { .primitive = {
|
||||||
.v0 = v0,
|
.v0 = v0,
|
||||||
.v1 = v1,
|
.v1 = v1,
|
||||||
.v2 = v2,
|
.v2 = v2,
|
||||||
.uv1 = uv1,
|
.uv1 = uv1,
|
||||||
.uv0 = uv0,
|
.uv0 = uv0,
|
||||||
.uv2 = uv2,
|
.uv2 = uv2,
|
||||||
};
|
}};
|
||||||
|
|
||||||
UncoloredSpaceTriangle *triangles = (UncoloredSpaceTriangle *)(void *)batch_p->value.primitives;
|
union UncoloredSpaceTriangle *triangles = (union UncoloredSpaceTriangle *)(void *)batch_p->value.primitives;
|
||||||
|
|
||||||
arrpush(triangles, triangle);
|
arrpush(triangles, triangle);
|
||||||
batch_p->value.primitives = (uint8_t *)triangles;
|
batch_p->value.primitives = (uint8_t *)triangles;
|
||||||
@ -45,7 +45,38 @@ void draw_triangle(const char *path,
|
|||||||
void draw_uncolored_space_traingle_batch(struct MeshBatch *batch,
|
void draw_uncolored_space_traingle_batch(struct MeshBatch *batch,
|
||||||
TextureKey texture_key)
|
TextureKey texture_key)
|
||||||
{
|
{
|
||||||
VertexBuffer const vertex_array = get_scratch_vertex_array();
|
static VertexBuffer vertex_array = 0;
|
||||||
|
if (vertex_array == 0)
|
||||||
|
vertex_array = create_vertex_buffer();
|
||||||
|
|
||||||
|
const size_t primitives_len = arrlenu(batch->primitives);
|
||||||
|
|
||||||
|
/* nothing to do */
|
||||||
|
if (primitives_len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key);
|
||||||
|
const Rect dims = textures_get_dims(&ctx.texture_cache, texture_key);
|
||||||
|
|
||||||
|
const float wr = srcrect.w / dims.w;
|
||||||
|
const float hr = srcrect.h / dims.h;
|
||||||
|
const float xr = srcrect.x / dims.w;
|
||||||
|
const float yr = srcrect.y / dims.h;
|
||||||
|
|
||||||
|
/* update pixel-based uvs to correspond with texture atlases */
|
||||||
|
for (size_t i = 0; i < primitives_len; ++i) {
|
||||||
|
struct UncoloredSpaceTrianglePayload *payload =
|
||||||
|
&((union UncoloredSpaceTriangle *)(void *)batch->primitives)[i].payload;
|
||||||
|
|
||||||
|
payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr;
|
||||||
|
payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr;
|
||||||
|
payload->uv1.x = xr + ((float)payload->uv1.x / srcrect.w) * wr;
|
||||||
|
payload->uv1.y = yr + ((float)payload->uv1.y / srcrect.h) * hr;
|
||||||
|
payload->uv2.x = xr + ((float)payload->uv2.x / srcrect.w) * wr;
|
||||||
|
payload->uv2.y = yr + ((float)payload->uv2.y / srcrect.h) * hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_util.h"
|
#include "twn_util.h"
|
||||||
#include "twn_util_c.h"
|
#include "twn_util_c.h"
|
||||||
#include "twn_audio.h"
|
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <stb_ds.h>
|
#include <stb_ds.h>
|
||||||
@ -191,32 +190,42 @@ void audio_play(const char *path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TWN_API void audio_parameter(const char *channel, const char *param, float value) {
|
TWN_API void audio_set(const char *channel, AudioParam param, float value) {
|
||||||
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
|
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
|
||||||
if (!pair) {
|
if (!pair) {
|
||||||
log_warn("No channel by the name of %s to set a parameter for", channel);
|
log_warn("No channel by the name of %s to set a parameter for", channel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_strcmp(param, "repeat") == 0) {
|
switch (param) {
|
||||||
|
case AUDIO_PARAM_REPEAT:
|
||||||
pair->value.repeat = (bool)value;
|
pair->value.repeat = (bool)value;
|
||||||
|
break;
|
||||||
} else if (SDL_strcmp(param, "volume") == 0) {
|
case AUDIO_PARAM_VOLUME:
|
||||||
if (value > 1.0f || value < 0.0f) {
|
if (value > 1.0f) {
|
||||||
log_warn("Out of range volume for channel %s set", channel);
|
log_warn("Out of range volume for channel %s set", channel);
|
||||||
value = clampf(value, 0.0f, 1.0f);
|
value = 1.0f;
|
||||||
|
}
|
||||||
|
if (value < 0.0f) {
|
||||||
|
log_warn("Out of range volume for channel %s set", channel);
|
||||||
|
value = 0.0f;
|
||||||
}
|
}
|
||||||
pair->value.volume = value;
|
pair->value.volume = value;
|
||||||
|
break;
|
||||||
} else if (SDL_strcmp(param, "panning") == 0) {
|
case AUDIO_PARAM_PANNING:
|
||||||
if (value > 1.0f || value < -1.0f) {
|
if (value > 1.0f) {
|
||||||
log_warn("Out of range panning for channel %s set", channel);
|
log_warn("Out of range panning for channel %s set", channel);
|
||||||
value = clampf(value, -1.0f, +1.0f);
|
value = 1.0f;
|
||||||
|
}
|
||||||
|
if (value < -1.0f) {
|
||||||
|
log_warn("Out of range panning for channel %s set", channel);
|
||||||
|
value = -1.0f;
|
||||||
}
|
}
|
||||||
pair->value.panning = value;
|
pair->value.panning = value;
|
||||||
|
break;
|
||||||
} else
|
default:
|
||||||
CRY("Audio channel parameter setting failed", "Invalid parameter enum given");
|
CRY("Audio channel parameter setting failed", "Invalid parameter enum given");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef TWN_AUDIO_C_H
|
#ifndef TWN_AUDIO_C_H
|
||||||
#define TWN_AUDIO_C_H
|
#define TWN_AUDIO_C_H
|
||||||
|
|
||||||
|
#include "twn_audio.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_audio.h>
|
#include <SDL2/SDL_audio.h>
|
||||||
|
|
||||||
#define STB_VORBIS_HEADER_ONLY
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "twn_camera_c.h"
|
#include "twn_camera.h"
|
||||||
#include "twn_vec.h"
|
#include "twn_vec.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ typedef struct EngineContext {
|
|||||||
/* where the app was run from, used as the root for packs */
|
/* where the app was run from, used as the root for packs */
|
||||||
char *base_dir;
|
char *base_dir;
|
||||||
|
|
||||||
Vec2 window_dims;
|
Vec2i window_dims;
|
||||||
|
|
||||||
/* configuration */
|
/* configuration */
|
||||||
toml_table_t *config_table;
|
toml_table_t *config_table;
|
||||||
@ -64,20 +64,19 @@ typedef struct EngineContext {
|
|||||||
int64_t delta_averager_residual;
|
int64_t delta_averager_residual;
|
||||||
int64_t time_averager[4];
|
int64_t time_averager[4];
|
||||||
|
|
||||||
SDL_GLContext *gl_context;
|
|
||||||
SDL_Window *window;
|
|
||||||
uint32_t window_id;
|
|
||||||
|
|
||||||
/* this should be a multiple of the current ticks per second */
|
/* this should be a multiple of the current ticks per second */
|
||||||
/* use it to simulate low framerate (e.g. at 60 tps, set to 2 for 30 fps) */
|
/* use it to simulate low framerate (e.g. at 60 tps, set to 2 for 30 fps) */
|
||||||
/* it can be changed at runtime; any resulting logic anomalies are bugs */
|
/* it can be changed at runtime; any resulting logic anomalies are bugs */
|
||||||
uint32_t update_multiplicity;
|
uint32_t update_multiplicity;
|
||||||
|
|
||||||
|
SDL_GLContext *gl_context;
|
||||||
|
SDL_Window *window;
|
||||||
|
uint32_t window_id;
|
||||||
|
|
||||||
bool is_running;
|
bool is_running;
|
||||||
bool window_size_has_changed;
|
bool window_size_has_changed;
|
||||||
bool resync_flag;
|
bool resync_flag;
|
||||||
bool was_successful;
|
bool was_successful;
|
||||||
bool render_double_buffered;
|
|
||||||
} EngineContext;
|
} EngineContext;
|
||||||
|
|
||||||
/* TODO: does it need to be marked with TWN_API? */
|
/* TODO: does it need to be marked with TWN_API? */
|
||||||
|
151
src/twn_input.c
151
src/twn_input.c
@ -1,14 +1,13 @@
|
|||||||
#include "twn_input_c.h"
|
#include "twn_input_c.h"
|
||||||
#include "twn_util.h"
|
#include "twn_util.h"
|
||||||
#include "twn_util_c.h"
|
|
||||||
#include "twn_control.h"
|
#include "twn_control.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_input.h"
|
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <stb_ds.h>
|
#include <stb_ds.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
static void update_action_pressed_state(InputState *input, Action *action) {
|
static void update_action_pressed_state(InputState *input, Action *action) {
|
||||||
@ -70,40 +69,20 @@ static void update_action_pressed_state(InputState *input, Action *action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ActionHashItem *input_add_action(char const *action_name) {
|
|
||||||
SDL_assert(action_name);
|
|
||||||
|
|
||||||
Action new_action = { 0 };
|
|
||||||
new_action.bindings = SDL_calloc(ctx.keybind_slots, sizeof *new_action.bindings);
|
|
||||||
shput(ctx.input.action_hash, action_name, new_action);
|
|
||||||
return shgetp(ctx.input.action_hash, action_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void input_delete_action(char const *action_name) {
|
|
||||||
SDL_assert(action_name);
|
|
||||||
|
|
||||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
|
||||||
SDL_assert(action);
|
|
||||||
|
|
||||||
SDL_free(action->value.bindings);
|
|
||||||
shdel(ctx.input.action_hash, action_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void input_bind_code_to_action(InputState *input,
|
static void input_bind_code_to_action(InputState *input,
|
||||||
char const *action_name,
|
char const *action_name,
|
||||||
ButtonSource source,
|
ButtonSource source,
|
||||||
union ButtonCode code)
|
union ButtonCode code)
|
||||||
{
|
{
|
||||||
ActionHashItem *action_item = shgetp_null(input->action_hash, action_name);
|
ActionHashItem *action_item = shgetp_null(input->action_hash, action_name);
|
||||||
if (!action_item)
|
if (action_item == NULL) {
|
||||||
action_item = input_add_action(action_name);
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
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 < action->num_bindings; ++i) {
|
for (size_t i = 0; i < (uint64_t)ctx.keybind_slots; ++i) {
|
||||||
Button *binding = &action->bindings[i];
|
Button *binding = &action->bindings[i];
|
||||||
|
|
||||||
if (binding->source != source)
|
if (binding->source != source)
|
||||||
@ -130,8 +109,7 @@ static void input_bind_code_to_action(InputState *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_already_bound) {
|
if (is_already_bound) {
|
||||||
/* keep it alive */
|
log_warn("(%s) Code already bound to action \"%s\".", __func__, action_name);
|
||||||
binding->in_use = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,14 +117,13 @@ static void input_bind_code_to_action(InputState *input,
|
|||||||
/* if we're at max bindings, forget the first element and shift the rest */
|
/* if we're at max bindings, forget the first element and shift the rest */
|
||||||
if (action->num_bindings == (uint64_t)ctx.keybind_slots) {
|
if (action->num_bindings == (uint64_t)ctx.keybind_slots) {
|
||||||
--action->num_bindings;
|
--action->num_bindings;
|
||||||
size_t shifted_size = sizeof action->bindings[0] * (ctx.keybind_slots - 1);
|
size_t shifted_size = (sizeof action->bindings) - (sizeof action->bindings[0]);
|
||||||
SDL_memmove(action->bindings, action->bindings + 1, shifted_size);
|
SDL_memmove(action->bindings, action->bindings + 1, shifted_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
action->bindings[action->num_bindings++] = (Button) {
|
action->bindings[action->num_bindings++] = (Button) {
|
||||||
.source = source,
|
.source = source,
|
||||||
.code = code,
|
.code = code,
|
||||||
.in_use = true,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,15 +134,16 @@ static void input_unbind_code_from_action(InputState *input,
|
|||||||
union ButtonCode code)
|
union ButtonCode code)
|
||||||
{
|
{
|
||||||
ActionHashItem *action_item = shgetp_null(input->action_hash, action_name);
|
ActionHashItem *action_item = shgetp_null(input->action_hash, action_name);
|
||||||
if (!action_item)
|
if (action_item == NULL) {
|
||||||
action_item = input_add_action(action_name);
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
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 < action->num_bindings; ++index) {
|
for (index = 0; index < (uint64_t)ctx.keybind_slots; ++index) {
|
||||||
Button *binding = &action->bindings[index];
|
Button *binding = &action->bindings[index];
|
||||||
|
|
||||||
if (binding->source != source)
|
if (binding->source != source)
|
||||||
@ -195,8 +173,10 @@ static void input_unbind_code_from_action(InputState *input,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_bound)
|
if (!is_bound) {
|
||||||
|
log_warn("(%s) Code is not bound to action \"%s\".", __func__, action_name);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* remove the element to unbind and shift the rest so there isn't a gap */
|
/* remove the element to unbind and shift the rest so there isn't a gap */
|
||||||
size_t elements_after_index = action->num_bindings - index;
|
size_t elements_after_index = action->num_bindings - index;
|
||||||
@ -217,45 +197,24 @@ void input_state_deinit(InputState *input) {
|
|||||||
|
|
||||||
|
|
||||||
void input_state_update(InputState *input) {
|
void input_state_update(InputState *input) {
|
||||||
/* TODO: don't spam it if it happens */
|
|
||||||
if (SDL_SetRelativeMouseMode(input->mouse_captured) != 0)
|
|
||||||
log_warn("(%s) Mouse capture isn't supported.", __func__);
|
|
||||||
|
|
||||||
int x, y;
|
|
||||||
|
|
||||||
input->keyboard_state = SDL_GetKeyboardState(NULL);
|
input->keyboard_state = SDL_GetKeyboardState(NULL);
|
||||||
input->mouse_state = SDL_GetMouseState(&x, &y);
|
input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x,
|
||||||
input->mouse_window_position = (Vec2){ (float)x, (float)y };
|
&input->mouse_window_position.y);
|
||||||
|
|
||||||
SDL_GetRelativeMouseState(&x, &y);
|
SDL_GetRelativeMouseState(&input->mouse_relative_position.x,
|
||||||
input->mouse_relative_position = (Vec2){ (float)x, (float)y };
|
&input->mouse_relative_position.y);
|
||||||
|
|
||||||
ctx.game.mouse_position = input->mouse_window_position;
|
ctx.game.mouse_position = input->mouse_window_position;
|
||||||
ctx.game.mouse_movement = input->mouse_relative_position;
|
ctx.game.mouse_movement = input->mouse_relative_position;
|
||||||
|
|
||||||
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
||||||
Action *action = &input->action_hash[i].value;
|
Action *action = &input->action_hash[i].value;
|
||||||
/* collect unused */
|
|
||||||
for (size_t u = 0; u < action->num_bindings; ++u) {
|
|
||||||
Button *button = &action->bindings[u];
|
|
||||||
if (!button->in_use)
|
|
||||||
input_unbind_code_from_action(input, input->action_hash[i].key, button->source, button->code);
|
|
||||||
else
|
|
||||||
button->in_use = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_action_pressed_state(input, action);
|
update_action_pressed_state(input, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t removed = 0;
|
|
||||||
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
|
||||||
if (input->action_hash[i - removed].value.num_bindings == 0)
|
|
||||||
input_delete_action(input->action_hash[i - removed].key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void input_action(char const *action_name,
|
void input_bind_action_control(char const *action_name,
|
||||||
Control control)
|
Control control)
|
||||||
{
|
{
|
||||||
SDL_assert_always(action_name);
|
SDL_assert_always(action_name);
|
||||||
@ -277,7 +236,57 @@ void input_action(char const *action_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool input_action_pressed(char const *action_name) {
|
void input_unbind_action_control(char const *action_name,
|
||||||
|
Control control)
|
||||||
|
{
|
||||||
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
|
if (CONTROL_SCANCODE_START <= control && control < CONTROL_SCANCODE_LIMIT)
|
||||||
|
input_unbind_code_from_action(&ctx.input,
|
||||||
|
action_name,
|
||||||
|
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
||||||
|
(union ButtonCode) { .scancode = (SDL_Scancode)control });
|
||||||
|
|
||||||
|
else if (CONTROL_MOUSECODE_START <= control && control < CONTROL_MOUSECODE_LIMIT) {
|
||||||
|
uint8_t const mouse_button = (uint8_t)(control - CONTROL_MOUSECODE_START);
|
||||||
|
input_unbind_code_from_action(&ctx.input,
|
||||||
|
action_name,
|
||||||
|
BUTTON_SOURCE_MOUSE,
|
||||||
|
(union ButtonCode) { .mouse_button = (uint8_t)SDL_BUTTON(mouse_button)});
|
||||||
|
} else
|
||||||
|
log_warn("(%s) Invalid control value given: %i.", __func__, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void input_add_action(char const *action_name) {
|
||||||
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
|
if (shgeti(ctx.input.action_hash, action_name) >= 0) {
|
||||||
|
log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action new_action = { 0 };
|
||||||
|
new_action.bindings = ccalloc(ctx.keybind_slots, sizeof *new_action.bindings);
|
||||||
|
shput(ctx.input.action_hash, action_name, new_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void input_delete_action(char const *action_name) {
|
||||||
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||||
|
if (action == NULL) {
|
||||||
|
log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_free(action->value.bindings);
|
||||||
|
shdel(ctx.input.action_hash, action_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool input_is_action_pressed(char const *action_name) {
|
||||||
SDL_assert_always(action_name);
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||||
@ -289,7 +298,7 @@ bool input_action_pressed(char const *action_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool input_action_just_pressed(char const *action_name) {
|
bool input_is_action_just_pressed(char const *action_name) {
|
||||||
SDL_assert_always(action_name);
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||||
@ -301,7 +310,7 @@ bool input_action_just_pressed(char const *action_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool input_action_just_released(char const *action_name) {
|
bool input_is_action_just_released(char const *action_name) {
|
||||||
SDL_assert_always(action_name);
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||||
@ -313,7 +322,7 @@ bool input_action_just_released(char const *action_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Vec2 input_action_position(char const *action_name) {
|
Vec2 input_get_action_position(char const *action_name) {
|
||||||
SDL_assert_always(action_name);
|
SDL_assert_always(action_name);
|
||||||
|
|
||||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||||
@ -326,8 +335,14 @@ Vec2 input_action_position(char const *action_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void input_mouse_captured(bool enabled) {
|
void input_set_mouse_captured(bool enabled) {
|
||||||
ctx.input.mouse_captured = enabled;
|
if (SDL_SetRelativeMouseMode(enabled) != 0)
|
||||||
|
log_warn("(%s) Mouse capture isn't supported.", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool input_is_mouse_captured(void) {
|
||||||
|
return SDL_GetRelativeMouseMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
#ifndef TWN_INPUT_C_H
|
#ifndef TWN_INPUT_C_H
|
||||||
#define TWN_INPUT_C_H
|
#define TWN_INPUT_C_H
|
||||||
|
|
||||||
#include "twn_types.h"
|
#include "twn_input.h"
|
||||||
|
#include "twn_vec.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define KEYBIND_SLOTS_DEFAULT 3
|
#define KEYBIND_SLOTS_DEFAULT 3
|
||||||
|
|
||||||
@ -33,7 +32,6 @@ typedef enum ButtonSource {
|
|||||||
typedef struct Button {
|
typedef struct Button {
|
||||||
enum ButtonSource source;
|
enum ButtonSource source;
|
||||||
union ButtonCode code;
|
union ButtonCode code;
|
||||||
bool in_use;
|
|
||||||
} Button;
|
} Button;
|
||||||
|
|
||||||
|
|
||||||
@ -61,8 +59,8 @@ typedef struct ActionHashItem {
|
|||||||
typedef struct InputState {
|
typedef struct InputState {
|
||||||
const uint8_t *keyboard_state; /* array of booleans indexed by scancode */
|
const uint8_t *keyboard_state; /* array of booleans indexed by scancode */
|
||||||
ActionHashItem *action_hash;
|
ActionHashItem *action_hash;
|
||||||
Vec2 mouse_window_position;
|
Vec2i mouse_window_position;
|
||||||
Vec2 mouse_relative_position;
|
Vec2i mouse_relative_position;
|
||||||
uint32_t mouse_state; /* SDL mouse button bitmask */
|
uint32_t mouse_state; /* SDL mouse button bitmask */
|
||||||
ButtonSource last_active_source;
|
ButtonSource last_active_source;
|
||||||
bool is_anything_just_pressed;
|
bool is_anything_just_pressed;
|
||||||
|
@ -37,8 +37,8 @@ static int event_callback(void *userdata, SDL_Event *event) {
|
|||||||
|
|
||||||
switch (event->window.event) {
|
switch (event->window.event) {
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
ctx.window_dims.x = (float)event->window.data1;
|
ctx.window_dims.x = event->window.data1;
|
||||||
ctx.window_dims.y = (float)event->window.data2;
|
ctx.window_dims.y = event->window.data2;
|
||||||
ctx.resync_flag = true;
|
ctx.resync_flag = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -204,8 +204,8 @@ static void main_loop(void) {
|
|||||||
/* TODO: disable rendering pushes on not-last ? */
|
/* TODO: disable rendering pushes on not-last ? */
|
||||||
render_queue_clear();
|
render_queue_clear();
|
||||||
poll_events();
|
poll_events();
|
||||||
game_object_tick();
|
|
||||||
input_state_update(&ctx.input);
|
input_state_update(&ctx.input);
|
||||||
|
game_object_tick();
|
||||||
preserve_persistent_ctx_fields();
|
preserve_persistent_ctx_fields();
|
||||||
|
|
||||||
ctx.frame_accumulator -= ctx.desired_frametime;
|
ctx.frame_accumulator -= ctx.desired_frametime;
|
||||||
@ -444,7 +444,7 @@ static bool initialize(void) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
ctx.base_render_width = datum_base_render_width.u.i;
|
ctx.base_render_width = datum_base_render_width.u.i;
|
||||||
ctx.game.resolution.x = (float)ctx.base_render_width;
|
ctx.game.resolution.x = (int)ctx.base_render_width;
|
||||||
|
|
||||||
toml_datum_t datum_base_render_height = toml_int_in(game, "base_render_height");
|
toml_datum_t datum_base_render_height = toml_int_in(game, "base_render_height");
|
||||||
if (!datum_base_render_height.ok) {
|
if (!datum_base_render_height.ok) {
|
||||||
@ -452,7 +452,7 @@ static bool initialize(void) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
ctx.base_render_height = datum_base_render_height.u.i;
|
ctx.base_render_height = datum_base_render_height.u.i;
|
||||||
ctx.game.resolution.y = (float)ctx.base_render_height;
|
ctx.game.resolution.y = (int)ctx.base_render_height;
|
||||||
|
|
||||||
ctx.window = SDL_CreateWindow(datum_title.u.s,
|
ctx.window = SDL_CreateWindow(datum_title.u.s,
|
||||||
SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED,
|
||||||
@ -508,8 +508,8 @@ static bool initialize(void) {
|
|||||||
|
|
||||||
/* TODO: */
|
/* TODO: */
|
||||||
// SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
|
// SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
|
||||||
ctx.window_dims.x = (float)ctx.base_render_width;
|
ctx.window_dims.x = (int)ctx.base_render_width;
|
||||||
ctx.window_dims.y = (float)ctx.base_render_height;
|
ctx.window_dims.y = (int)ctx.base_render_height;
|
||||||
|
|
||||||
/* add a watcher for immediate updates on window size */
|
/* add a watcher for immediate updates on window size */
|
||||||
SDL_AddEventWatch(event_callback, NULL);
|
SDL_AddEventWatch(event_callback, NULL);
|
||||||
@ -658,8 +658,6 @@ static bool initialize(void) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ctx.render_double_buffered = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -788,8 +786,6 @@ int enter_loop(int argc, char **argv) {
|
|||||||
main_loop();
|
main_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
profile_list_stats();
|
|
||||||
|
|
||||||
/* loop is over */
|
/* loop is over */
|
||||||
game_object_unload();
|
game_object_unload();
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef TWN_TEXTURES_C_H
|
#ifndef TWN_TEXTURES_C_H
|
||||||
#define TWN_TEXTURES_C_H
|
#define TWN_TEXTURES_C_H
|
||||||
|
|
||||||
#include "twn_types.h"
|
#include "twn_util.h"
|
||||||
#include "twn_texture_modes.h"
|
#include "twn_texture_modes.h"
|
||||||
#include "rendering/twn_gpu_texture_c.h"
|
#include "rendering/twn_gpu_texture_c.h"
|
||||||
|
|
||||||
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
|
|
||||||
typedef struct Texture {
|
typedef struct Texture {
|
||||||
Rect 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;
|
||||||
GPUTexture 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 */
|
||||||
GPUTexture repeating_texture; /* separately allocated Texture, for loners == loner_texture */
|
GPUTexture repeating_texture; /* separately allocated Texture, for loners == loner_texture */
|
||||||
@ -50,7 +50,6 @@ typedef struct TextureCache {
|
|||||||
typedef struct TextureKey { uint16_t id; } TextureKey;
|
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 TEXTURE_KEY_INVALID (TextureKey) { (uint16_t)-1 }
|
|
||||||
#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 TextureCache *cache, SDL_Window *window);
|
void textures_cache_init(struct TextureCache *cache, SDL_Window *window);
|
||||||
|
153
src/twn_util.c
153
src/twn_util.c
@ -9,16 +9,6 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
static struct ProfileItem {
|
|
||||||
char const *key;
|
|
||||||
struct Profile {
|
|
||||||
uint64_t tick_start;
|
|
||||||
uint64_t tick_accum;
|
|
||||||
uint64_t sample_count;
|
|
||||||
} value;
|
|
||||||
} *profiles;
|
|
||||||
|
|
||||||
|
|
||||||
void cry_impl(const char *file, const int line, const char *title, const char *text) {
|
void cry_impl(const char *file, const int line, const char *title, const char *text) {
|
||||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"TEARS AT %s:%d: %s ... %s", file, line, title, text);
|
"TEARS AT %s:%d: %s ... %s", file, line, title, text);
|
||||||
@ -207,27 +197,59 @@ bool strends(const char *str, const char *suffix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* TODO: have our own */
|
bool overlap_rect(const Recti *a, const Recti *b, Recti *result) {
|
||||||
Rect rect_overlap(const Rect a, const Rect b) {
|
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
|
||||||
SDL_FRect a_sdl = { a.x, a.y, a.w, a.h };
|
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
|
||||||
SDL_FRect b_sdl = { b.x, b.y, b.w, b.h };
|
SDL_Rect result_sdl = { 0 };
|
||||||
SDL_FRect result_sdl = { 0 };
|
|
||||||
|
|
||||||
(void)SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
|
bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl);
|
||||||
|
|
||||||
return (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
if (result != NULL)
|
||||||
|
*result = (Recti){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
||||||
|
|
||||||
|
return intersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* TODO: have our own */
|
bool overlap_frect(const Rect *a, const Rect *b, Rect *result) {
|
||||||
bool rect_intersects(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 };
|
SDL_FRect result_sdl = { 0 };
|
||||||
|
|
||||||
|
bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
|
||||||
|
|
||||||
|
if (result != NULL)
|
||||||
|
*result = (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
||||||
|
|
||||||
|
return intersection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool intersect_rect(const Recti *a, const Recti *b) {
|
||||||
|
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
|
||||||
|
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
|
||||||
|
return SDL_HasIntersection(&a_sdl, &b_sdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool intersect_frect(const Rect *a, const Rect *b) {
|
||||||
|
SDL_FRect a_sdl = { a->x, a->y, a->w, a->h };
|
||||||
|
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
|
||||||
return SDL_HasIntersectionF(&a_sdl, &b_sdl);
|
return SDL_HasIntersectionF(&a_sdl, &b_sdl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Vec2 rect_center(Rect rect) {
|
Rect to_frect(Recti rect) {
|
||||||
|
return (Rect) {
|
||||||
|
.h = (float)rect.h,
|
||||||
|
.w = (float)rect.w,
|
||||||
|
.x = (float)rect.x,
|
||||||
|
.y = (float)rect.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vec2 frect_center(Rect rect) {
|
||||||
return (Vec2){
|
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,
|
||||||
@ -235,52 +257,25 @@ Vec2 rect_center(Rect rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32_t timer_tick_frames(int32_t frames_left) {
|
void tick_timer(int *value) {
|
||||||
SDL_assert(frames_left >= 0);
|
*value = MAX(*value - 1, 0);
|
||||||
return MAX(frames_left - 1, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float timer_tick_seconds(float seconds_left) {
|
void tick_ftimer(float *value) {
|
||||||
SDL_assert(seconds_left >= 0);
|
*value = MAX(*value - ((float)ctx.delta_time / (float)ctx.clocks_per_second), 0.0f);
|
||||||
return MAX(seconds_left - ((float)ctx.delta_time / (float)ctx.clocks_per_second), 0.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TimerElapseFramesResult timer_elapse_frames(int32_t frames_left, int32_t interval) {
|
bool repeat_ftimer(float *value, float at) {
|
||||||
SDL_assert(frames_left >= 0);
|
*value -= (float)ctx.delta_time / (float)ctx.clocks_per_second;
|
||||||
SDL_assert(interval > 0);
|
if (*value < 0.0f) {
|
||||||
|
*value += at;
|
||||||
frames_left -= 1;
|
return true;
|
||||||
bool elapsed = false;
|
|
||||||
if (frames_left <= 0) {
|
|
||||||
elapsed = true;
|
|
||||||
frames_left += interval;
|
|
||||||
}
|
}
|
||||||
return (TimerElapseFramesResult) {
|
return false;
|
||||||
.elapsed = elapsed,
|
|
||||||
.frames_left = frames_left
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TimerElapseSecondsResult timer_elapse_seconds(float seconds_left, float interval) {
|
|
||||||
SDL_assert(seconds_left >= 0);
|
|
||||||
SDL_assert(interval > 0);
|
|
||||||
|
|
||||||
seconds_left -= (float)ctx.delta_time / (float)ctx.clocks_per_second;
|
|
||||||
bool elapsed = false;
|
|
||||||
if (seconds_left <= 0.0f) {
|
|
||||||
elapsed = true;
|
|
||||||
seconds_left += interval;
|
|
||||||
}
|
|
||||||
return (TimerElapseSecondsResult) {
|
|
||||||
.elapsed = elapsed,
|
|
||||||
.seconds_left = seconds_left
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* TODO: handle utf8 */
|
/* TODO: handle utf8 */
|
||||||
char *expand_asterisk(const char *mask, const char *to) {
|
char *expand_asterisk(const char *mask, const char *to) {
|
||||||
const char *offset = SDL_strchr(mask, '*');
|
const char *offset = SDL_strchr(mask, '*');
|
||||||
@ -297,43 +292,3 @@ char *expand_asterisk(const char *mask, const char *to) {
|
|||||||
SDL_memcpy(str + (offset - mask) + to_len, offset + 1, mask_len - (offset - mask));
|
SDL_memcpy(str + (offset - mask) + to_len, offset + 1, mask_len - (offset - mask));
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void profile_start(char profile[const static 1]) {
|
|
||||||
uint64_t tick_accum = 0, sample_count = 0;
|
|
||||||
|
|
||||||
struct ProfileItem const *p = shgetp_null(profiles, profile);
|
|
||||||
if (p) {
|
|
||||||
tick_accum = p->value.tick_accum;
|
|
||||||
sample_count = p->value.sample_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
shput(profiles, profile, ((struct Profile) {
|
|
||||||
.tick_start = SDL_GetPerformanceCounter(),
|
|
||||||
.tick_accum = tick_accum,
|
|
||||||
.sample_count = sample_count,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void profile_end(char profile[const static 1]) {
|
|
||||||
struct ProfileItem *p = shgetp_null(profiles, profile);
|
|
||||||
if (!p) {
|
|
||||||
log_warn("profile %s wasn't started!", profile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->value.sample_count++;
|
|
||||||
p->value.tick_accum += SDL_GetPerformanceCounter() - p->value.tick_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void profile_list_stats(void) {
|
|
||||||
for (size_t i = 0; i < shlenu(profiles); ++i) {
|
|
||||||
log_info("profile '%s' on average took: %f seconds",
|
|
||||||
profiles[i].key,
|
|
||||||
(double)profiles[i].value.tick_accum /
|
|
||||||
(double)profiles[i].value.sample_count /
|
|
||||||
(double)(SDL_GetPerformanceFrequency()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MAX SDL_max
|
#define MAX SDL_max
|
||||||
#define MIN SDL_min
|
#define MIN SDL_min
|
||||||
@ -23,10 +23,6 @@ _Noreturn void die_abruptly(void);
|
|||||||
/* note: you must free the returned string */
|
/* note: you must free the returned string */
|
||||||
char *expand_asterisk(const char *mask, const char *to);
|
char *expand_asterisk(const char *mask, const char *to);
|
||||||
|
|
||||||
void profile_start(char profile[const static 1]);
|
|
||||||
void profile_end(char profile[const static 1]);
|
|
||||||
void profile_list_stats(void);
|
|
||||||
|
|
||||||
/* http://www.azillionmonkeys.com/qed/sqroot.html */
|
/* http://www.azillionmonkeys.com/qed/sqroot.html */
|
||||||
static inline float fast_sqrt(float x)
|
static inline float fast_sqrt(float x)
|
||||||
{
|
{
|
||||||
@ -43,7 +39,7 @@ static inline float fast_sqrt(float x)
|
|||||||
|
|
||||||
|
|
||||||
static inline Vec2 fast_cossine(float a) {
|
static inline Vec2 fast_cossine(float a) {
|
||||||
const float s = sinf(a);
|
const float s = SDL_sinf(a);
|
||||||
return (Vec2){
|
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
|
||||||
|
2
third-party/libxm/CMakeLists.txt
vendored
2
third-party/libxm/CMakeLists.txt
vendored
@ -1,4 +1,4 @@
|
|||||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
|
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||||
PROJECT(libxm C)
|
PROJECT(libxm C)
|
||||||
|
|
||||||
FUNCTION(OPTION_AND_DEFINE name description default_value)
|
FUNCTION(OPTION_AND_DEFINE name description default_value)
|
||||||
|
2
third-party/physfs/CMakeLists.txt
vendored
2
third-party/physfs/CMakeLists.txt
vendored
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
set(PHYSFS_VERSION 3.2.0)
|
set(PHYSFS_VERSION 3.2.0)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
project(PhysicsFS VERSION ${PHYSFS_VERSION} LANGUAGES C )
|
project(PhysicsFS VERSION ${PHYSFS_VERSION} LANGUAGES C )
|
||||||
|
|
||||||
|
2
third-party/stb/stb_ds.h
vendored
2
third-party/stb/stb_ds.h
vendored
@ -548,7 +548,7 @@ extern void * stbds_shmode_func(size_t elemsize, int mode);
|
|||||||
#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
|
#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
|
||||||
#define stbds_arraddnoff stbds_arraddnindex
|
#define stbds_arraddnoff stbds_arraddnindex
|
||||||
#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1])
|
#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1])
|
||||||
#define stbds_arrfree(a) ((void) ((a) ? stbds_arrfreef(a) : (void)0), (a)=NULL)
|
#define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
|
||||||
#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1)
|
#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1)
|
||||||
#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
|
#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
|
||||||
#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
|
#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user