aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.h71
-rw-r--r--src/graphics.c24
-rw-r--r--src/graphics.h10
-rw-r--r--src/main.c85
-rw-r--r--src/physics.c128
-rw-r--r--src/physics.h12
6 files changed, 203 insertions, 127 deletions
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 <stdint.h>
-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 <raylib.h>
+#include <raymath.h>
+#include <math.h>
+#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 <stdint.h>
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 <math.h>
#include <raylib.h>
#include <stdbool.h>
@@ -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 <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
+ };
+ }
+}
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 <time.h>
#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