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