c9e8808e0a24b9060609dacf9a218c2f49cac8f0
[physics.git] / src / main.cpp
1 /*
2  *  Copyright (C) 2008 Patrik Gornicz, Gornicz_P (at) hotmail (dot) com.
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <GL/gl.h>
19 #include <GL/glu.h>
20 #include <SDL/SDL.h>
21
22 #include "debug.h"
23 #include "handleSignal.h"
24
25 #include "game.h"
26 #include "ticks.h"
27
28 #include "graphics/graphics.h"
29 #include "input/inputManager.h"
30 #include "config/config.h"
31
32 /// ***** Private Method Headers *****
33
34 static void mainInit();
35 static void mainClean();
36
37 static void updatesInit();
38 static void updatesClean();
39
40 static void drawInit();
41 static void drawClean();
42
43 static void run();
44
45 static void updateFPSCounters();
46
47 static void handleInput();
48 static void update(float);
49 static void draw();
50
51 static int startUpdateThread(void*);
52 static int startDrawThread(void*);
53
54 /// ***** Private Variables *****
55
56 // variable used to determine if it is time to shutdown
57 static bool s_bIsRunning;
58
59 // Minimum possible wait times by used library
60 static const int s_iMinWaitMilli = 20;
61 static const int s_iMinWaitMicro = s_iMinWaitMilli * 1000;
62
63 /* Values for the main game loop
64  *
65  * s_iTargetUPS     := the amount of updates that is wanted in one second
66  * s_fAccUpdateWait := the accumulated wait time for the update sleeps
67  *
68  * s_iUPS           := updates per second for the last second
69  * s_iFPS           := frames per second for the last second
70  * s_iUpdateCount   := counts this seconds updates
71  * s_iDrawCount     := counts this seconds draws
72  * s_micLastSecond  := stores the time of the last second, used for ups and fps
73  */
74 static int      s_iTargetUPS     = 100;
75 static float    s_fAccUpdateWait = 0;
76
77 static int      s_iTargetFPS     = 100;
78 static float    s_fAccDrawWait   = 0;
79
80 static int s_iUPS, s_iFPS;
81 static int s_iUpdateCount, s_iDrawCount;
82 static MICRO s_micLastSecond;
83
84 static const float s_fGameStep    = 10;
85
86 static bool s_bUpdateInitialized  = false;
87 static bool s_bDrawInitialized    = false;
88
89 /// ***** MAIN Method *****
90 int main(int argc, char** args)
91 {
92     mainInit();
93     run();
94     mainClean();
95
96     return 0;
97 }
98
99 /// ***** Initializers/Cleaners *****
100
101 void mainInit()
102 {
103     installSignal();
104
105     debug::init();
106 }
107 void mainClean()
108 {
109     debug::clean();
110 }
111
112 void updatesInit()
113 {
114     while(!s_bDrawInitialized)
115         SDL_Delay(100);
116
117     game::init();
118     DPF(0, "Game initialized");
119
120     input::init();
121     DPF(0, "Input initialized");
122
123     cfg::init();
124     DPF(0, "Configs initialized");
125
126     DPF(0, "Initialization Complete");
127
128     s_bUpdateInitialized = true;
129 }
130 void updatesClean()
131 {
132     DPF(0, "Update Thread Cleaning");
133
134     cfg::clean();
135
136     input::clean();
137
138     game::clean();
139 }
140
141 void drawInit()
142 {
143     graphics::init();
144     DPF(0, "Graphics initialized");
145
146     s_bDrawInitialized = true;
147
148     while(!s_bUpdateInitialized)
149         SDL_Delay(100);
150 }
151 void drawClean()
152 {
153     DPF(0, "Draw Thread Cleaning");
154
155     graphics::clean();
156 }
157
158 /// ***** Private Methods *****
159
160 void run()
161 {
162     s_bIsRunning = true;
163
164     SDL_Thread* s_pUpdatesThread   = SDL_CreateThread(startUpdateThread,   NULL);
165     SDL_Thread* s_pDrawThread      = SDL_CreateThread(startDrawThread,     NULL);
166
167     SDL_WaitThread(s_pUpdatesThread,   NULL);
168     SDL_WaitThread(s_pDrawThread,      NULL);
169 }
170
171 void updateFPSCounters()
172 {
173     // Check if a second has passed to recalculate UPS and FPS
174     if (tickCountMicro() - s_micLastSecond >= 1000000)
175     {
176         s_iUPS = s_iUpdateCount;
177         s_iFPS = s_iDrawCount;
178
179         // NOT thread safe, but they're estimates anyways
180         s_iUpdateCount -= s_iUPS;
181         s_iDrawCount -= s_iFPS;
182
183         s_micLastSecond = tickCountMicro();
184
185         if(cfg::showFPS())
186         {
187             cout << "fps:\t" << s_iFPS << endl;
188         }
189         if(cfg::showUPS())
190         {
191             cout << "ups:\t" << s_iUPS << endl;
192         }
193     }
194 }
195
196 void handleInput()
197 {
198     input::update();
199
200     cfg::handleInput();
201     game::handleInput();
202
203     if(cfg::endGame())
204         s_bIsRunning = false;
205 }
206
207 void update(float fTimeStep)
208 {
209     game::update(fTimeStep);
210     s_iUpdateCount++;
211 }
212
213 void draw()
214 {
215     game::draw();
216
217     SDL_PumpEvents(); // has to be on the Draw thread for the Windows API
218
219     SDL_GL_SwapBuffers();
220
221     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
222
223     s_iDrawCount++;
224 }
225
226 int startUpdateThread(void*)
227 {
228     updatesInit();
229
230     s_micLastSecond = tickCountMicro();
231
232     while(s_bIsRunning)
233     {
234         MICRO time = tickCountMicro();
235             handleInput();
236             update(s_fGameStep);
237
238             updateFPSCounters();
239         time = tickCountMicro() - time;
240
241         float wait = (1000000.0 / s_iTargetUPS - time);
242         s_fAccUpdateWait += 0 < wait ? wait : 0;
243
244         if(s_iMinWaitMicro < s_fAccUpdateWait)
245         {
246             int iWaits = (int)(s_fAccUpdateWait / s_iMinWaitMicro);
247             s_fAccUpdateWait -= iWaits * s_iMinWaitMicro;
248             SDL_Delay(iWaits * s_iMinWaitMilli);
249         }
250     }
251
252     updatesClean();
253
254     return 0;
255 }
256
257 int startDrawThread(void*)
258 {
259     drawInit();
260
261     while(s_bIsRunning)
262     {
263         MICRO time = tickCountMicro();
264             draw();
265         time = tickCountMicro() - time;
266
267         float wait = (1000000.0 / s_iTargetFPS - time);
268         s_fAccDrawWait += 0 < wait ? wait : 0;
269
270         if(s_iMinWaitMicro < s_fAccDrawWait)
271         {
272             int iWaits = (int)(s_fAccDrawWait / s_iMinWaitMicro);
273             s_fAccDrawWait -= iWaits * s_iMinWaitMicro;
274             SDL_Delay(iWaits * s_iMinWaitMilli);
275         }
276     }
277
278     drawClean();
279
280     return 0;
281 }