diff --git a/CMakeLists.txt b/CMakeLists.txt index e88e4d6..f57ec57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(${PROJECT_NAME} mechanics.c vec_utils.c gravity.c + magnetism.c ) target_link_libraries(${PROJECT_NAME} diff --git a/consts.h b/consts.h index 8f8e198..4cdbede 100644 --- a/consts.h +++ b/consts.h @@ -1,9 +1,15 @@ #ifndef CONSTS_H_ #define CONSTS_H_ -#define TIMESTEP 10 -#define simx 2 +#define LOOP_TIMESTEP 1000000LL // Wall-clock nanoseconds between updates (1 kHz). +#define PHYSICS_TIMESTEP 100.0f // Simulated seconds per update. +#define WALL_COLLISIONS 0 // Enable for bounded collision tests. #define NEWTON_G 6.674e-11 +#define EARTH_MASS 5.9722e24 // Kilograms per simulation mass unit. #define UA 149.6E6 -#define RT 6378.14 +#define RT 6378.14 // Kilometers per simulation distance unit. +#define DISTANCE_UNIT_METERS (RT * 1000.0) +#define SIMULATION_G (NEWTON_G * EARTH_MASS / \ + (DISTANCE_UNIT_METERS * DISTANCE_UNIT_METERS * \ + DISTANCE_UNIT_METERS)) #endif // CONSTS_H_ diff --git a/gravity.c b/gravity.c index eb254c3..06b161a 100644 --- a/gravity.c +++ b/gravity.c @@ -4,7 +4,7 @@ #include void gravity(universe_t *uni) { - const float dt = 1.0f / simx; + const float dt = PHYSICS_TIMESTEP; for (unsigned int i = 0; i < uni->num_bodies; i++) { mass_body_t *body_1 = &uni->bodies[i]; for (unsigned int y = i + 1; y < uni->num_bodies; y++) { @@ -15,7 +15,7 @@ void gravity(universe_t *uni) { continue; } Vector2 direction = vec2_normalize(separation); - float factor = NEWTON_G * dt / distance_squared; + float factor = SIMULATION_G * dt / distance_squared; body_1->speed = vec2_sum(body_1->speed, vec2_scale(direction, factor * body_2->mass)); body_2->speed = vec2_sum(body_2->speed, diff --git a/magnetism.c b/magnetism.c index b49e8c8..4d7f0d7 100644 --- a/magnetism.c +++ b/magnetism.c @@ -6,19 +6,27 @@ #define EPSILON 8.85418782E-12 void magnetism(universe_t* uni){ - mass_body_t body_1; - mass_body_t body_2; - Vector2 diff, force; - for (unsigned int i = 0; i < uni->num_bodies-1; i++) { - body_1 = uni->bodies[i]; - for (unsigned int y = i + 1; y < uni->num_bodies; y++) { - body_2 = uni->bodies[y]; - diff=vec2_diff(body_2.position,body_1.position); - force = vec2_scale(diff,body_1.charge*body_2.charge/(4*PI*EPSILON*vec2_len(diff))); - body_1.speed=vec2_sum(body_1.speed,vec2_scale(force,1/body_1.mass)); - body_2.speed=vec2_diff(body_2.speed,vec2_scale(force,1/body_2.mass)); - uni->bodies[y] = body_2; + for (unsigned int i = 0; i < uni->num_bodies; i++) { + mass_body_t *body_1 = &uni->bodies[i]; + for (unsigned int y = i + 1; y < uni->num_bodies; y++) { + mass_body_t *body_2 = &uni->bodies[y]; + if (body_1->charge == 0 || body_2->charge == 0) { + continue; + } + Vector2 diff = vec2_diff(body_2->position, body_1->position); + float distance_squared = vec2_dot(diff, diff); + if (distance_squared == 0) { + continue; + } + // Charges are in coulombs; convert acceleration to Earth radii/s^2. + float factor = PHYSICS_TIMESTEP * body_1->charge * body_2->charge / + (4 * PI * EPSILON * EARTH_MASS * DISTANCE_UNIT_METERS * + DISTANCE_UNIT_METERS * DISTANCE_UNIT_METERS * distance_squared); + Vector2 impulse = vec2_scale(vec2_normalize(diff), factor); + body_1->speed = vec2_diff(body_1->speed, + vec2_scale(impulse, 1 / body_1->mass)); + body_2->speed = vec2_sum(body_2->speed, + vec2_scale(impulse, 1 / body_2->mass)); } - uni->bodies[i] = body_1; } } diff --git a/main.c b/main.c index 95a189e..ed98bc6 100644 --- a/main.c +++ b/main.c @@ -1,17 +1,16 @@ #define _POSIX_C_SOURCE 200809L #include "consts.h" #include "gravity.h" -#include "mechanics.h" #include "magnetism.h" +#include "mechanics.h" #include "objects.h" #include "vec_utils.h" -#include +#include #include #include #include #include #include -#include #include #include @@ -23,33 +22,31 @@ static inline int center_y_text(int height, int size) { return (height - size) / 2; } -long long timespec_diff_ns(struct timespec end, struct timespec start) { - return (end.tv_sec - start.tv_sec) * 1000000000LL + - (end.tv_nsec - start.tv_nsec); -} - void *physics(void *args) { universe_t *uni = (universe_t *)args; - struct timespec ot, ct; + struct timespec next_step; + clock_gettime(CLOCK_MONOTONIC, &next_step); while (true) { - clock_gettime(CLOCK_MONOTONIC, &ct); - gravity(uni); - mechanics(uni); - magnetism(uni); - ot = ct; - long long elapsed = timespec_diff_ns(ct, ot); - long long remaining = TIMESTEP / simx - elapsed; - - if (remaining > 0) { - long long sleep_time = remaining * 9 / 10; - - struct timespec sleep_time_spec = {0, sleep_time}; - thrd_sleep(&sleep_time_spec, NULL); + 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) { } - - do { - clock_gettime(CLOCK_MONOTONIC, &ct); - } while (timespec_diff_ns(ct, ot) < TIMESTEP / simx); } } @@ -60,22 +57,27 @@ int main(void) { const char *welcome_text = "Welcome to Fucked Physics!"; universe_t universe = {0, NULL}; universe.bodies = malloc(1024 * sizeof(mass_body_t)); - universe.num_bodies = 5; + universe.num_bodies = 7; universe.bodies[0] = - (mass_body_t){330432.0f, 696, {-0, 0}, {0, 0}, 696}; - universe.bodies[1] = - (mass_body_t){0.055f, 2.439, {0.387*UA/RT, 0}, {0, -38.86/RT}, 50}; - universe.bodies[2] = - (mass_body_t){0.815f, 0.1, {0.723*UA/RT, 0}, {0, -34.79/RT}, 50}; - universe.bodies[3] = - (mass_body_t){1.0f, 0.1, {1*UA/RT, 0}, {0, -29.29/RT}, 50}; - universe.bodies[4] = - (mass_body_t){0.107f, 200, {1.524*UA/RT, 0}, {0, -21.97/RT}, 200}; - universe.bodies[5] = - (mass_body_t){317.83f, 0.1, {5.203*UA/RT, 0}, {0, -12.43/RT}, 50}; - universe.bodies[6] = - (mass_body_t){95.159f, 200, {9.582*UA/RT, 0}, {0, -9.11/RT}, 200}; + (mass_body_t){330432.0f, 696340 / RT, {0, 0}, {0, 0}, 600, 0, "Sun"}; + universe.bodies[1] = (mass_body_t){ + 0.055f, 2439.7 / RT, {0.387 * UA / RT, 0}, {0, -38.86 / RT}, 80, + 0, "Mercury"}; + universe.bodies[2] = (mass_body_t){ + 0.815f, 6051.8 / RT, {0.723 * UA / RT, 0}, {0, -34.79 / RT}, 190, + 0, "Venus"}; + universe.bodies[3] = (mass_body_t){ + 1.0f, 1.0f, {1 * UA / RT, 0}, {0, -29.29 / RT}, 200, 0, "Earth"}; + universe.bodies[4] = (mass_body_t){ + 0.107f, 3389.5 / RT, {1.524 * UA / RT, 0}, {0, -21.97 / RT}, 106, + 0, "Mars"}; + universe.bodies[5] = (mass_body_t){ + 317.83f, 69911 / RT, {5.203 * UA / RT, 0}, {0, -12.43 / RT}, 400, + 0, "Jupiter"}; + universe.bodies[6] = (mass_body_t){ + 95.159f, 58232 / RT, {9.582 * UA / RT, 0}, {0, -9.11 / RT}, 300, + 0, "Saturn"}; SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(800, 450, "Fucked Physics"); SetTargetFPS(120); @@ -116,14 +118,18 @@ int main(void) { } } while (key_pressed != 0); - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || - IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) { + 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}; + (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++; } @@ -153,10 +159,15 @@ int main(void) { Vector2 scaled_offset = vec2_scale(canvas_offset, v_scale); Vector2 body_position = vec2_scale(universe.bodies[i].position, v_scale); body_position = vec2_sum(body_position, scaled_offset); - DrawCircleV(transform_to_center(body_position), - universe.bodies[i].draw_radius * v_scale, + body_position = transform_to_center(body_position); + float draw_radius = universe.bodies[i].draw_radius * v_scale; + 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), diff --git a/mechanics.c b/mechanics.c index 9c0857e..491187d 100644 --- a/mechanics.c +++ b/mechanics.c @@ -73,25 +73,23 @@ void collide_rigid(mass_body_t *a, float restitution) { } void mechanics(universe_t *uni) { - mass_body_t body_1; - mass_body_t body_2; - Vector2 collision; - Vector2 scaled_speed; + // Advance every body to the same simulated time before checking contacts. for (unsigned int i = 0; i < uni->num_bodies; i++) { - body_1 = uni->bodies[i]; - scaled_speed = vec2_scale(body_1.speed, 1.0 / simx); - body_1.position = vec2_sum(body_1.position, scaled_speed); - if (i < uni->num_bodies - 1) { - for (unsigned int y = i + 1; y < uni->num_bodies; y++) { - body_2 = uni->bodies[y]; - collision = body_collision(&body_1, &body_2); - if (!vec2_is_null(collision)) { - collide(&body_1, &body_2, collision, 0.99); - } - uni->bodies[y] = body_2; + mass_body_t *body = &uni->bodies[i]; + body->position = vec2_sum(body->position, + vec2_scale(body->speed, PHYSICS_TIMESTEP)); + } + for (unsigned int i = 0; i < uni->num_bodies; i++) { + mass_body_t *body_1 = &uni->bodies[i]; + for (unsigned int y = i + 1; y < uni->num_bodies; y++) { + mass_body_t *body_2 = &uni->bodies[y]; + Vector2 collision = body_collision(body_1, body_2); + if (!vec2_is_null(collision)) { + collide(body_1, body_2, collision, 0.99); } } - collide_rigid(&body_1, 1.1); - uni->bodies[i] = body_1; + if (WALL_COLLISIONS) { + collide_rigid(body_1, 1.0f); + } } } diff --git a/objects.h b/objects.h index 8b38d1b..d25bbe3 100644 --- a/objects.h +++ b/objects.h @@ -3,12 +3,13 @@ #include "vec_utils.h" typedef struct { - float mass; - float radius; - Vector2 position; - Vector2 speed; + float mass; // Earth masses. + float radius; // Earth radii (physical collision radius). + Vector2 position; // Earth radii. + Vector2 speed; // Earth radii per simulated second. float draw_radius; - float charge; + float charge; // Coulombs. + char name[32]; } mass_body_t; typedef struct universe {