#define _POSIX_C_SOURCE 200809L #include "consts.h" #include "gravity.h" #include "magnetism.h" #include "mechanics.h" #include "objects.h" #include "vec_utils.h" #include #include #include #include #include #include #include #include #include int center_x_text(const char *text, int width, int size) { return (width - MeasureText(text, size)) / 2; } static inline int center_y_text(int height, int size) { return (height - size) / 2; } void *physics(void *args) { universe_t *uni = (universe_t *)args; struct timespec next_step; clock_gettime(CLOCK_MONOTONIC, &next_step); while (true) { next_step.tv_sec += LOOP_TIMESTEP / 1000000000LL; next_step.tv_nsec += LOOP_TIMESTEP % 1000000000LL; if (next_step.tv_nsec >= 1000000000L) { next_step.tv_nsec -= 1000000000L; next_step.tv_sec++; } gravity(uni); magnetism(uni); mechanics(uni); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); if (now.tv_sec > next_step.tv_sec || (now.tv_sec == next_step.tv_sec && now.tv_nsec > next_step.tv_nsec)) { next_step = now; // An overrun starts a fresh schedule, without catch-up steps. } // Sleep only until the deadline; processing time is already included. while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_step, NULL) == EINTR) { } } } int main(int argc, char **argv) { if (argc != 2) { printf("No config provided\n"); return EXIT_FAILURE; } FILE *file = fopen(argv[1], "r"); if (file == NULL) { printf("File not found"); return EXIT_FAILURE; } int cam_lock = -1; float v_scale = 1; Vector2 canvas_offset = {0, 0}; pthread_t physics_thread; const char *welcome_text = "Welcome to Fucked Physics!"; universe_t universe = {0, NULL}; universe.bodies = malloc(1024 * sizeof(mass_body_t)); universe.num_bodies = 0; char line[1024]; fgets(line, 1024, file); while (fgets(line, 1024, file)) { printf("%s", line); float mass, radius, x, y, vx, vy, draw_radius, charge; char name[32]; sscanf(line, "%f, %f, {%f, %f}, {%f, %f}, %f, %f, %31s", &mass, &radius, &x, &y, &vx, &vy, &draw_radius, &charge, name); universe.bodies[universe.num_bodies] = (mass_body_t){ .mass = mass, .radius = radius / EARTH_RADIUS, .position = (Vector2){x * UA / EARTH_RADIUS, y * UA / EARTH_RADIUS}, .speed = (Vector2){vx / EARTH_RADIUS, vy / EARTH_RADIUS}, .draw_radius = draw_radius / EARTH_RADIUS, .charge = charge}; memcpy((void *)&universe.bodies[universe.num_bodies].name, (void *)&name, 32); universe.num_bodies++; } fclose(file); SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(800, 450, "Fucked Physics"); SetTargetFPS(120); pthread_create(&physics_thread, NULL, physics, (void *)&universe); while (!WindowShouldClose()) { int key_pressed = 0; do { key_pressed = GetKeyPressed(); switch (key_pressed) { case KEY_I: v_scale *= 1.26; break; case KEY_O: v_scale /= 1.26; break; case KEY_LEFT: canvas_offset.x += 100; break; case KEY_RIGHT: canvas_offset.x -= 100; break; case KEY_UP: canvas_offset.y += 100; break; case KEY_DOWN: canvas_offset.y -= 100; break; case KEY_ONE: cam_lock = 1; break; case KEY_TWO: cam_lock = 2; break; case KEY_THREE: cam_lock = 3; break; case KEY_FOUR: cam_lock = 4; break; case KEY_FIVE: cam_lock = 5; break; case KEY_SIX: cam_lock = 6; break; case KEY_SEVEN: cam_lock = 7; break; case KEY_EIGHT: cam_lock = 8; break; case KEY_NINE: cam_lock = 9; break; case KEY_ZERO: cam_lock = 0; break; case KEY_F: cam_lock = -1; } } while (key_pressed != 0); if (universe.num_bodies < 1024 && (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonDown(MOUSE_RIGHT_BUTTON))) { Vector2 mouse_pos = vec2_scale( transform_to_center(vec2_scale(GetMousePosition(), -1)), -1); mouse_pos = vec2_scale(mouse_pos, 1 / v_scale); universe.bodies[universe.num_bodies] = (mass_body_t){100000.0f, 0.1, mouse_pos, {0, 0}, 10, 0, ""}; snprintf(universe.bodies[universe.num_bodies].name, sizeof(universe.bodies[universe.num_bodies].name), "Body %u", universe.num_bodies + 1); universe.num_bodies++; } BeginDrawing(); ClearBackground(RAYWHITE); for (unsigned int i = 0; i < universe.num_bodies; i++) { Vector2 local_offset = canvas_offset; if (cam_lock != -1) { local_offset = vec2_scale(universe.bodies[cam_lock].position, -1); } Vector2 scaled_offset = vec2_scale(local_offset, v_scale); Vector2 body_position = vec2_scale(universe.bodies[i].position, v_scale); body_position = vec2_sum(body_position, scaled_offset); body_position = transform_to_center(body_position); float draw_radius = universe.bodies[i].draw_radius * v_scale; if (draw_radius < 2) { draw_radius = 2; } DrawCircleV(body_position, draw_radius, (Color){((256 / universe.num_bodies) * i) % 256 / 2, 255 * (i % 2) / 2, 255 * (i % 2) * (i % 3) / 2, 255}); // Keep labels readable at every zoom level. DrawText(universe.bodies[i].name, (int)(body_position.x + draw_radius + 6), (int)(body_position.y - 8), 16, BLACK); } // DrawText(welcome_text, center_x_text(welcome_text, GetRenderWidth(), 20), // center_y_text(GetRenderHeight(), 20), 20, BLACK); EndDrawing(); } CloseWindow(); return EXIT_SUCCESS; }