diff options
Diffstat (limited to 'src/physics.c')
| -rw-r--r-- | src/physics.c | 128 |
1 files changed, 105 insertions, 23 deletions
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 <math.h> #include <raylib.h> #include <raymath.h> -#include <time.h> -#include <string.h> #include <stdlib.h> -#include <math.h> -#include "physics.h" +#include <string.h> +#include <time.h> #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 + }; + } +} |
