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