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