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