From c176eedfb6de797c09acbff1f61cf62cf1f2c694 Mon Sep 17 00:00:00 2001 From: kotorifan Date: Tue, 28 Apr 2026 00:47:22 +0200 Subject: Enough for today --- src/common.h | 71 +++++++++++++++++++------------- src/graphics.c | 24 +---------- src/graphics.h | 10 ++--- src/main.c | 85 ++++++++++++++++++-------------------- src/physics.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++----------- src/physics.h | 12 ++++-- 6 files changed, 203 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/common.h b/src/common.h index 7664233..9fafd78 100644 --- a/src/common.h +++ b/src/common.h @@ -7,34 +7,19 @@ #define COMMON_H #include -typedef uint_least8_t u8; -typedef int_least8_t i8; -typedef uint_least16_t u16; -typedef int_least16_t i16; -typedef uint_least32_t u32; -typedef int_least32_t i32; - -typedef struct { - Color color; - float elast; - float frict; - float angle_vel; - float inertia; - Vector2 angle; - Vector2 pos; - Vector2 pos_prev; - Vector2 vel; - bool grabbed; - bool registered; - float mass; - Vector2* vertices; - Vector2* edges; - float line_thickness; - u32 vertex_n; // number of vertices - u32 edge_n; // number of edges -} object_t; +#include +#include +#include +#define MIN_VERTICES 3 +#define MAX_VERTICES 16 +#define MIN_EDGES 3 +#define MAX_EDGES 16 +// how many times to run update_physics per frame +// should be balanced between accuracy and CPU load, the higher +// the number the more resource-intensive it is +#define ITER_PER_FRAME 8 // settings // UI @@ -43,9 +28,9 @@ typedef struct { #define WINDOW_Y 1000 // physics #define MAX_OBJECTS 100 -#define FORCE_RESITUTION_DEFAULT 0.8f +#define FORCE_RESITUTION_DEFAULT 0.7f #define FORCE_GRAVITY_DEFAULT 1000.0f -#define FORCE_LINEAR_DAMPING_DEFAULT 0.995f; +#define FORCE_LINEAR_DAMPING_DEFAULT 0.995f #define ARR_LEN(arr) (sizeof(arr)/sizeof(arr[0])) @@ -56,5 +41,35 @@ typedef struct { 255 \ }) +typedef uint_least8_t u8; +typedef int_least8_t i8; +typedef uint_least16_t u16; +typedef int_least16_t i16; +typedef uint_least32_t u32; +typedef int_least32_t i32; + +typedef struct { + Color color; + + float elast; // restitution + float frict; + float angle; + float angle_vel; + float mass; + float inertia; + + Vector2 pos; + Vector2 pos_prev; + Vector2 vel; + Vector2 vertices[MAX_VERTICES]; + Vector2 local_vertices[MAX_VERTICES]; + Vector2 edges[MAX_EDGES]; + + float line_thickness; + u32 vertex_n; // number of vertices + u32 edge_n; // number of edges + bool grabbed; + bool registered; +} object_t; #endif // COMMON_H diff --git a/src/graphics.c b/src/graphics.c index 0f31d6a..1d55e6f 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -9,37 +9,16 @@ #include "graphics.h" #include "common.h" - void init_graphics(uint32_t x, uint32_t y, const char* title) { InitWindow(x, y, title); SetTargetFPS(60); } -void close_graphics(void) -{ - CloseWindow(); -} - -void begin_graphics_drawing(void) -{ - BeginDrawing(); -} - -void end_graphics_drawing(void) -{ - EndDrawing(); -} - -void clear_graphics(const Color color) -{ - ClearBackground(color); -} - void draw_graphics_object(const object_t* obj) { // shapes smaller than 3 don't make sense - if(obj->vertices == NULL || obj->vertex_n < 3) return; + if(obj->vertex_n < 3) return; for(u32 iter = 0; iter < obj->vertex_n; iter++) { Vector2 start = obj->vertices[iter]; @@ -48,7 +27,6 @@ void draw_graphics_object(const object_t* obj) } } - void draw_graphics_objects(const object_t* world, uint32_t count) { for (uint32_t iter = 0; iter < count; iter++) diff --git a/src/graphics.h b/src/graphics.h index 62dd874..8f327e6 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -1,4 +1,8 @@ -// graphics.h +/** + * @file graphics.h + * @brief Header file for graphics.c + * @date 2026-04-28 +*/ #ifndef GRAPHICS_H #define GRAPHICS_H #include "common.h" @@ -7,10 +11,6 @@ #include void init_graphics(uint32_t x, uint32_t y, const char* title); -void close_graphics(void); -void begin_graphics_drawing(void); -void end_graphics_drawing(void); -void clear_graphics(const Color color); void draw_graphics_object(const object_t* obj); void draw_graphics_objects(const object_t* world, uint32_t count); void draw_graphics_info(uint32_t objs); diff --git a/src/main.c b/src/main.c index f6a1de3..a30fc36 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,8 @@ /** - * @file main.c - * @brief The main loop and controls - * @date 2026-04-27 - */ + * @file main.c + * @brief The main loop and controls + * @date 2026-04-27 + */ #include #include #include @@ -24,30 +24,20 @@ int main(void) object_t* grabbed_obj = NULL; bool anti_gravity_toggle = false; - objs_count = 1; + + + objs_count = 0; init_physics(world, MAX_OBJECTS); init_graphics(WINDOW_X, WINDOW_Y, SCREEN_TITLE); + + Color bg_color = RANDOM_COLOR(); + Image bg = GenImagePerlinNoise(GetScreenWidth(), GetScreenHeight(), 0, 0, 3.0f); + Texture2D bg_tex = LoadTextureFromImage(bg); + UnloadImage(bg); + while(!WindowShouldClose()) { Vector2 pos_cursor = GetMousePosition(); - /* if(IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { */ - /* if(objs_count == 0) { */ - /* grabbed_obj = NULL; */ - /* } else { */ - /* for(uint32_t iter = objs_count; iter > 0; iter--) { */ - /* object_t* obj = &world[iter - 1]; */ - /* float dx = pos_cursor.x - obj->pos.x; */ - /* float dy = pos_cursor.y - obj->pos.y; */ - - /* if(hypot(dx, dy) <= (obj->size_x && obj->size_y)) { */ - /* obj->grabbed = true; */ - /* grabbed_obj = obj; */ - /* break; */ - /* } */ - /* } */ - /* } */ - /* } */ - if (IsMouseButtonReleased(MOUSE_BUTTON_RIGHT)) { if(grabbed_obj != NULL) { grabbed_obj->grabbed = false; @@ -57,50 +47,55 @@ int main(void) if(IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { if(objs_count < MAX_OBJECTS) { - world[objs_count++] = (object_t){ + object_t obj = (object_t){ .color = RANDOM_COLOR(), - .elast = 0.9f, - .frict = 0.99f, + .elast = 0.05f, +// .frict = 0.99f, + .frict = 2.00f, .pos = pos_cursor, .pos_prev = pos_cursor, .vel = { - (float)GetRandomValue(-300, 300), - (float)GetRandomValue(-300, 300) + (float)GetRandomValue(-30, 75), + (float)GetRandomValue(-30, 75) }, .grabbed = false, .registered = true, - .mass = GetRandomValue(3, 100), // will be done later + .line_thickness = 2, + .mass = GetRandomValue(20, 50), // will be done later + .angle = 0.0f, + .angle_vel = 0.0f, + .vertex_n = 0, + .edge_n = 0, }; + generate_random_shape(&obj, (float)GetRandomValue(3, 30)); + world[objs_count++] = obj; } } if(IsKeyPressed(KEY_G)) { - if(anti_gravity_toggle) { - force_gravity = -75.0f; - anti_gravity_toggle = false; - } else if(!anti_gravity_toggle) { - force_gravity = FORCE_GRAVITY_DEFAULT; - anti_gravity_toggle = true; - } + anti_gravity_toggle = !anti_gravity_toggle; + // make the exact number customizable in the common.h + force_gravity = anti_gravity_toggle ? -75.0f : FORCE_GRAVITY_DEFAULT; } - // to-do - /* if(IsKeyPressed(KEY_C)) { */ - /* clear_world(world); */ - /* } */ float dt = GetFrameTime(); - update_physics(world, objs_count, force_gravity, dt); + const u32 steps = ITER_PER_FRAME; + float sub_dt = dt/(float)steps; + for(u32 iter = 0; iter < ITER_PER_FRAME; iter++) { + update_physics(world, objs_count, force_gravity, sub_dt); + } - begin_graphics_drawing(); + BeginDrawing(); - clear_graphics(RAYWHITE); + ClearBackground(BLACK); + DrawTexture(bg_tex, 0, 0, bg_color); draw_graphics_objects(world, objs_count); draw_graphics_info(objs_count); - end_graphics_drawing(); + EndDrawing(); } - close_graphics(); + CloseWindow(); return EXIT_SUCCESS; } diff --git a/src/physics.c b/src/physics.c index f61e0c8..d8e9ec9 100644 --- a/src/physics.c +++ b/src/physics.c @@ -3,14 +3,17 @@ * @brief All things related to physics * @date 2026-04-27 */ +#include #include #include -#include -#include #include -#include -#include "physics.h" +#include +#include #include "common.h" +#include "physics.h" + +#define OBJ_MAX_SPEED 300.0f // obviously a float, so beware +#define GROUND_OFFSET 30.0f /** @brief iterate through all objects and check whether they are grabbed or registered @@ -26,8 +29,11 @@ void init_physics(object_t* world, uint32_t max_objs) /** @brief uses the SAT to check whether a collision between two objects is happening */ -bool check_collision(object_t* obj1, object_t* obj2) +bool check_collision(object_t* obj1, object_t* obj2, Vector2* out_n, float* out_depth) { + float min_overlap = INFINITY; + Vector2 best_axis = {0}; + for(u32 shape = 0; shape < 2; shape++) { object_t* obj = (shape == 0) ? obj1 : obj2; @@ -52,38 +58,114 @@ bool check_collision(object_t* obj1, object_t* obj2) } if(a_max < b_min || b_max < a_min) return false; + + float overlap = fminf(a_max, b_max) - fmaxf(a_min, b_min); + if(overlap < min_overlap) { + min_overlap = overlap; + best_axis = axis; + } } } + Vector2 dir = Vector2Subtract(obj2->pos, obj1->pos); + if(Vector2DotProduct(dir, best_axis) < 0) { + best_axis = Vector2Negate(best_axis); + } + *out_n = best_axis; + *out_depth = min_overlap; return true; } -void resolve_collision(object_t* obj1, object_t* obj2, Vector2 normal) +void resolve_collision(object_t* obj1, object_t* obj2, Vector2 normal, float out_depth) +{ + Vector2 rvel = Vector2Subtract(obj1->vel, obj2->vel); + float vel = Vector2DotProduct(rvel, normal); + + // Early exist options for too small values + if(vel > 0.0f) return; + + //float e = fminf(obj1->elast, obj2->elast); + float e = FORCE_RESITUTION_DEFAULT; + float inv_m1 = 1.0f/obj1->mass; + float inv_m2 = 1.0f/obj2->mass; + float j = -(1.0f + e) * vel; + j /= (inv_m1 + inv_m2); + + // correction + Vector2 corr = Vector2Scale(normal, out_depth * 0.8f); + obj1->pos = Vector2Subtract(obj1->pos, Vector2Scale(corr, inv_m1)); + obj2->pos = Vector2Add(obj2->pos, Vector2Scale(corr, inv_m2)); + obj1->vel = Vector2Scale(obj1->vel, FORCE_LINEAR_DAMPING_DEFAULT); + obj2->vel = Vector2Scale(obj2->vel, FORCE_LINEAR_DAMPING_DEFAULT); +} + +void update_shape(object_t* obj) { - Vector2 rel_vel = Vector2Subtract(obj1->vel, obj2->vel); - float vel_along = Vector2DotProduct(rel_vel, normal); - if(vel_along > 0) return; + float c = cosf(obj->angle); + float s = sinf(obj->angle); + + for(u32 iter = 0; iter < obj->vertex_n; iter++) { + Vector2 v = obj->local_vertices[iter]; + + obj->vertices[iter] = (Vector2){ // rotate and translate + v.x * c - v.y * s + obj->pos.x, + v.x * s - v.y * c + obj->pos.y + }; + } + + for(u32 iter = 0; iter < obj->vertex_n; iter++) { + Vector2 a = obj->vertices[iter]; + Vector2 b = obj->vertices[(iter + 1) % obj->vertex_n]; + obj->edges[iter] = Vector2Subtract(b, a); + } } void update_physics(object_t* world, u32 objs_count, float gravity, float dt) { - const u32 screen_width = GetScreenWidth(); - const u32 screen_height = GetScreenHeight(); + float left = 0.0f; + float right = (float)GetScreenWidth(); + float top = 0.0f; + float bottom = (float)GetScreenHeight() - GROUND_OFFSET; for(u32 iter = 0; iter < objs_count; iter++) { - object_t* obj = &world[iter]; - - // Clamp objects to the window boundaries if they move outside - obj->pos.x = Clamp(obj->pos.x, 0.0f, (float)screen_width); - obj->pos.y = Clamp(obj->pos.y, 0.0f, (float)screen_height); + object_t* obj1 = &world[iter]; + + // here it's separate if statements because ALL these must be checked, + // so else if wouldn't work here + if (obj1->pos.x < left) { obj1->pos.x = left; obj1->vel.x *= -obj1->elast; } + if (obj1->pos.x > right) { obj1->pos.x = right; obj1->vel.x *= -obj1->elast; } + if (obj1->pos.y < top) { obj1->pos.y = top; obj1->vel.y *= -obj1->elast; } + if (obj1->pos.y > bottom) { obj1->pos.y = bottom; obj1->vel.y *= -obj1->elast; } + + obj1->vel.y += gravity * dt; + // small global damping to prevent explosive movements + obj1->vel = Vector2Scale(obj1->vel, FORCE_LINEAR_DAMPING_DEFAULT); + obj1->vel = Vector2ClampValue(obj1->vel, 0.0f, OBJ_MAX_SPEED); + obj1->pos = Vector2Add(obj1->pos, Vector2Scale(obj1->vel, dt)); + + update_shape(obj1); - obj->vel.x += gravity * dt; - obj->pos = Vector2Add(obj->pos, Vector2Scale(obj->vel, dt)); - for(u32 iter2 = 0; iter2 < objs_count; iter2++) { - object_t* obj_a = &world[iter]; - object_t* obj_b = &world[iter++]; - if(check_collision(obj_a, obj_b)) { - + for(u32 iter2 = iter + 1; iter2 < objs_count; iter2++) { + object_t* obj2 = &world[iter2]; + Vector2 normal; + float out_depth; + if(check_collision(obj1, obj2, &normal, &out_depth)) { + resolve_collision(obj1, obj2, normal, out_depth); } } } } + +void generate_random_shape(object_t* obj, float radius) +{ + obj->vertex_n = GetRandomValue(MIN_VERTICES, MAX_VERTICES); + for(u32 iter = 0; iter < obj->vertex_n; iter++) { + float a = (float)iter / obj->vertex_n * 2.0f * PI; + a += GetRandomValue(-30, 30) * DEG2RAD; + float r = radius * (0.4f + (0.4f + (float)GetRandomValue(0, 80)/100.0f)); + + obj->local_vertices[iter] = (Vector2){ + cosf(a) * r, + sinf(a) * r + }; + } +} diff --git a/src/physics.h b/src/physics.h index a191860..e69a0b6 100644 --- a/src/physics.h +++ b/src/physics.h @@ -1,4 +1,7 @@ -// physics.h +/** + * @file physics.h + * @brief Header file with functions for physics.h; physics related, duh + */ #ifndef PHYSICS_H #define PHYSICS_H @@ -6,9 +9,12 @@ #include #include "common.h" - void init_physics(object_t* world, uint32_t max_objs); -bool check_collision(object_t* obj1, object_t* obj2); +bool check_collision(object_t* obj1, object_t* obj2, Vector2* out_n, float* out_depth); +void resolve_collision(object_t* obj1, object_t* obj2, Vector2 normal, float out_depth); +void update_shape(object_t* obj); void update_physics(object_t* world, uint32_t objs_count, float gravity, float dt); void clear_all_physics(object_t* world, uint32_t* count); +void generate_random_shape(object_t* obj, float radius); + #endif // PHYSICS_H -- cgit v1.3