aboutsummaryrefslogtreecommitdiffstats
path: root/src/physics.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/physics.c')
-rw-r--r--src/physics.c128
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
+ };
+ }
+}