]> git.itanic.dy.fi Git - sdl-planets/blob - main.c
main.c: Use getopt_long() for reading cmd line arguments
[sdl-planets] / main.c
1 #include <SDL.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <time.h>
5 #include <getopt.h>
6
7 #include "random.h"
8 #include "planet.h"
9
10 #define MAX_FPS 60
11
12 static void fade_buf(SDL_Surface *screen, double fade)
13 {
14         int i;
15         unsigned int *buf = screen->pixels;
16         unsigned char *b;
17         unsigned char amount;
18         static double threshold;
19         threshold += fade;
20
21         if (threshold > 1) {
22                 amount = threshold;
23                 threshold -= amount;
24         } else {
25                 return;
26         }
27
28         for (i = 0; i < screen->pitch * screen->h / sizeof(*buf); i++) {
29                 if (!buf[i])
30                         continue;
31                 b = (unsigned char *)&buf[i];
32                 *b = *b >= amount ? (*b - amount) : *b;
33                 b++;
34                 *b = *b >= amount ? (*b - amount) : *b;
35                 b++;
36                 *b = *b >= amount ? (*b - amount) : *b;
37         }
38 }
39
40 static void clear_buf(SDL_Surface *screen)
41 {
42         int i;
43         unsigned int *buf = screen->pixels;
44
45         for (i = 0; i < screen->pitch * screen->h / 4; i++)
46                 buf[i] = 0;
47 }
48
49 /* Return time in microseconds */
50
51 static unsigned long gettime(void)
52 {
53         struct timespec tp;
54         unsigned long ret;
55         static unsigned long start;
56
57         clock_gettime(CLOCK_MONOTONIC, &tp);
58
59         ret = tp.tv_sec * 1000 * 1000;
60         ret += tp.tv_nsec / 1000;
61
62         if (!start)
63                 start = ret;
64
65         return ret - start;
66 }
67
68 struct sim_status {
69         SDL_Surface *screen;
70         struct camera *cam;
71         double time_scale;
72         int tracers_enabled;
73 };
74
75 static int poll_events(struct sim_status *status, double time)
76 {
77         static double time_scale_rate = 1;
78         SDL_Event event;
79         struct camera *cam = status->cam;
80
81         status->time_scale *= pow(time_scale_rate, time);
82
83         while (SDL_PollEvent(&event)) {
84                 switch (event.type) {
85                 case SDL_KEYDOWN:
86                         switch (event.key.keysym.sym) {
87                         case SDLK_ESCAPE:
88                                 goto quit;
89                         case SDLK_LEFT:
90                                 cam->speed.x = -CAM_SPEED;
91                                 break;
92                         case SDLK_RIGHT:
93                                 cam->speed.x = CAM_SPEED;
94                                 break;
95                         case SDLK_UP:
96                                 cam->speed.y = -CAM_SPEED;
97                                 break;
98                         case SDLK_DOWN:
99                                 cam->speed.y = CAM_SPEED;
100                                 break;
101                         case SDLK_PLUS:
102                         case SDLK_q:
103                                 cam->zoom_rate = CAM_ZOOM_RATE;
104                                 break;
105                         case SDLK_MINUS:
106                         case SDLK_w:
107                                 cam->zoom_rate = 1 / CAM_ZOOM_RATE;
108                                 break;
109                         case SDLK_s:
110                                 time_scale_rate = 1.5;
111                                 break;
112                         case SDLK_a:
113                                 time_scale_rate = 1 / 1.5;
114                                 break;
115                         case SDLK_1:
116                                 status->time_scale = 1;
117                                 break;
118                         case SDLK_2:
119                                 status->time_scale = 10;
120                                 break;
121                         case SDLK_3:
122                                 status->time_scale = 20;
123                                 break;
124                         case SDLK_4:
125                                 status->time_scale = 30;
126                                 break;
127                         case SDLK_0:
128                                 status->time_scale = 0;
129                                 break;
130                         case SDLK_t:
131                                 status->tracers_enabled =
132                                         !status->tracers_enabled;
133                         default:
134                                 break;
135                         }
136                         break;
137                 case SDL_KEYUP:
138                         switch (event.key.keysym.sym) {
139                         case SDLK_LEFT:
140                                 cam->speed.x = 0;
141                                 break;
142                         case SDLK_RIGHT:
143                                 cam->speed.x = 0;
144                                 break;
145                         case SDLK_UP:
146                                 cam->speed.y = 0;
147                                 break;
148                         case SDLK_DOWN:
149                                 cam->speed.y = 0;
150                                 break;
151                         case SDLK_PLUS:
152                         case SDLK_q:
153                                 cam->zoom_rate = 1;
154                                 break;
155                         case SDLK_MINUS:
156                         case SDLK_w:
157                                 cam->zoom_rate = 1;
158                                 break;
159                         case SDLK_s:
160                                 time_scale_rate = 1;
161                                 break;
162                         case SDLK_a:
163                                 time_scale_rate = 1;
164                                 break;
165                         default:
166                                 break;
167                         }
168                         break;
169                 case SDL_VIDEORESIZE:
170                         status->screen =
171                                 SDL_SetVideoMode(event.resize.w,
172                                                  event.resize.h,
173                                                  32,
174                                                  status->screen->flags);
175                         break;
176                 case SDL_QUIT:
177                         goto quit;
178                 }
179         }
180
181         return 0;
182 quit:
183         printf("\nExiting. Good bye!\n");
184         return 1;
185 }
186
187 static void loop(SDL_Surface *screen, int num_of_planets, double total_mass,
188                  double range)
189 {
190         struct sim_status status;
191         struct planet *planet, *pl1, *pl2, *planet_root;
192         struct camera camera;
193         struct planet_search_iterator itr;
194         struct vector vect;
195
196         int planets;
197         int framecount = 0, last_fps_time = 0;
198         int last_framecount = 0, last_step_count = 0;
199         unsigned long old_ticks, ticks = 0, last_frame_tick = 0;
200         unsigned long step_count = 0;
201         double last_fps = 0, last_sps = 0, fade_amount;
202         double step_time = 0, true_time = 0;
203         int visible_planets;
204
205         init_camera(&camera);
206
207         status.cam = &camera;
208         status.time_scale = 1;
209         status.screen = screen;
210         status.tracers_enabled = 0;
211
212         itr.screen = screen;
213         itr.cam = &camera;
214         itr.qt_iterator.direction = planet_search_rectangular;
215         itr.qt_iterator.callback = planet_draw_iterator;
216
217         planet = malloc(sizeof(*planet));
218         init_planet(planet);
219         create_planets(planet, num_of_planets, total_mass, range);
220
221         ticks = SDL_GetTicks();
222         while (1) {
223                 planets = 0;
224
225                 if (status.time_scale > 0) {
226                         list_for_each_entry(pl1, &planet->list, list) {
227                                 pl2 = list_to_planet(pl1->list.next);
228                                 list_for_each_entry_from(pl2, &planet->list,
229                                                          list) {
230                                         struct planet *ptmp;
231                                         if (!gravitize_planets(pl1, pl2,
232                                                                step_time))
233                                                 continue;
234
235                                         ptmp = list_to_planet(pl2->list.prev);
236                                         merge_planets(pl1, pl2);
237                                         pl2 = ptmp;
238                                 }
239
240                                 planet_root = move_planet(pl1, step_time);
241                                 planets++;
242                         }
243                 }
244                 move_camera(&camera, true_time);
245
246                 if (poll_events(&status, true_time))
247                         return;
248
249                 old_ticks = ticks;
250                 ticks = gettime();
251                 true_time = (ticks - old_ticks) / (1000.0 * 1000.0) ;
252                 step_time = true_time * status.time_scale;
253                 step_time = MIN(step_time, 0.02);
254                 step_count++;
255
256                 /*
257                  * Do not draw to the screen more often than MAX_FPS
258                  * per second
259                  */
260                 if (last_frame_tick + (1000 * 1000) / MAX_FPS > ticks)
261                         continue;
262
263                 SDL_LockSurface(screen);
264
265                 if (status.tracers_enabled &&
266                     !camera.speed.x && !camera.speed.y &&
267                     camera.zoom_rate == 1) {
268                         fade_amount = (ticks - last_frame_tick) /
269                                 (1000 * 1000.0) * 20;
270                         fade_buf(screen, fade_amount);
271                 } else {
272                         clear_buf(screen);
273                 }
274
275                 itr.limit[0] = camera.pos;
276                 vect.x = screen->w / 2;
277                 vect.y = screen->h / 2;
278                 vector_scale(&vect, 1 / camera.zoom, &vect);
279                 vector_add(&itr.limit[0], &vect, &itr.limit[1]);
280                 vector_sub(&itr.limit[0], &vect, &itr.limit[0]);
281
282                 itr.qt_iterator.head = &planet_root->tree;
283
284                 visible_planets = walk_quadtree(&itr.qt_iterator);
285
286                 SDL_UnlockSurface(screen);
287
288                 SDL_Flip(screen);
289
290                 last_frame_tick = ticks;
291
292                 if (last_fps_time + (500 * 1000) < ticks) {
293                         last_framecount = framecount - last_framecount;
294                         last_fps = last_framecount * 1000 * 1000 /
295                                 (float)(ticks - last_fps_time);
296                         last_framecount = framecount;
297
298                         last_step_count = step_count - last_step_count;
299                         last_sps = last_step_count * 1000 * 1000 /
300                                 (float)(ticks - last_fps_time);
301                         last_step_count = step_count;
302
303                         last_fps_time = ticks;
304                 }
305
306                 printf("  \rfps: %.2f, steps/s: %.2f, planets: %d"
307                        ", scale %.2f, zoom %.2f, step %ld, visible %d,"
308                        " depth %ld, c:%ld",
309                        last_fps, last_sps, planets, status.time_scale,
310                        camera.zoom, step_count, visible_planets,
311                        planet_root->tree.depth, planet_root->tree.children);
312                 fflush(stdout);
313
314
315                 framecount++;
316         }
317 }
318
319 int main(int argc, char *argv[])
320 {
321         SDL_Surface *screen;
322         int flags = SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_RESIZABLE;
323         int planets = 100, xres = 800, yres = 600;
324         double total_mass = 50000;
325         double range = 500;
326         int optind = 0, c;
327         static struct option long_options[] = {
328                 { .val = 'x', .name = "xres", .has_arg = 1, },
329                 { .val = 'y', .name = "yres", .has_arg = 1, },
330                 { .val = 'p', .name = "planets", .has_arg = 1 },
331                 { .val = 'm', .name = "total-mass", .has_arg = 1 },
332                 { .val = 'r', .name = "range", .has_arg = 1 },
333                 {},
334         };
335         char short_options[] = "x:y:p:m:r:";
336
337         if (SDL_Init(SDL_INIT_VIDEO) != 0) {
338                 fprintf(stderr, "Unable to initialize SDL: %s\n",
339                         SDL_GetError());
340
341                 return 1;
342         }
343         atexit(SDL_Quit);
344
345         while (1) {
346                 c = getopt_long(argc, argv, short_options, long_options,
347                                 &optind);
348
349                 if (c == -1)
350                         break;
351
352                 printf("%c: %s\n", c, optarg);
353
354                 switch (c) {
355                 case 'x':
356                         xres = atoi(optarg);
357                         break;
358
359                 case 'y':
360                         yres = atoi(optarg);
361                         break;
362
363                 case 'p':
364                         planets = atoi(optarg);
365                         break;
366
367                 case 'r':
368                         range = atoi(optarg);
369                         break;
370                 }
371         }
372
373         screen = SDL_SetVideoMode(xres, yres, 32, flags);
374         if (screen == NULL) {
375                 fprintf(stderr, "Unable to set video mode: %s\n",
376                         SDL_GetError());
377                 return 2;
378         }
379
380         SDL_WM_SetCaption(argv[0], NULL);
381
382         loop(screen, planets, total_mass, range);
383
384         return 0;
385 }
386