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