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