]> Dogcows Code - chaz/yoink/blob - src/Moof/Engine.cc
minor refactoring and state progress
[chaz/yoink] / src / Moof / Engine.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <algorithm>
30 #include <cstdlib> // exit
31 #include <list>
32 #include <string>
33
34 #include <SDL/SDL.h>
35 #include "fastevents.h"
36 #include <AL/alut.h>
37
38 #include "Dispatcher.hh"
39 #include "Engine.hh"
40 #include "Event.hh"
41 #include "Log.hh"
42 #include "Random.hh"
43 #include "Settings.hh"
44 #include "Timer.hh"
45 #include "Video.hh"
46
47
48 namespace Mf {
49
50
51 class Engine::Impl
52 {
53 public:
54
55 Impl(int argc, char* argv[], const std::string& name,
56 const std::string& iconFile, const std::string& configFile,
57 Engine& engine) :
58 mInterface(engine),
59 mTimestep(0.01),
60 mPrintFps(false)
61 {
62 #if defined(_WIN32) || defined (_WIN64) || defined(__WIN32__)
63 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
64 #else
65 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
66 #endif
67 {
68 logError("sdl is complaining: %s", SDL_GetError());
69 throw Exception(Exception::SDL_ERROR);
70 }
71 if (FE_Init() != 0)
72 {
73 logError("fast events error: %s", FE_GetError());
74 throw Exception(Exception::SDL_ERROR);
75 }
76 alutInit(&argc, argv);
77
78 Settings& settings = Settings::getInstance();
79 settings.loadFromFile(configFile);
80 settings.parseArgs(argc, argv);
81
82 long randomSeed;
83 if (settings.get("rngseed", randomSeed)) setSeed(randomSeed);
84 else setSeed();
85
86 Scalar timestep = 80.0;
87 settings.get("timestep", timestep);
88 mTimestep = 1.0 / timestep;
89
90 Scalar maxFps = 40.0;
91 settings.get("maxfps", maxFps);
92 mDrawRate = 1.0 / maxFps;
93
94 settings.get("printfps", mPrintFps);
95
96 mVideo = Video::alloc(name, iconFile);
97 mVideo->makeActive();
98 }
99
100 ~Impl()
101 {
102 // the video object must be destroyed before we can shutdown SDL
103 mVideo.reset();
104
105 alutExit();
106 FE_Quit();
107 SDL_Quit();
108 }
109
110
111 /**
112 * The main loop. This just calls dispatchEvents(), update(), and draw()
113 * over and over again. The timing of the update and draw are decoupled.
114 * The actual frame rate is also calculated here. This function will return
115 * the exit code used to stop the loop.
116 */
117
118 void run()
119 {
120 Scalar ticksNow = Timer::getTicks();
121
122 Scalar nextStep = ticksNow;
123 Scalar nextDraw = ticksNow;
124 Scalar nextFpsUpdate = ticksNow + 1.0;
125
126 Scalar totalTime = 0.0;
127 Scalar deltaTime = 0.0;
128 Scalar accumulator = mTimestep;
129
130 mFps = 0;
131 int frameAccum = 0;
132
133 do
134 {
135 Scalar newTicks = Timer::getTicks();
136 deltaTime = newTicks - ticksNow;
137 ticksNow = newTicks;
138
139 if (deltaTime >= 0.25) deltaTime = 0.25;
140 accumulator += deltaTime;
141
142 Timer::fireIfExpired(ticksNow);
143
144 while (accumulator >= mTimestep)
145 {
146 dispatchEvents();
147 update(totalTime, mTimestep);
148
149 totalTime += mTimestep;
150 accumulator -= mTimestep;
151
152 nextStep += mTimestep;
153 }
154 if (ticksNow >= nextStep)
155 {
156 nextStep = ticksNow + mTimestep;
157 }
158
159 if (ticksNow >= nextDraw)
160 {
161 frameAccum++;
162
163 if (ticksNow >= nextFpsUpdate) // determine the actual fps
164 {
165 mFps = frameAccum;
166 frameAccum = 0;
167
168 nextFpsUpdate += 1.0;
169 if (ticksNow >= nextFpsUpdate)
170 {
171 nextFpsUpdate = ticksNow + 1.0;
172 }
173
174 if (mPrintFps)
175 {
176 logInfo("%d fps", mFps);
177 }
178 }
179
180 draw(accumulator / mTimestep);
181 mVideo->swap();
182
183 nextDraw += mDrawRate;
184 if (ticksNow >= nextDraw)
185 {
186 // we missed some scheduled draws, so reset the schedule
187 nextDraw = ticksNow + mDrawRate;
188 }
189 }
190
191 // be a good citizen and give back what you don't need
192 Timer::sleep(std::min(std::min(nextStep, nextDraw),
193 Timer::getNextFire()), true);
194 }
195 while (!mStack.empty());
196 }
197
198 void dispatchEvents()
199 {
200 SDL_Event event;
201
202 while (FE_PollEvent(&event) == 1)
203 {
204 switch (event.type)
205 {
206 case SDL_KEYDOWN:
207 if (event.key.keysym.sym == SDLK_ESCAPE &&
208 (SDL_GetModState() & KMOD_CTRL) )
209 {
210 // emergency escape
211 exit(0);
212 }
213 break;
214
215 case SDL_VIDEORESIZE:
216 mVideo->resize(event.resize.w, event.resize.h);
217 break;
218 }
219
220 handleEvent(event);
221 }
222 }
223
224
225 void update(Scalar t, Scalar dt)
226 {
227 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
228 {
229 (*mStackIt)->update(t, dt);
230 }
231 }
232
233 void draw(Scalar alpha)
234 {
235 // FIXME - this will crash if the layer being drawn pops itself
236 std::list<LayerP>::reverse_iterator it;
237 for (it = mStack.rbegin(); it != mStack.rend(); ++it)
238 {
239 (*it)->draw(alpha);
240 }
241 }
242
243 void handleEvent(const Event& event)
244 {
245 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
246 {
247 if ((*mStackIt)->handleEvent(event)) break;
248 }
249 }
250
251
252 void push(LayerP layer)
253 {
254 ASSERT(layer && "cannot push null layer");
255 mStack.push_front(layer);
256 logInfo(" push: %d", mStack.size());
257 layer->pushed(mInterface);
258 }
259
260 LayerP pop()
261 {
262 bool fixIt = false;
263 if (mStack.begin() == mStackIt) fixIt = true;
264
265 LayerP popped = mStack.front();
266 mStack.pop_front();
267 logInfo(" pop: %d", mStack.size());
268 popped->popped(mInterface);
269
270 if (fixIt) mStackIt = --mStack.begin();
271
272 return popped;
273 }
274
275 LayerP pop(Layer* layer)
276 {
277 bool fixIt = false;
278
279 std::list<LayerP> popped;
280
281 std::list<LayerP>::iterator it;
282 for (it = mStack.begin(); it != mStack.end(); ++it)
283 {
284 popped.push_back(*it);
285
286 if (it == mStackIt) fixIt = true;
287
288 if ((*it).get() == layer)
289 {
290 ++it;
291 mStack.erase(mStack.begin(), it);
292
293 for (it = popped.begin(); it != popped.end(); ++it)
294 {
295 (*it)->popped(mInterface);
296 }
297
298 if (fixIt) mStackIt = --mStack.begin();
299
300 return popped.back();
301 }
302 }
303
304 return LayerP();
305 }
306
307 void clear()
308 {
309 mStack.clear();
310 mStackIt = mStack.begin();
311 logInfo("clear: %d", mStack.size());
312 }
313
314
315 Engine& mInterface;
316
317 VideoP mVideo;
318
319 std::list<LayerP> mStack;
320 std::list<LayerP>::iterator mStackIt;
321
322 Scalar mTimestep;
323 Scalar mDrawRate;
324
325 long mFps;
326 bool mPrintFps;
327 };
328
329
330 static Engine* instance = 0;
331
332 Engine::Engine(int argc, char* argv[], const std::string& name,
333 const std::string& iconFile, const std::string& configFile) :
334 mImpl(new Engine::Impl(argc, argv, name, iconFile, configFile, *this))
335 {
336 instance = this;
337 }
338
339
340 Engine& Engine::getInstance()
341 {
342 ASSERT(instance && "dereferencing null pointer");
343 return *instance;
344 // TODO this has not been completely thought out
345 //static Engine engine;
346 //return engine;
347 }
348
349
350 void Engine::run()
351 {
352 return mImpl->run();
353 }
354
355 void Engine::setTimestep(Scalar ts)
356 {
357 mImpl->mTimestep = ts;
358 }
359
360 Scalar Engine::getTimestep() const
361 {
362 return mImpl->mTimestep;
363 }
364
365 void Engine::setMaxFrameRate(long maxFps)
366 {
367 mImpl->mDrawRate = 1.0 / Scalar(maxFps);
368 }
369
370 long Engine::getMaxFrameRate() const
371 {
372 return long(1.0 / mImpl->mDrawRate);
373 }
374
375
376 Video& Engine::getVideo() const
377 {
378 return *mImpl->mVideo;
379 }
380
381 long Engine::getFrameRate() const
382 {
383 return mImpl->mFps;
384 }
385
386
387 void Engine::push(LayerP layer)
388 {
389 // pass through
390 mImpl->push(layer);
391 }
392
393 LayerP Engine::pop()
394 {
395 // pass through
396 return mImpl->pop();
397 }
398
399 LayerP Engine::pop(Layer* layer)
400 {
401 // pass through
402 return mImpl->pop(layer);
403 }
404
405 void Engine::clear()
406 {
407 // pass through
408 mImpl->clear();
409 }
410
411
412 } // namespace Mf
413
414 /** vim: set ts=4 sw=4 tw=80: *************************************************/
415
This page took 0.045503 seconds and 4 git commands to generate.