commit 68f4f9a532c10774aebc8d12d8a96278f782538a Author: Aaro Saila Date: Wed Sep 16 11:46:41 2026 +0300 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/assets/LiberationSans-Regular.ttf b/assets/LiberationSans-Regular.ttf new file mode 100644 index 0000000..5610ce5 Binary files /dev/null and b/assets/LiberationSans-Regular.ttf differ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..c6aa3a5 --- /dev/null +++ b/meson.build @@ -0,0 +1,55 @@ +project( + 'scufpong', + 'c', + default_options: [ + 'force_fallback_for=scufeng' + ] +) + +c_args = [ + '-std=c23', + '-Wall', + '-Wextra', + '-pedantic' +] + +scufeng_dep = dependency( + 'scufeng', + allow_fallback: true +) + +game = executable( + 'game', + 'src/common/game.c', + 'src/game/MainMenu.c', + 'src/game/MultiPlayer.c', + 'src/game/SinglePlayer.c', + 'src/game/pong.c', + 'src/game/screen.c', + include_directories: include_directories( + 'src/common/' + ), + c_args: [ + c_args, + '-DFONT_PATH="../assets/LiberationSans-Regular.ttf"', + '-D_DEFAULT_SOURCE' # for inet_aton + ], + link_args: [ + '-lm' + ], + dependencies: [ + scufeng_dep + ] +) + +server = executable( + 'server', + 'src/server/server.c', + c_args: [ + c_args, + '-D_DEFAULT_SOURCE' # for inet_aton + ], + dependencies: [ + scufeng_dep + ] +) diff --git a/src/common/game.c b/src/common/game.c new file mode 100644 index 0000000..4d935a5 --- /dev/null +++ b/src/common/game.c @@ -0,0 +1,271 @@ +#include "cglm/call/vec2.h" + +#include "game.h" +#include "scufeng/engine.h" +#include "scufeng/util.h" + +typedef enum { + BCR_PADDLE_L_GOAL, + BCR_PADDLE_R_GOAL, +} BALL_COLLISION_RESULT; + +GameState GameState_new( + const float paddles_w, + const float paddles_h, + const float paddle_l_x, + const float paddle_speed, + const float ball_w, + const vec2 ball_pos, + const float ball_speed +) { + +#define BALL_START_DIR { -1.0f, 0.0f } + + // clang-format off + GameState state = { + .paddle_l = { + .pos = { paddle_l_x, 0.5f }, + .w = paddles_w, + .h = paddles_h, + .dirv = GLM_VEC2_ZERO_INIT, + .speed = paddle_speed + }, + .paddle_r = { + .pos = { 1.0f - paddle_l_x, 0.5f }, + .w = paddles_w, + .h = paddles_h, + .dirv = GLM_VEC2_ZERO_INIT, + .speed = paddle_speed + }, + .ball = { + // .pos = { 0.5f, 0.5f }, + .pos = { ball_pos[0], ball_pos[1] }, + .w = ball_w, + .h = ball_w, + .dirv = BALL_START_DIR, + .speed = ball_speed + }, + .ball_start_pos = { ball_pos[0], ball_pos[1] }, + .ball_start_dir = BALL_START_DIR + }; + // clang-format on + + return state; +} + +static void paddle_collision_check(MovingObject* const paddle) { + if (paddle->pos[1] - paddle->h / 2.0f < 0.0f) { + paddle->pos[1] = paddle->h / 2.0f; + } + if (paddle->pos[1] + paddle->h / 2.0f > 1.0f) { + paddle->pos[1] = 1.0f - paddle->h / 2.0f; + } +} + +static void paddle_r_move(GameState* const state) { + const float ball_y = state->ball.pos[1]; + + const float target_zone_range = state->paddle_r.h / 2.0f; + const float target_zone_top = state->paddle_r.pos[1] - target_zone_range; + const float target_zone_middle = state->paddle_r.pos[1]; + const float target_zone_bottom = state->paddle_r.pos[1] + target_zone_range; + + const bool ball_is_below = ball_y > target_zone_bottom; + const bool ball_is_above = ball_y < target_zone_top; + const bool ball_is_centered = ball_y <= target_zone_middle + target_zone_range / 2.0f + && ball_y >= target_zone_middle - target_zone_range / 2.0f; + // printf("%d%d\n", ball_is_below, ball_is_above); + + if (ball_is_below) { + state->paddle_r.dirv[1] = 1.0f; + } else if (ball_is_above) { + state->paddle_r.dirv[1] = -1.0f; + } else if (ball_is_centered) { + state->paddle_r.dirv[1] = 0.0f; + } +} + +static void game_next_round(GameState* const state) { + glmc_vec2_copy(state->ball_start_pos, state->ball.pos); + glmc_vec2_copy(state->ball_start_dir, state->ball.dirv); +} + +static bool ball_bounds_collision_check(GameState* const state, vec2 new_pos) { + MovingObject* const ball = &state->ball; + + const float wd2 = ball->w / 2.0f; + const float hd2 = ball->h / 2.0f; + + if (new_pos[0] + wd2 <= 0.0f) { + state->score_r++; + game_next_round(state); + return true; + } else if (new_pos[0] - wd2 >= 1.0f) { + state->score_l++; + game_next_round(state); + return true; + } + + if (new_pos[1] - hd2 < 0.0f) { + new_pos[1] = hd2; + glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, -1.0f }, ball->dirv); + } + if (new_pos[1] + hd2 > 1.0f) { + new_pos[1] = 1.0f - hd2; + glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, 1.0f }, ball->dirv); + } + + return false; +} + +static void ball_paddles_collision_check(GameState* const state, vec2 new_pos) { + static const float possible_y_directions[] = { + 0.0f, + 0.5f, + -0.5f + }; + + MovingObject* const ball = &state->ball; + const MovingObject* const paddle_l = &state->paddle_l; + const MovingObject* const paddle_r = &state->paddle_r; + + const float ball_wd2 = ball->w / 2.0f; + const float ball_hd2 = ball->h / 2.0f; + const float paddle_wd2 = paddle_l->w / 2.0f; + const float paddle_hd2 = paddle_l->h / 2.0f; + vec2 paddle_side_top; + vec2 paddle_side_bottom; + vec2 ball_side_top; + vec2 ball_side_bottom; + float paddle_back_x; + vec2 ball_pos; + vec2 new_pos_adj; + paddle_side_top[0] = paddle_l->pos[0] + paddle_wd2; + paddle_side_top[1] = paddle_l->pos[1] - paddle_hd2; + paddle_side_bottom[0] = paddle_side_top[0]; + paddle_side_bottom[1] = paddle_l->pos[1] + paddle_hd2; + ball_side_top[0] = ball->pos[0] - ball_wd2; + ball_side_top[1] = ball->pos[1] - ball_hd2; + ball_side_bottom[0] = ball_side_top[0]; + ball_side_bottom[1] = ball->pos[1] + ball_hd2; + paddle_back_x = paddle_l->pos[0] - paddle_wd2; + glmc_vec2_copy(ball->pos, ball_pos); + glmc_vec2_copy(new_pos, new_pos_adj); + + bool collision = false; + bool mirror_ip = false; + vec2 ip; + + if (state->ball.dirv[0] > 0.0f) { + mirror_ip = true; + paddle_side_top[0] = 1.0f - (paddle_r->pos[0] - paddle_r->w / 2.0f); + paddle_side_top[1] = (paddle_r->pos[1] - paddle_r->h / 2.0f); + paddle_side_bottom[0] = paddle_side_top[0]; + paddle_side_bottom[1] = paddle_side_top[1] + paddle_r->h; + ball_pos[0] = 1.0f - ball_pos[0]; + ball_side_top[0] = ball_pos[0] - ball_wd2; + ball_side_bottom[0] = ball_side_top[0]; + paddle_back_x = 1.0f - (paddle_r->pos[0] + paddle_r->w / 2.0f); + new_pos_adj[0] = 1.0f - new_pos_adj[0]; + } + + // SEResult res = se_render_line_segment_norm_coord( + // paddle_side_top, + // paddle_side_bottom, + // (vec4) { 0.0f, 1.0f, 0.0f, 1.0f } + // ); + // if (res != SE_SUCCESS) { + // LOG_ERRORF("Failed to draw segment: %d", res); + // } + // res = se_render_line_segment_norm_coord( + // ball_side_top, + // ball_side_bottom, + // (vec4) { 1.0f, 0.0f, 0.0f, 1.0f } + // ); + // if (res != SE_SUCCESS) { + // LOG_ERRORF("Failed to draw segment: %d", res); + // } + // res = se_render_line_segment_norm_coord( + // (vec2) { paddle_back_x, 0.0f }, + // (vec2) { paddle_back_x, 1.0f }, + // (vec4) { 0.0f, 1.0f, 0.0f, 1.0f } + // ); + // if (res != SE_SUCCESS) { + // LOG_ERRORF("Failed to draw segment: %d", res); + // } + + const bool inside_paddle = (ball_side_top[0] <= paddle_side_top[0] && ball_side_top[0] >= paddle_back_x) + && ((ball_side_top[1] <= paddle_side_bottom[1] && ball_side_top[1] >= paddle_side_top[1]) + || (ball_side_bottom[1] <= paddle_side_bottom[1] && ball_side_bottom[1] >= paddle_side_top[1])); + + if (inside_paddle) { + ball->dirv[0] *= -1.0f; + ball->dirv[1] = possible_y_directions[se_rand_int() % 3]; + return; + }; + + collision = se_line_segments_intersect( + ball_pos, + new_pos_adj, + paddle_side_top, + paddle_side_bottom, + ip + ); + + if (collision) { + new_pos[0] = mirror_ip ? 1.0f - ip[0] : ip[0]; + new_pos[1] = ip[1]; + state->ball.dirv[0] *= -1.0f; + state->ball.dirv[1] = possible_y_directions[se_rand_int() % 3]; + // state->ball.dirv[1] = 0.0f; + } +} + +static void ball_move(GameState* const state, const double delta) { + vec2 ball_movement; + const float ball_movement_scale = delta * state->ball.speed; + // glmc_vec2_normalize(state->ball.dirv); + glmc_vec2_scale(state->ball.dirv, ball_movement_scale, ball_movement); + + vec2 new_pos; + glmc_vec2_add(state->ball.pos, ball_movement, new_pos); + + if (ball_bounds_collision_check(state, new_pos)) { + return; + } + ball_paddles_collision_check(state, new_pos); + + glmc_vec2_copy(new_pos, state->ball.pos); +} + +void GameState_move_paddle_l(GameState* const state, const SEDirection dir) { + switch (dir) { + case SE_UP: + state->paddle_l.dirv[1] = -1.0f; + break; + case SE_DOWN: + state->paddle_l.dirv[1] = 1.0f; + break; + case SE_NONE: + state->paddle_l.dirv[1] = 0.0f; + default: + break; + } +} + +void GameState_update(GameState* const state, const double delta) { + // Paddles + vec2 paddle_movement; + const float paddle_movement_scale = delta * state->paddle_l.speed; + glmc_vec2_scale(state->paddle_l.dirv, paddle_movement_scale, paddle_movement); + glmc_vec2_add(state->paddle_l.pos, paddle_movement, state->paddle_l.pos); + paddle_collision_check(&state->paddle_l); + + paddle_r_move(state); + glmc_vec2_scale(state->paddle_r.dirv, paddle_movement_scale, paddle_movement); + glmc_vec2_add(state->paddle_r.pos, paddle_movement, state->paddle_r.pos); + paddle_collision_check(&state->paddle_r); + + // Ball + ball_move(state, delta); +} diff --git a/src/common/game.h b/src/common/game.h new file mode 100644 index 0000000..2284922 --- /dev/null +++ b/src/common/game.h @@ -0,0 +1,40 @@ +#ifndef GAME_H_ +#define GAME_H_ + +#include + +#include "cglm/types.h" + +#include "scufeng/engine.h" + +typedef struct { + vec2 pos; + float w; + float h; + vec2 dirv; + float speed; +} MovingObject; + +typedef struct { + MovingObject paddle_l; + MovingObject paddle_r; + MovingObject ball; + vec2 ball_start_pos; + vec2 ball_start_dir; + uint8_t score_l; + uint8_t score_r; +} GameState; + +GameState GameState_new( + const float paddles_w, + const float paddles_h, + const float paddle_l_x, + const float paddle_speed, + const float ball_w, + const vec2 ball_pos, + const float ball_speed +); +void GameState_move_paddle_l(GameState *const state, const SEDirection dir); +void GameState_update(GameState *const state, const double delta); + +#endif // GAME_H_ diff --git a/src/game/MainMenu.c b/src/game/MainMenu.c new file mode 100644 index 0000000..0a0e65d --- /dev/null +++ b/src/game/MainMenu.c @@ -0,0 +1,61 @@ +#include + +#include "scufeng/engine.h" +#include "scufeng/Shape.h" +#include "scufeng/Scene.h" +#include "cglm/vec3.h" + +#include "MainMenu.h" +#include "screen.h" + +MainMenu MainMenu_alloc(const unsigned int screen_w, const unsigned int screen_h) { + MainMenu mm; + + mm.title_text = SEMesh_string_alloc("Pong", 1.0f, GLM_VEC3_ONE); + mm.title_text.transform.pos[0] = screen_w / 2.0f; + mm.title_text.transform.pos[1] = screen_h * 0.8f; + + mm.sp_text = SEMesh_string_alloc("Singleplayer", 1.0f, GLM_VEC3_ONE); + mm.sp_text.transform.pos[0] = screen_w * 0.25f; + mm.sp_text.transform.pos[1] = screen_h * 0.5f; + + mm.mp_text = SEMesh_string_alloc("Multiplayer", 1.0f, GLM_VEC3_ONE); + mm.mp_text.transform.pos[0] = screen_w - mm.sp_text.transform.pos[0]; + mm.mp_text.transform.pos[1] = mm.sp_text.transform.pos[1]; + + return mm; +} + +static Screen MainMenu_input_handle(MainMenu *const mm) { + if (se_get_mouse_button(SE_MOUSE_BUTTON_LEFT) == SE_KEY_ACTION_PRESS) { + double x, y; + se_get_mouse_pos(&x, &y); + + if (SEMesh_rect_is_pos_inside(&mm->sp_text, x, y)) { + printf("SP\n"); + return PONG_SINGLEPLAYER; + } + + if (SEMesh_rect_is_pos_inside(&mm->mp_text, x, y)) { + printf("MP\n"); + return PONG_MULTIPLAYER; + } + } + + return PONG_MAIN_MENU; +} + +Screen MainMenu_tick(MainMenu *const mm) { + const Screen screen = MainMenu_input_handle(mm); + if (screen != PONG_MAIN_MENU) { + return screen; + } + SEMesh *meshes[] = { + &mm->title_text, + &mm->sp_text, + &mm->mp_text + }; + se_render_meshes_2d(meshes, sizeof(meshes) / sizeof(meshes[0])); + + return PONG_MAIN_MENU; +} diff --git a/src/game/MainMenu.h b/src/game/MainMenu.h new file mode 100644 index 0000000..923915a --- /dev/null +++ b/src/game/MainMenu.h @@ -0,0 +1,14 @@ +#pragma once + +#include "scufeng/Shape.h" + +#include "screen.h" + +typedef struct { + SEMesh title_text; + SEMesh sp_text; + SEMesh mp_text; +} MainMenu; + +MainMenu MainMenu_alloc(const unsigned int screen_w, const unsigned int screen_h); +Screen MainMenu_tick(MainMenu *const mm); diff --git a/src/game/MultiPlayer.c b/src/game/MultiPlayer.c new file mode 100644 index 0000000..7204707 --- /dev/null +++ b/src/game/MultiPlayer.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scufeng/log.h" + +#include "MultiPlayer.h" + +MultiPlayer MultiPlayer_alloc( + const char *server_host, + const uint16_t server_port +) { + MultiPlayer mp; + + struct protoent *proto = getprotobyname("tcp"); + mp.sock = socket(AF_INET, SOCK_STREAM, proto->p_proto); + if (mp.sock == -1) { + LOG_FATALF("socket failed: %d %s", errno, strerror(errno)); + } + + struct in_addr server_addr; + int res = inet_aton(server_host, &server_addr); + if (res == 0) { + LOG_FATAL("inet_aton failed"); + } + + struct sockaddr_in server_sockaddr = { + .sin_family = AF_INET, + .sin_addr = server_addr, + .sin_port = htons(server_port) + }; + + mp.conn = connect( + mp.sock, + (struct sockaddr *) &server_sockaddr, + sizeof(server_sockaddr) + ); + if (mp.conn == -1) { + LOG_FATALF("connect failed: %d %s", errno, strerror(errno)); + } + + return mp; +} + +void MultiPlayer_free(MultiPlayer *const mp) { + close(mp->conn); + mp->conn = 0; + close(mp->sock); + mp->sock = 0; +} + +void MultiPlayer_tick(MultiPlayer *const mp) { + char buf[1024] = { 0 }; + const size_t buf_size = sizeof(buf) / sizeof(buf[0]); + fgets(buf, buf_size, stdin); + + send(mp->conn, buf, strlen(buf), 0); +} diff --git a/src/game/MultiPlayer.h b/src/game/MultiPlayer.h new file mode 100644 index 0000000..f41b471 --- /dev/null +++ b/src/game/MultiPlayer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +typedef struct { + int sock; + int conn; +} MultiPlayer; + +MultiPlayer MultiPlayer_alloc( + const char *server_host, + const uint16_t server_port +); +void MultiPlayer_free(MultiPlayer *const mp); +void MultiPlayer_tick(MultiPlayer *const mp); diff --git a/src/game/SinglePlayer.c b/src/game/SinglePlayer.c new file mode 100644 index 0000000..210f1d6 --- /dev/null +++ b/src/game/SinglePlayer.c @@ -0,0 +1,122 @@ +#include "cglm/vec4.h" +#include "scufeng/Scene.h" + +#include "SinglePlayer.h" +#include "screen.h" + +SinglePlayer SinglePlayer_alloc( + unsigned int screen_w, + unsigned int screen_h, + float paddles_w, + float paddles_h, + float paddle_l_x, + float paddle_speed, + float ball_w, + vec2 ball_pos, + float ball_speed +) { + SinglePlayer sp = { + .screen_w = screen_w, + .screen_h = screen_h, + .state = GameState_new( + paddles_w, + paddles_h, + paddle_l_x, + paddle_speed, + ball_w, + ball_pos, + ball_speed + ), + .paddle_l = SEMesh_rect_color_alloc(GLM_VEC4_ONE), + .paddle_r = SEMesh_rect_color_alloc(GLM_VEC4_ONE), + .ball = SEMesh_rect_color_alloc(GLM_VEC4_ONE), + }; + sp.paddle_l.transform.scale[0] = screen_w * paddles_w; + sp.paddle_l.transform.scale[1] = screen_h * paddles_h; + sp.paddle_l.transform.pos[0] = screen_w * paddle_l_x; + sp.paddle_l.transform.pos[1] = screen_h / 2.0f; + + sp.paddle_r.transform.scale[0] = screen_w * paddles_w; + sp.paddle_r.transform.scale[1] = screen_h * paddles_h; + sp.paddle_r.transform.pos[0] = screen_w * (1.0f - paddle_l_x); + sp.paddle_r.transform.pos[1] = screen_h / 2.0f; + + sp.ball.transform.scale[0] = screen_w * ball_w; + sp.ball.transform.scale[1] = sp.ball.transform.scale[0]; + sp.ball.transform.pos[0] = screen_w * ball_pos[0]; + sp.ball.transform.pos[1] = screen_h * ball_pos[1]; + + return sp; +} + +void SinglePlayer_free(SinglePlayer *sp) { + SEMesh_free(&sp->paddle_l); + SEMesh_free(&sp->paddle_r); + SEMesh_free(&sp->ball); +} + +static void pos_convert( + vec3 render_pos, + const vec3 state_pos, + const unsigned int screen_w, + const unsigned int screen_h +) { + render_pos[0] = state_pos[0] * screen_w; + render_pos[1] = (1.0f - state_pos[1]) * screen_h; +} + +static void update_shapes( + const GameState *const state, + SEMesh *const paddle_l, + SEMesh *const paddle_r, + SEMesh *const ball, + const unsigned int screen_w, + const unsigned int screen_h +) { + + // Paddles + pos_convert(paddle_l->transform.pos, state->paddle_l.pos, screen_w, screen_h); + pos_convert(paddle_r->transform.pos, state->paddle_r.pos, screen_w, screen_h); + + // Ball + pos_convert(ball->transform.pos, state->ball.pos, screen_w, screen_h); +} + +static bool key_input_handle(GameState *const state) { + if (se_get_key(SE_KEY_W) == SE_KEY_ACTION_PRESS) { + GameState_move_paddle_l(state, SE_UP); + } else if (se_get_key(SE_KEY_S) == SE_KEY_ACTION_PRESS) { + GameState_move_paddle_l(state, SE_DOWN); + } else { + GameState_move_paddle_l(state, SE_NONE); + } + + if (se_get_key(SE_KEY_ESCAPE) == SE_KEY_ACTION_PRESS) { + return true; + } + + return false; +} + +Screen SinglePlayer_tick(SinglePlayer *const sp, const double delta) { + if (key_input_handle(&sp->state)) { + return PONG_MAIN_MENU; + } + GameState_update(&sp->state, delta); + update_shapes( + &sp->state, + &sp->paddle_l, + &sp->paddle_r, + &sp->ball, + sp->screen_w, + sp->screen_h + ); + SEMesh *meshes[] = { + &sp->paddle_l, + &sp->paddle_r, + &sp->ball + }; + se_render_meshes_2d(meshes, sizeof(meshes) / sizeof(meshes[0])); + + return PONG_SINGLEPLAYER; +} diff --git a/src/game/SinglePlayer.h b/src/game/SinglePlayer.h new file mode 100644 index 0000000..f7cfb0d --- /dev/null +++ b/src/game/SinglePlayer.h @@ -0,0 +1,29 @@ +#pragma once + +#include "scufeng/Shape.h" + +#include "game.h" +#include "screen.h" + +typedef struct { + unsigned int screen_w; + unsigned int screen_h; + GameState state; + SEMesh paddle_l; + SEMesh paddle_r; + SEMesh ball; +} SinglePlayer; + +SinglePlayer SinglePlayer_alloc( + unsigned int screen_w, + unsigned int screen_h, + float paddles_w, + float paddles_h, + float paddle_l_x, + float paddle_speed, + float ball_w, + vec2 ball_pos, + float ball_speed +); +void SinglePlayer_free(SinglePlayer *sp); +Screen SinglePlayer_tick(SinglePlayer *const sp, const double delta); diff --git a/src/game/pong.c b/src/game/pong.c new file mode 100644 index 0000000..7a1531c --- /dev/null +++ b/src/game/pong.c @@ -0,0 +1,78 @@ +#include "cglm/types.h" +#include "scufeng/engine.h" +#include "scufeng/text.h" + +#include "screen.h" +#include "MainMenu.h" +#include "SinglePlayer.h" +#include "MultiPlayer.h" + +static const unsigned int screen_w = 1200; +static const unsigned int screen_h = 600; +static const float paddles_w = 0.01f; +static const float paddles_h = 0.10f; +static const float paddle_l_x = (1.0f / 16); +static const float ball_w = paddles_w * 0.5f; +static vec2 ball_pos = { 0.5f, 0.5f }; +static const float paddle_speed = 0.7f; +static const float ball_speed = 0.7f; + +int main() { + se_init(true); + se_window_create(screen_w, screen_h, "Pong", -1); + se_swap_interval(0); + se_fps_lock_set_fps(60); + se_font_load(FONT_PATH, 0, 48); + + se_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + + MainMenu mm = MainMenu_alloc(screen_w, screen_h); + SinglePlayer sp; + + double last_frame_time = 0.0; + while (!se_window_should_close()) { + const double current_time = se_get_time(); + const double delta = current_time - last_frame_time; + last_frame_time = current_time; + se_fps_lock_frame_start_s(current_time); + + se_clear(); + se_handle_events(); + + switch (current_screen) { + case PONG_MAIN_MENU: + current_screen = MainMenu_tick(&mm); + if (current_screen == PONG_SINGLEPLAYER) { + sp = SinglePlayer_alloc( + screen_w, + screen_h, + paddles_w, + paddles_h, + paddle_l_x, + paddle_speed, + ball_w, + ball_pos, + ball_speed + ); + } + break; + case PONG_SINGLEPLAYER: + current_screen = SinglePlayer_tick(&sp, delta); + if (current_screen == PONG_MAIN_MENU) { + SinglePlayer_free(&sp); + } + break; + + case PONG_MULTIPLAYER: + MultiPlayer_tick(); + break; + } + + // printf("%u %u\n", sp.state.score_l, sp.state.score_r); + se_fps_lock_wait(); + se_swap_buffers(); + // printf("FT: %lf FPS: %lf\n", se_get_time() - current_time, 1.0 / (se_get_time() - current_time)); + } + + return 0; +} diff --git a/src/game/screen.c b/src/game/screen.c new file mode 100644 index 0000000..d175d8d --- /dev/null +++ b/src/game/screen.c @@ -0,0 +1,3 @@ +#include "screen.h" + +Screen current_screen = PONG_MAIN_MENU; diff --git a/src/game/screen.h b/src/game/screen.h new file mode 100644 index 0000000..e2ae012 --- /dev/null +++ b/src/game/screen.h @@ -0,0 +1,9 @@ +#pragma once + +typedef enum { + PONG_MAIN_MENU, + PONG_SINGLEPLAYER, + PONG_MULTIPLAYER, +} Screen; + +extern Screen current_screen; diff --git a/src/server/server.c b/src/server/server.c new file mode 100644 index 0000000..2fc8007 --- /dev/null +++ b/src/server/server.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "scufeng/log.h" + +#define SERVER_HOST "127.0.0.1" +#define SERVER_PORT 8080 + +int main() { + struct protoent *proto = getprotobyname("tcp"); + if (proto == nullptr) { + LOG_FATAL("getprotobyname failed"); + } + const int sock = socket(AF_INET, SOCK_STREAM, proto->p_proto); + if (sock == -1) { + LOG_FATALF("socket failed: %s", strerror(errno)); + } + + { + struct in_addr addr; + int res = inet_aton(SERVER_HOST, &addr); + if (res == 0) { + LOG_FATAL("inet_aton failed"); + } + struct sockaddr_in own_addr = { + .sin_family = AF_INET, + .sin_addr = addr, + .sin_port = htons(SERVER_PORT) + }; + res = bind(sock, (struct sockaddr*) &own_addr, sizeof(own_addr)); + if (res == -1) { + LOG_FATALF("bind failed: %s", strerror(errno)); + } + } + + int res = listen(sock, 5); + if (res == -1) { + LOG_FATALF("listen failed: %s", strerror(errno)); + } + + int conn = -1; + + conn = accept(sock, nullptr, nullptr); + if (conn == -1) { + LOG_FATALF("accept failed: %s", strerror(errno)); + } + + char buf[1024] = {0}; + const size_t buf_size = sizeof(buf) / sizeof(buf[0]); + + while (true) { + int received = recv(conn, buf, buf_size, 0); + if (received == 0) { + printf("buf: %s\n", buf); + buf[0] = '\0'; + break; + } else if (received == -1) { + LOG_FATALF("recv failed: %d %s", errno, strerror(errno)); + } else { + buf[received] = '\0'; + } + } + + return 0; +} diff --git a/subprojects/.wraplock b/subprojects/.wraplock new file mode 100644 index 0000000..e69de29 diff --git a/subprojects/scufeng/GIT_COMMIT b/subprojects/scufeng/GIT_COMMIT new file mode 100644 index 0000000..d609ddf --- /dev/null +++ b/subprojects/scufeng/GIT_COMMIT @@ -0,0 +1 @@ +c505e04113e221188440051de27fad12c0670205 diff --git a/subprojects/scufeng/include/scufeng/Camera.h b/subprojects/scufeng/include/scufeng/Camera.h new file mode 100644 index 0000000..962d41a --- /dev/null +++ b/subprojects/scufeng/include/scufeng/Camera.h @@ -0,0 +1,37 @@ +#ifndef SCUFENG_CAMERA_H_ +#define SCUFENG_CAMERA_H_ + +#include "cglm/types.h" + +#include "scufeng/engine.h" + +typedef struct { + vec3 pos; + float pitch_rad; + float yaw_rad; + float fov_rad; + float near_clip; + float far_clip; + vec3 front; + vec3 right; + mat4 view; + mat4 projection; +} SECamera; + +SECamera SECamera_new( + vec3 pos, + const float fov_rad, + const float near_clip, + const float far_clip +); + +void SECamera_set_perspective(SECamera *const camera); +void SECamera_pos_set(SECamera *const camera, vec3 pos); +void SECamera_move_to_direction(SECamera *const camera, SEDirection dir, const float mag); +void SECamera_look_dir(SECamera *const camera, vec3 dir); +void SECamera_look_at(SECamera *const camera, vec3 target); +void SECamera_pitch_add(SECamera *const camera, const float pitch_rad); +void SECamera_yaw_add(SECamera *const camera, const float yaw_rad); +void SECamera_view_update(SECamera *const camera); + +#endif // SCUFENG_CAMERA_H_ diff --git a/subprojects/scufeng/include/scufeng/Scene.h b/subprojects/scufeng/include/scufeng/Scene.h new file mode 100644 index 0000000..70f552e --- /dev/null +++ b/subprojects/scufeng/include/scufeng/Scene.h @@ -0,0 +1,12 @@ +#ifndef SCUFENG_SCENE_H_ +#define SCUFENG_SCENE_H_ + +#include "scufeng/Shape.h" +#include "engine.h" + +void se_render_meshes_2d(SEMesh *const *const meshes, const size_t meshes_n); +SEResult se_render_line_segment(const vec2 p1, const vec2 p2, const vec4 color); +SEResult se_render_line_segment_norm_coord(const vec2 p1, const vec2 p2, const vec4 color); +void se_text_render(const char* text, int x, int y, float scale, const vec3 color); + +#endif // SCUFENG_SCENE_H_ diff --git a/subprojects/scufeng/include/scufeng/Shape.h b/subprojects/scufeng/include/scufeng/Shape.h new file mode 100644 index 0000000..0373a6e --- /dev/null +++ b/subprojects/scufeng/include/scufeng/Shape.h @@ -0,0 +1,90 @@ +#ifndef SCUFENG_SHAPE_H_ +#define SCUFENG_SHAPE_H_ + +#include +#include + +#include "cglm/types.h" + +#include "scufeng/engine.h" + +typedef enum { + SE_TRIANGLES, + SE_TRIANGLE_STRIP, + SE_TRIANGLE_FAN, + SE_LINES, + SE_LINE_STRIP +} SEDrawMode; + +typedef struct { + vec3 pos; + vec3 scale; + vec3 rotation; +} SETransform; + +typedef struct { + float pos[3]; + float tex_coords[2]; +} SEVertex; + +typedef enum { + SE_MESH_COLOR, + SE_MESH_TEXTURE +} SEMeshType; + +typedef struct { + unsigned int id; + float w; + float h; +} SEMeshTexture; + +typedef struct { + unsigned int vao; + unsigned int vbo; + size_t verts_n; + int draw_mode; + + SETransform transform; + + SEMeshType type; + union { + SEMeshTexture texture; + vec4 color; + }; +} SEMesh; + +SEResult SEShape_alloc( + SETransform *const shape, + const SEVertex *const vertices, + const size_t vertices_n, + const vec4 color, + const SEDrawMode draw_mode +); +void SEMesh_free(SEMesh *const mesh); +void SEMesh_set_draw_mode(SEMesh *mesh, const SEDrawMode mode); +void SEMesh_render(const SEMesh *const mesh); +void SEMesh_bbox_get_2d( + const SEMesh *const mesh, + float *left, + float *right, + float *top, + float *bottom +); +bool SEMesh_rect_is_pos_inside(const SEMesh *const mesh, const float x, const float y); +void SEShape_rotation_set(SETransform *shape, vec3 rotation); +void SEShape_rotate_2d_center(SETransform *const shape, const float w, const float h, const float angle_rad); +void SEShape_scale_set(SETransform *shape, vec3 scale); +void SEShape_scale(SETransform *const shape, vec3 scale); +void SEShape_pos_set(SETransform *const shape, vec3 pos); +void SEShape_translate_2d_center(SETransform *const shape, const float w, const float h, const vec2 pos); +void SEShape_transform_reset(SETransform *shape); + +SEMesh SEMesh_rect_color_alloc(vec4 color); +SEMesh SEMesh_string_alloc( + const char *text, + const float scale, + const vec3 color +); +// void SEShape_transform_apply(SEShape *const shape); + +#endif // SCUFENG_SHAPE_H_ diff --git a/subprojects/scufeng/include/scufeng/engine.h b/subprojects/scufeng/include/scufeng/engine.h new file mode 100644 index 0000000..903ac68 --- /dev/null +++ b/subprojects/scufeng/include/scufeng/engine.h @@ -0,0 +1,74 @@ +#ifndef SCUFENG_H_ +#define SCUFENG_H_ + +#include + +#include "keys.h" // IWYU pragma: export + +#define SERESULT_X(X) \ + X(SE_SUCCESS) \ + X(SE_OUT_OF_MEMORY) \ + X(SE_FC_FAILURE) \ + X(SE_NOT_FOUND) \ + X(SE_FT_FAILURE) + +#define X(val) val, + +typedef enum { + SERESULT_X(X) +} SEResult; + +#undef X + +typedef enum { + SE_FILL, + SE_LINE, + SE_POINT +} SEPolygonMode; + +typedef enum { + SE_NONE, + SE_FORWARD, + SE_BACKWARD, + SE_LEFT, + SE_RIGHT, + SE_UP, + SE_DOWN +} SEDirection; + +typedef void (*SEKeyCb)(SEKey key, SEKeyAction action, SEKeyMod mods); + +void se_init(const bool debug); +void se_deinit(void); +void se_window_create( + const int w, + const int h, + const char* title, + const int fullscreen_monitor_i +); +void se_window_close(); +void se_clear_color( + const float r, + const float g, + const float b, + const float a +); +void se_clear(void); +void se_swap_buffers(void); +bool se_window_should_close(void); +void se_handle_events(void); +double se_get_time(void); +double se_delta_time(const double current_time); +void se_set_polygon_mode(const SEPolygonMode mode); +void se_set_key_callback(SEKeyCb cb); +SEKeyAction se_get_key(const SEKey key); +SEKeyAction se_get_mouse_button(const SEMouseButton btn); +void se_get_mouse_pos(double *x, double *y); +void se_swap_interval(const int interval); +void se_fps_lock_set_fps(const unsigned int fps); +void se_fps_lock_frame_start_s(const double frame_start_s); +void se_fps_lock_wait(void); + +const char* SEResult_to_str(const SEResult res); + +#endif // SCUFENG_H_ diff --git a/subprojects/scufeng/include/scufeng/keys.h b/subprojects/scufeng/include/scufeng/keys.h new file mode 100644 index 0000000..6d0efc0 --- /dev/null +++ b/subprojects/scufeng/include/scufeng/keys.h @@ -0,0 +1,164 @@ +#ifndef SCUFENG_KEYS_H_ +#define SCUFENG_KEYS_H_ + +#include "GLFW/glfw3.h" + +typedef enum { + SE_KEY_UNKNOWN = GLFW_KEY_UNKNOWN, + + /* Printable keys */ + SE_KEY_SPACE = GLFW_KEY_SPACE, + SE_KEY_APOSTROPHE = GLFW_KEY_APOSTROPHE, + SE_KEY_COMMA = GLFW_KEY_COMMA, + SE_KEY_MINUS = GLFW_KEY_MINUS, + SE_KEY_PERIOD = GLFW_KEY_PERIOD, + SE_KEY_SLASH = GLFW_KEY_SLASH, + SE_KEY_0 = GLFW_KEY_0, + SE_KEY_1 = GLFW_KEY_1, + SE_KEY_2 = GLFW_KEY_2, + SE_KEY_3 = GLFW_KEY_3, + SE_KEY_4 = GLFW_KEY_4, + SE_KEY_5 = GLFW_KEY_5, + SE_KEY_6 = GLFW_KEY_6, + SE_KEY_7 = GLFW_KEY_7, + SE_KEY_8 = GLFW_KEY_8, + SE_KEY_9 = GLFW_KEY_9, + SE_KEY_SEMICOLON = GLFW_KEY_SEMICOLON, + SE_KEY_EQUAL = GLFW_KEY_EQUAL, + SE_KEY_A = GLFW_KEY_A, + SE_KEY_B = GLFW_KEY_B, + SE_KEY_C = GLFW_KEY_C, + SE_KEY_D = GLFW_KEY_D, + SE_KEY_E = GLFW_KEY_E, + SE_KEY_F = GLFW_KEY_F, + SE_KEY_G = GLFW_KEY_G, + SE_KEY_H = GLFW_KEY_H, + SE_KEY_I = GLFW_KEY_I, + SE_KEY_J = GLFW_KEY_J, + SE_KEY_K = GLFW_KEY_K, + SE_KEY_L = GLFW_KEY_L, + SE_KEY_M = GLFW_KEY_M, + SE_KEY_N = GLFW_KEY_N, + SE_KEY_O = GLFW_KEY_O, + SE_KEY_P = GLFW_KEY_P, + SE_KEY_Q = GLFW_KEY_Q, + SE_KEY_R = GLFW_KEY_R, + SE_KEY_S = GLFW_KEY_S, + SE_KEY_T = GLFW_KEY_T, + SE_KEY_U = GLFW_KEY_U, + SE_KEY_V = GLFW_KEY_V, + SE_KEY_W = GLFW_KEY_W, + SE_KEY_X = GLFW_KEY_X, + SE_KEY_Y = GLFW_KEY_Y, + SE_KEY_Z = GLFW_KEY_Z, + SE_KEY_LEFT_BRACKET = GLFW_KEY_LEFT_BRACKET, + SE_KEY_BACKSLASH = GLFW_KEY_BACKSLASH, + SE_KEY_RIGHT_BRACKET = GLFW_KEY_RIGHT_BRACKET, + SE_KEY_GRAVE_ACCENT = GLFW_KEY_GRAVE_ACCENT, + SE_KEY_WORLD_1 = GLFW_KEY_WORLD_1, + SE_KEY_WORLD_2 = GLFW_KEY_WORLD_2, + + /* Function keys */ + SE_KEY_ESCAPE = GLFW_KEY_ESCAPE, + SE_KEY_ENTER = GLFW_KEY_ENTER, + SE_KEY_TAB = GLFW_KEY_TAB, + SE_KEY_BACKSPACE = GLFW_KEY_BACKSPACE, + SE_KEY_INSERT = GLFW_KEY_INSERT, + SE_KEY_DELETE = GLFW_KEY_DELETE, + SE_KEY_RIGHT = GLFW_KEY_RIGHT, + SE_KEY_LEFT = GLFW_KEY_LEFT, + SE_KEY_DOWN = GLFW_KEY_DOWN, + SE_KEY_UP = GLFW_KEY_UP, + SE_KEY_PAGE_UP = GLFW_KEY_PAGE_UP, + SE_KEY_PAGE_DOWN = GLFW_KEY_PAGE_DOWN, + SE_KEY_HOME = GLFW_KEY_HOME, + SE_KEY_END = GLFW_KEY_END, + SE_KEY_CAPS_LOCK = GLFW_KEY_CAPS_LOCK, + SE_KEY_SCROLL_LOCK = GLFW_KEY_SCROLL_LOCK, + SE_KEY_NUM_LOCK = GLFW_KEY_NUM_LOCK, + SE_KEY_PRINT_SCREEN = GLFW_KEY_PRINT_SCREEN, + SE_KEY_PAUSE = GLFW_KEY_PAUSE, + SE_KEY_F1 = GLFW_KEY_F1, + SE_KEY_F2 = GLFW_KEY_F2, + SE_KEY_F3 = GLFW_KEY_F3, + SE_KEY_F4 = GLFW_KEY_F4, + SE_KEY_F5 = GLFW_KEY_F5, + SE_KEY_F6 = GLFW_KEY_F6, + SE_KEY_F7 = GLFW_KEY_F7, + SE_KEY_F8 = GLFW_KEY_F8, + SE_KEY_F9 = GLFW_KEY_F9, + SE_KEY_F10 = GLFW_KEY_F10, + SE_KEY_F11 = GLFW_KEY_F11, + SE_KEY_F12 = GLFW_KEY_F12, + SE_KEY_F13 = GLFW_KEY_F13, + SE_KEY_F14 = GLFW_KEY_F14, + SE_KEY_F15 = GLFW_KEY_F15, + SE_KEY_F16 = GLFW_KEY_F16, + SE_KEY_F17 = GLFW_KEY_F17, + SE_KEY_F18 = GLFW_KEY_F18, + SE_KEY_F19 = GLFW_KEY_F19, + SE_KEY_F20 = GLFW_KEY_F20, + SE_KEY_F21 = GLFW_KEY_F21, + SE_KEY_F22 = GLFW_KEY_F22, + SE_KEY_F23 = GLFW_KEY_F23, + SE_KEY_F24 = GLFW_KEY_F24, + SE_KEY_F25 = GLFW_KEY_F25, + SE_KEY_KP_0 = GLFW_KEY_KP_0, + SE_KEY_KP_1 = GLFW_KEY_KP_1, + SE_KEY_KP_2 = GLFW_KEY_KP_2, + SE_KEY_KP_3 = GLFW_KEY_KP_3, + SE_KEY_KP_4 = GLFW_KEY_KP_4, + SE_KEY_KP_5 = GLFW_KEY_KP_5, + SE_KEY_KP_6 = GLFW_KEY_KP_6, + SE_KEY_KP_7 = GLFW_KEY_KP_7, + SE_KEY_KP_8 = GLFW_KEY_KP_8, + SE_KEY_KP_9 = GLFW_KEY_KP_9, + SE_KEY_KP_DECIMAL = GLFW_KEY_KP_DECIMAL, + SE_KEY_KP_DIVIDE = GLFW_KEY_KP_DIVIDE, + SE_KEY_KP_MULTIPLY = GLFW_KEY_KP_MULTIPLY, + SE_KEY_KP_SUBTRACT = GLFW_KEY_KP_SUBTRACT, + SE_KEY_KP_ADD = GLFW_KEY_KP_ADD, + SE_KEY_KP_ENTER = GLFW_KEY_KP_ENTER, + SE_KEY_KP_EQUAL = GLFW_KEY_KP_EQUAL, + SE_KEY_LEFT_SHIFT = GLFW_KEY_LEFT_SHIFT, + SE_KEY_LEFT_CONTROL = GLFW_KEY_LEFT_CONTROL, + SE_KEY_LEFT_ALT = GLFW_KEY_LEFT_ALT, + SE_KEY_LEFT_SUPER = GLFW_KEY_LEFT_SUPER, + SE_KEY_RIGHT_SHIFT = GLFW_KEY_RIGHT_SHIFT, + SE_KEY_RIGHT_CONTROL = GLFW_KEY_RIGHT_CONTROL, + SE_KEY_RIGHT_ALT = GLFW_KEY_RIGHT_ALT, + SE_KEY_RIGHT_SUPER = GLFW_KEY_RIGHT_SUPER, + SE_KEY_MENU = GLFW_KEY_MENU, +} SEKey; + +typedef enum { + SE_KEY_ACTION_RELEASE = GLFW_RELEASE, + SE_KEY_ACTION_PRESS = GLFW_PRESS, + SE_KEY_ACTION_REPEAT = GLFW_REPEAT, +} SEKeyAction; + +typedef enum { + SE_KEY_MOD_SHIFT = GLFW_MOD_SHIFT, + SE_KEY_MOD_CONTROL = GLFW_MOD_CONTROL, + SE_KEY_MOD_ALT = GLFW_MOD_ALT, + SE_KEY_MOD_SUPER = GLFW_MOD_SUPER, + SE_KEY_MOD_CAPS_LOCK = GLFW_MOD_CAPS_LOCK, + SE_KEY_MOD_NUM_LOCK = GLFW_MOD_NUM_LOCK, +} SEKeyMod; + +typedef enum { + SE_MOUSE_BUTTON_1 = 0, + SE_MOUSE_BUTTON_2 = 1, + SE_MOUSE_BUTTON_3 = 2, + SE_MOUSE_BUTTON_4 = 3, + SE_MOUSE_BUTTON_5 = 4, + SE_MOUSE_BUTTON_6 = 5, + SE_MOUSE_BUTTON_7 = 6, + SE_MOUSE_BUTTON_8 = 7, + SE_MOUSE_BUTTON_LAST = GLFW_MOUSE_BUTTON_8, + SE_MOUSE_BUTTON_LEFT = GLFW_MOUSE_BUTTON_1, + SE_MOUSE_BUTTON_RIGHT = GLFW_MOUSE_BUTTON_2, + SE_MOUSE_BUTTON_MIDDLE = GLFW_MOUSE_BUTTON_3, +} SEMouseButton; + +#endif // SCUFENG_KEYS_H_ diff --git a/subprojects/scufeng/include/scufeng/log.h b/subprojects/scufeng/include/scufeng/log.h new file mode 100644 index 0000000..874c1d1 --- /dev/null +++ b/subprojects/scufeng/include/scufeng/log.h @@ -0,0 +1,23 @@ +#ifndef LOG_H_ +#define LOG_H_ + +#include +#include +#include + +#define LOG(str) log_with_tag(stdout, __FILE__, __LINE__, "LOG", (str)) +#define LOGF(fmt, ...) logf_with_tag(stdout, __FILE__, __LINE__, "LOG", (fmt), __VA_ARGS__) + +#define LOG_ERROR(str) log_with_tag(stderr, __FILE__, __LINE__, "ERROR", (str)) +#define LOG_ERRORF(fmt, ...) logf_with_tag(stderr, __FILE__, __LINE__, "ERROR", (fmt), __VA_ARGS__) + +#define LOG_FATAL(str) LOG_ERROR(str); exit(-1) +#define LOG_FATALF(fmt, ...) LOG_ERRORF(fmt, __VA_ARGS__); exit(-1) + +#define LOG_FATAL_TODO(str) log_with_tag(stderr, __FILE__, __LINE__, "FATAL TODO", (str)); exit(-1) +#define LOG_FATAL_TODOF(fmt, ...) logf_with_tag(stderr, __FILE__, __LINE__, "FATAL TODO", (fmt), __VA_ARGS__); exit(-1) + +void log_with_tag(FILE* out_file, char* file_name, size_t line, const char* tag, char* str); +void logf_with_tag(FILE* out_file, char* file_name, size_t line, const char* tag, char* fmt, ...); + +#endif // LOG_H_ diff --git a/subprojects/scufeng/include/scufeng/model.h b/subprojects/scufeng/include/scufeng/model.h new file mode 100644 index 0000000..1ee8d2b --- /dev/null +++ b/subprojects/scufeng/include/scufeng/model.h @@ -0,0 +1,18 @@ +#ifndef SCUFENG_MODEL_H_ +#define SCUFENG_MODEL_H_ + +#include + +typedef struct { + float* vertices; + size_t vertices_n; +} Mesh; + +typedef struct { + Mesh* meshes; + size_t meshes_n; +} Model; + +Model model_load_gltf(const char* path); + +#endif // SCUFENG_MODEL_H_ diff --git a/subprojects/scufeng/include/scufeng/scufcont_impl.h b/subprojects/scufeng/include/scufeng/scufcont_impl.h new file mode 100644 index 0000000..0a8089b --- /dev/null +++ b/subprojects/scufeng/include/scufeng/scufcont_impl.h @@ -0,0 +1,9 @@ +#ifndef SE_SCUFCONT_IMPL_H_ +#define SE_SCUFCONT_IMPL_H_ + +#include "scufcont/DArray.h" +#include "scufeng/Shape.h" + +SCDARRAY_HEADER(SETransform*, SEShapep) + +#endif // SE_SCUFCONT_IMPL_H_ diff --git a/subprojects/scufeng/include/scufeng/text.h b/subprojects/scufeng/include/scufeng/text.h new file mode 100644 index 0000000..76cc196 --- /dev/null +++ b/subprojects/scufeng/include/scufeng/text.h @@ -0,0 +1,12 @@ +#ifndef SCUFENG_TEXT_H_ +#define SCUFENG_TEXT_H_ + +#include "scufeng/engine.h" + +SEResult se_font_load( + const char *path, + const unsigned int pixel_w, + const unsigned int pixel_h +); + +#endif // SCUFENG_TEXT_H_ diff --git a/subprojects/scufeng/include/scufeng/util.h b/subprojects/scufeng/include/scufeng/util.h new file mode 100644 index 0000000..4cab66c --- /dev/null +++ b/subprojects/scufeng/include/scufeng/util.h @@ -0,0 +1,15 @@ +#ifndef SCUFENG_UTIL_H_ +#define SCUFENG_UTIL_H_ + +#include "cglm/types.h" + +int se_rand_int(); +bool se_line_segments_intersect( + const vec2 a1, + const vec2 a2, + const vec2 b1, + const vec2 b2, + vec2 ip +); + +#endif // SCUFENG_UTIL_H_ diff --git a/subprojects/scufeng/libscufeng.a b/subprojects/scufeng/libscufeng.a new file mode 100644 index 0000000..1654201 Binary files /dev/null and b/subprojects/scufeng/libscufeng.a differ diff --git a/subprojects/scufeng/meson.build b/subprojects/scufeng/meson.build new file mode 100644 index 0000000..17038da --- /dev/null +++ b/subprojects/scufeng/meson.build @@ -0,0 +1,15 @@ +project('scufeng', 'c') + +cc = meson.get_compiler('c') + +scufeng_lib_dep = cc.find_library( + 'scufeng', + dirs: meson.current_source_dir() +) + +scufeng_dep = declare_dependency( + dependencies: scufeng_lib_dep, + include_directories: include_directories('include') +) + +meson.override_dependency('scufeng', scufeng_dep)