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