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