refactored main
[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_iUPS, s_iFPS;
78 static int s_iUpdateCount, s_iDrawCount;
79 static MICRO s_micLastSecond;
80
81 static const float s_fGameStep    = 10;
82
83 static bool s_bUpdateInitialized  = false;
84 static bool s_bDrawInitialized    = false;
85
86 /// ***** MAIN Method *****
87 int main(int argc, char** args)
88 {
89     mainInit();
90     run();
91     mainClean();
92
93     return 0;
94 }
95
96 /// ***** Initializers/Cleaners *****
97
98 void mainInit()
99 {
100     installSignal();
101
102     debug::init();
103 }
104 void mainClean()
105 {
106     debug::clean();
107 }
108
109 void updatesInit()
110 {
111     while(!s_bDrawInitialized)
112         SDL_Delay(100);
113
114     game::init();
115     DPF(0, "Game initialized");
116
117     input::init();
118     DPF(0, "Input initialized");
119
120     cfg::init();
121     DPF(0, "Configs initialized");
122
123     DPF(0, "Initialization Complete");
124
125     s_bUpdateInitialized = true;
126 }
127 void updatesClean()
128 {
129     DPF(0, "Update Thread Cleaning");
130
131     cfg::clean();
132
133     input::clean();
134
135     game::clean();
136 }
137
138 void drawInit()
139 {
140     graphics::init();
141     DPF(0, "Graphics initialized");
142
143     s_bDrawInitialized = true;
144
145     while(!s_bUpdateInitialized)
146         SDL_Delay(100);
147 }
148 void drawClean()
149 {
150     DPF(0, "Draw Thread Cleaning");
151
152     graphics::clean();
153 }
154
155 /// ***** Private Methods *****
156
157 void run()
158 {
159     s_bIsRunning = true;
160
161     SDL_Thread* s_pUpdatesThread   = SDL_CreateThread(startUpdateThread,   NULL);
162     SDL_Thread* s_pDrawThread      = SDL_CreateThread(startDrawThread,     NULL);
163
164     SDL_WaitThread(s_pUpdatesThread,   NULL);
165     SDL_WaitThread(s_pDrawThread,      NULL);
166 }
167
168 void updateFPSCounters()
169 {
170     // Check if a second has passed to recalculate UPS and FPS
171     if (tickCountMicro() - s_micLastSecond >= 1000000)
172     {
173         s_iUPS = s_iUpdateCount;
174         s_iFPS = s_iDrawCount;
175
176         // NOT thread safe, but they're estimates anyways
177         s_iUpdateCount -= s_iUPS;
178         s_iDrawCount -= s_iFPS;
179
180         s_micLastSecond = tickCountMicro();
181
182         if(cfg::showFPS())
183         {
184             cout << "fps:\t" << s_iFPS << endl;
185         }
186         if(cfg::showUPS())
187         {
188             cout << "ups:\t" << s_iUPS << endl;
189         }
190     }
191 }
192
193 void handleInput()
194 {
195     input::update();
196
197     cfg::handleInput();
198     game::handleInput();
199
200     if(cfg::endGame())
201         s_bIsRunning = false;
202 }
203
204 void update(float fTimeStep)
205 {
206     game::update(fTimeStep);
207     s_iUpdateCount++;
208 }
209
210 void draw()
211 {
212     game::draw();
213
214     SDL_PumpEvents(); // has to be on the Draw thread for the Windows API
215
216     SDL_GL_SwapBuffers();
217
218     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
219
220     s_iDrawCount++;
221 }
222
223 int startUpdateThread(void*)
224 {
225     updatesInit();
226
227     s_micLastSecond = tickCountMicro();
228
229     while(s_bIsRunning)
230     {
231         MICRO time = tickCountMicro();
232             handleInput();
233             update(s_fGameStep);
234
235             updateFPSCounters();
236         time = tickCountMicro() - time;
237
238         float wait = (1000000.0 / s_iTargetUPS - time);
239         s_fAccUpdateWait += 0 < wait ? wait : 0;
240
241         if(s_iMinWaitMicro < s_fAccUpdateWait)
242         {
243             int iWaits = s_fAccUpdateWait / s_iMinWaitMicro;
244             s_fAccUpdateWait -= iWaits * s_iMinWaitMicro;
245             SDL_Delay(iWaits * s_iMinWaitMilli);
246         }
247     }
248
249     updatesClean();
250
251     return 0;
252 }
253
254 int startDrawThread(void*)
255 {
256     drawInit();
257
258     while(s_bIsRunning)
259     {
260         draw();
261     }
262
263     drawClean();
264
265     return 0;
266 }