#include #include #include #include #include #include "random.h" #include "planet.h" #define MAX_FPS 60 static void fade_buf(SDL_Surface *screen, double fade) { int i; unsigned int *buf = screen->pixels; unsigned char *b; unsigned char amount; static double threshold; threshold += fade; if (threshold > 1) { amount = threshold; threshold -= amount; } else { return; } for (i = 0; i < screen->pitch * screen->h / sizeof(*buf); i++) { if (!buf[i]) continue; b = (unsigned char *)&buf[i]; *b = *b >= amount ? (*b - amount) : *b; b++; *b = *b >= amount ? (*b - amount) : *b; b++; *b = *b >= amount ? (*b - amount) : *b; } } static void clear_buf(SDL_Surface *screen) { int i; unsigned int *buf = screen->pixels; for (i = 0; i < screen->pitch * screen->h / 4; i++) buf[i] = 0; } /* Return time in microseconds */ static unsigned long gettime(void) { struct timespec tp; unsigned long ret; static unsigned long start; clock_gettime(CLOCK_MONOTONIC, &tp); ret = tp.tv_sec * 1000 * 1000; ret += tp.tv_nsec / 1000; if (!start) start = ret; return ret - start; } struct sim_status { SDL_Surface *screen; struct camera *cam; double time_scale; int tracers_enabled; }; static int poll_events(struct sim_status *status, double time) { static double time_scale_rate = 1; SDL_Event event; struct camera *cam = status->cam; status->time_scale *= pow(time_scale_rate, time); while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: goto quit; case SDLK_LEFT: cam->speed.x = -CAM_SPEED; break; case SDLK_RIGHT: cam->speed.x = CAM_SPEED; break; case SDLK_UP: cam->speed.y = -CAM_SPEED; break; case SDLK_DOWN: cam->speed.y = CAM_SPEED; break; case SDLK_PLUS: case SDLK_q: cam->zoom_rate = CAM_ZOOM_RATE; break; case SDLK_MINUS: case SDLK_w: cam->zoom_rate = 1 / CAM_ZOOM_RATE; break; case SDLK_s: time_scale_rate = 1.5; break; case SDLK_a: time_scale_rate = 1 / 1.5; break; case SDLK_1: status->time_scale = 1; break; case SDLK_2: status->time_scale = 10; break; case SDLK_3: status->time_scale = 20; break; case SDLK_4: status->time_scale = 30; break; case SDLK_0: status->time_scale = 0; break; case SDLK_t: status->tracers_enabled = !status->tracers_enabled; default: break; } break; case SDL_KEYUP: switch (event.key.keysym.sym) { case SDLK_LEFT: cam->speed.x = 0; break; case SDLK_RIGHT: cam->speed.x = 0; break; case SDLK_UP: cam->speed.y = 0; break; case SDLK_DOWN: cam->speed.y = 0; break; case SDLK_PLUS: case SDLK_q: cam->zoom_rate = 1; break; case SDLK_MINUS: case SDLK_w: cam->zoom_rate = 1; break; case SDLK_s: time_scale_rate = 1; break; case SDLK_a: time_scale_rate = 1; break; default: break; } break; case SDL_VIDEORESIZE: status->screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, status->screen->flags); break; case SDL_QUIT: goto quit; } } return 0; quit: printf("\nExiting. Good bye!\n"); return 1; } static void loop(SDL_Surface *screen, int num_of_planets, double total_mass, double range) { struct sim_status status; struct planet *planet, *pl1, *pl2, *planet_root; struct camera camera; struct planet_search_iterator itr; struct vector vect; int planets; int framecount = 0, last_fps_time = 0; int last_framecount = 0, last_step_count = 0; unsigned long old_ticks, ticks = 0, last_frame_tick = 0; unsigned long step_count = 0; double last_fps = 0, last_sps = 0, fade_amount; double step_time = 0, true_time = 0; int visible_planets; init_camera(&camera); status.cam = &camera; status.time_scale = 1; status.screen = screen; status.tracers_enabled = 0; itr.screen = screen; itr.cam = &camera; itr.qt_iterator.direction = planet_search_rectangular; itr.qt_iterator.callback = planet_draw_iterator; planet = malloc(sizeof(*planet)); init_planet(planet); create_planets(planet, num_of_planets, total_mass, range); ticks = SDL_GetTicks(); while (1) { planets = 0; if (status.time_scale > 0) { list_for_each_entry(pl1, &planet->list, list) { pl2 = list_to_planet(pl1->list.next); list_for_each_entry_from(pl2, &planet->list, list) { struct planet *ptmp; if (!gravitize_planets(pl1, pl2, step_time)) continue; ptmp = list_to_planet(pl2->list.prev); merge_planets(pl1, pl2); pl2 = ptmp; } planet_root = move_planet(pl1, step_time); planets++; } } move_camera(&camera, true_time); if (poll_events(&status, true_time)) return; old_ticks = ticks; ticks = gettime(); true_time = (ticks - old_ticks) / (1000.0 * 1000.0) ; step_time = true_time * status.time_scale; step_time = MIN(step_time, 0.02); step_count++; /* * Do not draw to the screen more often than MAX_FPS * per second */ if (last_frame_tick + (1000 * 1000) / MAX_FPS > ticks) continue; SDL_LockSurface(screen); if (status.tracers_enabled && !camera.speed.x && !camera.speed.y && camera.zoom_rate == 1) { fade_amount = (ticks - last_frame_tick) / (1000 * 1000.0) * 20; fade_buf(screen, fade_amount); } else { clear_buf(screen); } itr.limit[0] = camera.pos; vect.x = screen->w / 2; vect.y = screen->h / 2; vector_scale(&vect, 1 / camera.zoom, &vect); vector_add(&itr.limit[0], &vect, &itr.limit[1]); vector_sub(&itr.limit[0], &vect, &itr.limit[0]); itr.qt_iterator.head = &planet_root->tree; visible_planets = walk_quadtree(&itr.qt_iterator); SDL_UnlockSurface(screen); SDL_Flip(screen); last_frame_tick = ticks; if (last_fps_time + (500 * 1000) < ticks) { last_framecount = framecount - last_framecount; last_fps = last_framecount * 1000 * 1000 / (float)(ticks - last_fps_time); last_framecount = framecount; last_step_count = step_count - last_step_count; last_sps = last_step_count * 1000 * 1000 / (float)(ticks - last_fps_time); last_step_count = step_count; last_fps_time = ticks; } printf(" \rfps: %.2f, steps/s: %.2f, planets: %d" ", scale %.2f, zoom %.2f, step %ld, visible %d," " depth %ld, c:%ld", last_fps, last_sps, planets, status.time_scale, camera.zoom, step_count, visible_planets, planet_root->tree.depth, planet_root->tree.children); fflush(stdout); framecount++; } } int main(int argc, char *argv[]) { SDL_Surface *screen; int flags = SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_RESIZABLE; int planets = 100, xres = 800, yres = 600; double total_mass = 50000; double range = 500; int optind = 0, c; static struct option long_options[] = { { .val = 'x', .name = "xres", .has_arg = 1, }, { .val = 'y', .name = "yres", .has_arg = 1, }, { .val = 'p', .name = "planets", .has_arg = 1 }, { .val = 'm', .name = "total-mass", .has_arg = 1 }, { .val = 'r', .name = "range", .has_arg = 1 }, {}, }; char short_options[] = "x:y:p:m:r:"; if (SDL_Init(SDL_INIT_VIDEO) != 0) { fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); return 1; } atexit(SDL_Quit); while (1) { c = getopt_long(argc, argv, short_options, long_options, &optind); if (c == -1) break; printf("%c: %s\n", c, optarg); switch (c) { case 'x': xres = atoi(optarg); break; case 'y': yres = atoi(optarg); break; case 'p': planets = atoi(optarg); break; case 'r': range = atoi(optarg); break; } } screen = SDL_SetVideoMode(xres, yres, 32, flags); if (screen == NULL) { fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError()); return 2; } SDL_WM_SetCaption(argv[0], NULL); loop(screen, planets, total_mass, range); return 0; }