diff --git a/CelestialObject.h b/CelestialObject.h index 1732f78a5383e3a85ef62d4de9e25aa889523816..266f603f86c9fda21c33d15efb527a15374a62b8 100644 --- a/CelestialObject.h +++ b/CelestialObject.h @@ -2,7 +2,6 @@ #define CELESTIAL_OBJECT_H #include "Vector2.h" -#include "gfx/gfx.h" typedef struct CelestialObject CelestialObject; typedef struct CelestialObject { diff --git a/Makefile b/Makefile index b8b51028b0c6945ea9c5a358e5a1e2d25742298b..602e97af934bbc6076aeb65c69f807fd88cb6a2b 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,13 @@ TARGET = main CC:=gcc # CFLAGS:=-g -Ofast -Wall -Wextra -fsanitize=address -fsanitize=leak -std=gnu11 # CFLAGS:=-fsanitize=address -CFLAGS:=-g -Ofast -Wall -Wextra -std=gnu11 -LDFLAGS:=-lm -lSDL2 -VPATH:=gfx +CFLAGS:=-g -O3 -Wall -Wextra -std=gnu11 +LDFLAGS:=-lm -lGL -lGLU -lglut %.o: %.c $(HEADERS) $(CC) $(CFLAGS) -c $< -o $@ -$(TARGET): main.o Vector2.o CelestialObject.o PlanetarySystem.o gfx.o +$(TARGET): main.o Vector2.o CelestialObject.o PlanetarySystem.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) clean: diff --git a/PlanetarySystem.c b/PlanetarySystem.c index a27f9eed57a0a5d763435501f0f7ecbd1e2629a4..962676b780b3b6ed322d36e17c0bb96b16ad5a91 100644 --- a/PlanetarySystem.c +++ b/PlanetarySystem.c @@ -1,10 +1,11 @@ #include "PlanetarySystem.h" +#include <GL/glut.h> +#include <math.h> #include <stdlib.h> #include "CelestialObject.h" #include "Vector2.h" -#include "gfx/gfx.h" const uint32_t SCREEN_WIDTH = 1000; const uint32_t SCREEN_HEIGHT = 1000; @@ -30,20 +31,19 @@ const double VENUS_SEMI_MAJOR_AXIS = 108.208475 * 1E9; const double EARTH_SEMI_MAJOR_AXIS = 149.598262 * 1E9; const double MARS_SEMI_MAJOR_AXIS = 227.943824 * 1E9; -PlanetarySystem *planetary_system_create(double interval) { +PlanetarySystem *planetary_system_create() { PlanetarySystem *planetary_system = (PlanetarySystem *)malloc(sizeof(PlanetarySystem)); planetary_system->objects_length = 6; planetary_system->objects = (CelestialObject **)malloc(sizeof(PlanetarySystem *) * planetary_system->objects_length); - planetary_system->interval = interval; planetary_system->zoom_factor = 1; planetary_system->reference_frame_object_index = 0; - planetary_system->objects[0] = celestial_object_create(SUN_MASS, 0, 0, 50, 0x00FFFFFF); - planetary_system->objects[1] = celestial_object_create(MERCURY_MASS, MERCURY_SEMI_MAJOR_AXIS, MERCURY_ECCENTRICITY, 10, 0x00DBCECA); - planetary_system->objects[2] = celestial_object_create(VENUS_MASS, VENUS_SEMI_MAJOR_AXIS, VENUS_ECCENTRICITY, 20, 0x008B7D82); - planetary_system->objects[3] = celestial_object_create(EARTH_MASS, EARTH_SEMI_MAJOR_AXIS, EARTH_ECCENTRICITY, 20, 0x006b93d6); - planetary_system->objects[4] = celestial_object_create(MARS_MASS, MARS_SEMI_MAJOR_AXIS, MARS_ECCENTRICITY, 12, 0x00BC2732); + planetary_system->objects[0] = celestial_object_create(SUN_MASS, 0, 0, 50, 0xFFFFFF); + planetary_system->objects[1] = celestial_object_create(MERCURY_MASS, MERCURY_SEMI_MAJOR_AXIS, MERCURY_ECCENTRICITY, 10, 0xDBCECA); + planetary_system->objects[2] = celestial_object_create(VENUS_MASS, VENUS_SEMI_MAJOR_AXIS, VENUS_ECCENTRICITY, 20, 0x8B7D82); + planetary_system->objects[3] = celestial_object_create(EARTH_MASS, EARTH_SEMI_MAJOR_AXIS, EARTH_ECCENTRICITY, 20, 0x6b93d6); + planetary_system->objects[4] = celestial_object_create(MARS_MASS, MARS_SEMI_MAJOR_AXIS, MARS_ECCENTRICITY, 12, 0xBC2732); planetary_system->objects[5] = celestial_object_create(7.34767309 * 1E22, 384.399 * 1E6, 0.0549, 5, 0x8A2BE2); planetary_system->objects[5]->current_position.x += planetary_system->objects[3]->current_position.x; @@ -68,7 +68,7 @@ Vector2 calculate_gravitational_acceleration(PlanetarySystem *planetary_system, return a; } -void planetary_system_update(PlanetarySystem *planetary_system) { +void planetary_system_update(PlanetarySystem *planetary_system, double interval) { for (uint32_t i = 1; i < planetary_system->objects_length; i += 1) { CelestialObject *object = planetary_system->objects[i]; Vector2 current_position = object->current_position; @@ -84,20 +84,19 @@ void planetary_system_update(PlanetarySystem *planetary_system) { double periapsis_velocity_scalar = sqrt((G * star->mass * (1 + object->eccentricity)) / (object->semi_major_axis * (1 - object->eccentricity))); if (i == 5) { - printf("%lf\n", periapsis_velocity_scalar); periapsis_velocity_scalar += 30290.322245; } Vector2 r = vector2_normalize(vector2_create(current_position.y, -current_position.x)); Vector2 periapsis_velocity = vector2_multiply(r, periapsis_velocity_scalar); - new_position = vector2_add(current_position, vector2_multiply(periapsis_velocity, planetary_system->interval)); + new_position = vector2_add(current_position, vector2_multiply(periapsis_velocity, interval)); Vector2 a = calculate_gravitational_acceleration(planetary_system, i); - new_position = vector2_add(new_position, vector2_multiply(a, 0.5 * pow(planetary_system->interval, 2))); + new_position = vector2_add(new_position, vector2_multiply(a, 0.5 * pow(interval, 2))); } else { new_position = vector2_substract(vector2_multiply(current_position, 2), object->previous_position); Vector2 a = calculate_gravitational_acceleration(planetary_system, i); - new_position = vector2_add(new_position, vector2_multiply(a, pow(planetary_system->interval, 2))); + new_position = vector2_add(new_position, vector2_multiply(a, pow(interval, 2))); } object->previous_position = object->current_position; @@ -106,20 +105,29 @@ void planetary_system_update(PlanetarySystem *planetary_system) { } Vector2 scale_position(Vector2 position) { - Vector2 scaled_position = vector2_multiply(position, 1.0 / (250 * 1E9)); + Vector2 scaled_position = vector2_multiply(position, 1.0 / (260 * 1E9)); scaled_position = vector2_fit_canvas(scaled_position, SCREEN_WIDTH, SCREEN_HEIGHT); return scaled_position; } -void planetary_system_draw(PlanetarySystem *planetary_system, struct gfx_context_t *context) { - gfx_clear(context, COLOR_BLACK); - +void planetary_system_draw(PlanetarySystem *planetary_system) { for (uint32_t i = 0; i < planetary_system->objects_length; i += 1) { CelestialObject *object = planetary_system->objects[i]; - Vector2 tmp = vector2_substract(object->current_position, planetary_system->objects[planetary_system->reference_frame_object_index]->current_position); - tmp = vector2_multiply(tmp, planetary_system->zoom_factor); - Vector2 scaled_position = scale_position(tmp); + Vector2 unscaled_position = vector2_substract(object->current_position, planetary_system->objects[planetary_system->reference_frame_object_index]->current_position); + unscaled_position = vector2_multiply(unscaled_position, planetary_system->zoom_factor); + Vector2 scaled_position = scale_position(unscaled_position); + + uint32_t color = object->drawing_color; + glColor3ub((color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF) >> 0); + glBegin(GL_POLYGON); + + for (uint32_t i = 0; i < 360; i += 1) { + double theta = i * 3.1415 / 180; + double x = scaled_position.x + object->drawing_disc_radius * cos(theta); + double y = scaled_position.y + object->drawing_disc_radius * sin(theta); + glVertex2f(x, y); + } - draw_full_circle(context, scaled_position.x, scaled_position.y, object->drawing_disc_radius, object->drawing_color); + glEnd(); } } diff --git a/PlanetarySystem.h b/PlanetarySystem.h index 4fdf97fe53d19402d65404dbf1e577d23a003fe7..7c7f12c4b45ded30596fdb4cfa28bfcd2200a81a 100644 --- a/PlanetarySystem.h +++ b/PlanetarySystem.h @@ -4,7 +4,6 @@ #include <stdint.h> #include "CelestialObject.h" -#include "gfx/gfx.h" const uint32_t SCREEN_WIDTH; const uint32_t SCREEN_HEIGHT; @@ -17,8 +16,8 @@ typedef struct { int32_t reference_frame_object_index; } PlanetarySystem; -PlanetarySystem *planetary_system_create(double interval); -void planetary_system_update(PlanetarySystem *planetary_system); -void planetary_system_draw(PlanetarySystem *planetary_system, struct gfx_context_t *context); +PlanetarySystem *planetary_system_create(); +void planetary_system_update(PlanetarySystem *planetary_system, double interval); +void planetary_system_draw(PlanetarySystem *planetary_system); #endif diff --git a/README b/README new file mode 100644 index 0000000000000000000000000000000000000000..59f8fb0015dfe2f35550678a47552639c71997f0 --- /dev/null +++ b/README @@ -0,0 +1 @@ +sudo apt-get install freeglut3-dev diff --git a/gfx/gfx.c b/gfx/gfx.c deleted file mode 100644 index 3821077bbbf7964c49e73b9b94d6dc123bcb9d9f..0000000000000000000000000000000000000000 --- a/gfx/gfx.c +++ /dev/null @@ -1,150 +0,0 @@ -/// @file gfx.c -/// @author Florent Gluck -/// @date November 6, 2016 -/// Helper routines to render pixels in fullscreen graphic mode. -/// Uses the SDL2 library. -/// Update : Add full circle -/// @author Michaƫl El Kharroubi -/// @date 19 November 2021 - -#include "gfx.h" -#include <assert.h> - -/// Create a fullscreen graphic window. -/// @param title Title of the window. -/// @param width Width of the window in pixels. -/// @param height Height of the window in pixels. -/// @return a pointer to the graphic context or NULL if it failed. -struct gfx_context_t *gfx_create(char *title, uint32_t width, uint32_t height) -{ - if (SDL_Init(SDL_INIT_VIDEO) != 0) - goto error; - SDL_Window *window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_RESIZABLE); - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0); - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, width, height); - uint32_t *pixels = malloc(width * height * sizeof(uint32_t)); - struct gfx_context_t *ctxt = malloc(sizeof(struct gfx_context_t)); - - if (!window || !renderer || !texture || !pixels || !ctxt) - goto error; - - ctxt->renderer = renderer; - ctxt->texture = texture; - ctxt->window = window; - ctxt->width = width; - ctxt->height = height; - ctxt->pixels = pixels; - - SDL_ShowCursor(SDL_DISABLE); - gfx_clear(ctxt, COLOR_BLACK); - return ctxt; - -error: - return NULL; -} - -/// Draw a pixel in the specified graphic context. -/// @param ctxt Graphic context where the pixel is to be drawn. -/// @param column X coordinate of the pixel. -/// @param row Y coordinate of the pixel. -/// @param color Color of the pixel. -void gfx_putpixel(struct gfx_context_t *ctxt, uint32_t column, uint32_t row, uint32_t color) -{ - if (column < ctxt->width && row < ctxt->height) - ctxt->pixels[ctxt->width * row + column] = color; -} - -/// Clear the specified graphic context. -/// @param ctxt Graphic context to clear. -/// @param color Color to use. -void gfx_clear(struct gfx_context_t *ctxt, uint32_t color) -{ - int n = ctxt->width * ctxt->height; - while (n) - ctxt->pixels[--n] = color; -} - -/// Display the graphic context. -/// @param ctxt Graphic context to clear. -void gfx_present(struct gfx_context_t *ctxt) -{ - SDL_UpdateTexture( - ctxt->texture, NULL, ctxt->pixels, ctxt->width * sizeof(uint32_t)); - SDL_RenderCopy(ctxt->renderer, ctxt->texture, NULL, NULL); - SDL_RenderPresent(ctxt->renderer); -} - -/// Destroy a graphic window. -/// @param ctxt Graphic context of the window to close. -void gfx_destroy(struct gfx_context_t *ctxt) -{ - SDL_ShowCursor(SDL_ENABLE); - SDL_DestroyTexture(ctxt->texture); - SDL_DestroyRenderer(ctxt->renderer); - SDL_DestroyWindow(ctxt->window); - free(ctxt->pixels); - ctxt->texture = NULL; - ctxt->renderer = NULL; - ctxt->window = NULL; - ctxt->pixels = NULL; - SDL_Quit(); - free(ctxt); -} - -/// If a key was pressed, returns its key code (non blocking call). -/// List of key codes: https://wiki.libsdl.org/SDL_Keycode -/// @return the key that was pressed or 0 if none was pressed. -SDL_Keycode gfx_keypressed() -{ - SDL_Event event; - if (SDL_PollEvent(&event)) - { - if (event.type == SDL_KEYDOWN) - return event.key.keysym.sym; - } - return 0; -} - -/// Draw a full circle using Andres's discrete circle algorithm. -/// @param ctxt Graphic context to clear. -/// @param c_column X coordinate of the circle center. -/// @param c_row Y coordinate of the circle center. -/// @param r The radius of circle (in pixels). -/// @param color Color to use. -void draw_full_circle(struct gfx_context_t *ctxt, uint32_t c_column, uint32_t c_row, uint32_t r, uint32_t color) -{ - int32_t x = 0, y = r, d = r - 1; - while (y >= x) - { - gfx_putpixel(ctxt, c_column + x, c_row + y, color); - gfx_putpixel(ctxt, c_column + y, c_row + x, color); - gfx_putpixel(ctxt, c_column - x, c_row + y, color); - gfx_putpixel(ctxt, c_column - y, c_row + x, color); - - gfx_putpixel(ctxt, c_column + x, c_row - y, color); - gfx_putpixel(ctxt, c_column + y, c_row - x, color); - gfx_putpixel(ctxt, c_column - x, c_row - y, color); - gfx_putpixel(ctxt, c_column - y, c_row - x, color); - - if ((2 * x) <= d) - { - d -= 2 * x + 1; - x += 1; - } - else if (d < (2 * (((int32_t)r) - y))) - { - d += 2 * y - 1; - y -= 1; - } - else - { - d -= 2 * (x - y + 1); - y -= 1; - x += 1; - } - } - if (r > 0) - draw_full_circle(ctxt, c_column, c_row, r - 1, color); -} diff --git a/gfx/gfx.h b/gfx/gfx.h deleted file mode 100644 index 20b7815e4c5ef7a8daa871153c3f4affeeb448b6..0000000000000000000000000000000000000000 --- a/gfx/gfx.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _GFX_H_ -#define _GFX_H_ - -#include <SDL2/SDL.h> -#include <stdbool.h> -#include <stdint.h> - -#define MAKE_COLOR(r, g, b) \ - ((uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16)) - -#define COLOR_GET_B(color) (color & 0xff) -#define COLOR_GET_G(color) ((color >> 8) & 0xff) -#define COLOR_GET_R(color) ((color >> 16) & 0xff) - -#define COLOR_BLACK 0x00000000 -#define COLOR_RED 0x00FF0000 -#define COLOR_GREEN 0x0000FF00 -#define COLOR_BLUE 0x000000FF -#define COLOR_WHITE 0x00FFFFFF -#define COLOR_YELLOW 0x00FFFF00 - -struct gfx_context_t -{ - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Texture *texture; - uint32_t *pixels; - uint32_t width; - uint32_t height; -}; - -extern void gfx_putpixel( - struct gfx_context_t *ctxt, uint32_t column, uint32_t row, uint32_t color); -extern void gfx_clear(struct gfx_context_t *ctxt, uint32_t color); -extern struct gfx_context_t *gfx_create(char *text, uint32_t width, uint32_t height); -extern void gfx_destroy(struct gfx_context_t *ctxt); -extern void gfx_present(struct gfx_context_t *ctxt); -extern SDL_Keycode gfx_keypressed(); -extern void draw_full_circle(struct gfx_context_t *ctxt, uint32_t c_column, uint32_t c_row, uint32_t r, uint32_t color); - -#endif diff --git a/main.c b/main.c index 336ce592e500fbec6127a6a4f355b9169eacbe12..31e44e23080359a24451f75d87596366adbd2cdf 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,4 @@ +#include <GL/glut.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -5,79 +6,115 @@ #include "PlanetarySystem.h" #include "Vector2.h" -#include "gfx/gfx.h" -int main() { - // srand(time(NULL)); - // srand(0); +#define WINDOW_NAME "Solar System" +#define REFRESH_RATE 100 +#define UPDATE_RATE 10000 +#define TIME_ELASPING_PER_SECOND 3600 * 24 * 30 + +// https://stackoverflow.com/questions/3417837/what-is-the-best-way-to-suppress-a-unused-variable-x-warning +#ifdef UNUSED +#elif defined(__GNUC__) +#define UNUSED(x) UNUSED_##x __attribute__((unused)) +#elif defined(__LCLINT__) +#define UNUSED(x) /*@unused@*/ x +#else +#define UNUSED(x) x +#endif + +PlanetarySystem *planetary_system; +uint32_t elapsed_time = 0; +uint32_t true_time_shift = 0; + +void draw_timer() { + glutPostRedisplay(); + glutTimerFunc(1000 / REFRESH_RATE, draw_timer, 0); + elapsed_time += 1; +} + +void update_timer() { + for (uint32_t i = 0; i < UPDATE_RATE / REFRESH_RATE; i += 1) { + planetary_system_update(planetary_system, (double)TIME_ELASPING_PER_SECOND / UPDATE_RATE); + } - struct gfx_context_t *context = gfx_create("Solar System", SCREEN_WIDTH, SCREEN_HEIGHT); + glutTimerFunc(1000 / REFRESH_RATE, update_timer, 0); +} + +void draw() { + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); - if (!context) { - fprintf(stderr, "Graphics initialization failed!\n"); - return EXIT_FAILURE; + if (elapsed_time % REFRESH_RATE == 0) { + char title[100]; + sprintf(title, "Solar System (%d : %ld)", elapsed_time / REFRESH_RATE, (time(NULL) - true_time_shift) % 1000); + glutSetWindowTitle(title); } - int32_t time_elapsing_per_second = 3600 * 24 * 5; - int32_t refresh_rate = 240; - int32_t elapsed_time = 0; - double sleep_duration = 1.0 / refresh_rate * 1E9; - printf("%lf\n", (double)time_elapsing_per_second / refresh_rate); + planetary_system_draw(planetary_system); - PlanetarySystem *planetary_system = planetary_system_create((double)time_elapsing_per_second / refresh_rate); + glFlush(); +} - int64_t tmp = time(NULL) % 1000; +void handle_keyboard_input(unsigned char key, int UNUSED(x), int UNUSED(y)) { + if (key == 27) { + exit(0); + } +} - while (true) { - planetary_system_update(planetary_system); - gfx_present(context); - planetary_system_draw(planetary_system, context); +void handle_special_keyboard_input(int key, int UNUSED(x), int UNUSED(y)) { + if (key == GLUT_KEY_LEFT) { + planetary_system->reference_frame_object_index -= 1; - if (elapsed_time % refresh_rate == 0) { - char title[100]; - sprintf(title, "Solar System (%d : %ld)", elapsed_time / refresh_rate, (time(NULL) - tmp) % 1000); - SDL_SetWindowTitle(context->window, title); + if (planetary_system->reference_frame_object_index < 0) { + planetary_system->reference_frame_object_index = planetary_system->objects_length - 1; } + } - SDL_Event event; - if (SDL_PollEvent(&event)) { - if (event.type == SDL_MOUSEWHEEL) { - if (event.wheel.y > 0) { - planetary_system->zoom_factor *= 1.1; - } else if (event.wheel.y < 0) { - planetary_system->zoom_factor /= 1.1; - - if (planetary_system->zoom_factor < 1) { - planetary_system->zoom_factor = 1; - } - } - } - - if (event.type == SDL_KEYDOWN) { - if (event.key.keysym.sym == SDLK_ESCAPE) { - break; - } + if (key == GLUT_KEY_RIGHT) { + planetary_system->reference_frame_object_index += 1; + planetary_system->reference_frame_object_index %= planetary_system->objects_length; + } +} - if (event.key.keysym.sym == SDLK_LEFT) { - planetary_system->reference_frame_object_index -= 1; +void handle_mouse_input(int button, int state, int UNUSED(x), int UNUSED(y)) { + if (button == 3 || button == 4) { + if (state == GLUT_UP) return; - if (planetary_system->reference_frame_object_index < 0) { - planetary_system->reference_frame_object_index = planetary_system->objects_length - 1; - } - } + if (button == 3) { + planetary_system->zoom_factor *= 1.1; + } else { + planetary_system->zoom_factor /= 1.1; - if (event.key.keysym.sym == SDLK_RIGHT) { - planetary_system->reference_frame_object_index += 1; - planetary_system->reference_frame_object_index %= planetary_system->objects_length; - } + if (planetary_system->zoom_factor < 1) { + planetary_system->zoom_factor = 1; } } - - struct timespec t = {.tv_sec = 0, .tv_nsec = sleep_duration}; - nanosleep(&t, NULL); - elapsed_time += 1; } +} + +int main(int argc, char *argv[]) { + planetary_system = planetary_system_create(); + + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); + glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT); + glutCreateWindow(WINDOW_NAME); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0); + + glutDisplayFunc(draw); + glutKeyboardFunc(handle_keyboard_input); + glutSpecialFunc(handle_special_keyboard_input); + glutMouseFunc(handle_mouse_input); + + true_time_shift = time(NULL) % 1000; + glutTimerFunc(1000 / REFRESH_RATE, update_timer, 0); + glutTimerFunc(1000 / REFRESH_RATE, draw_timer, 0); + + glutMainLoop(); - gfx_destroy(context); return EXIT_SUCCESS; }