]> Dogcows Code - chaz/yoink/blob - src/Moof/Core.cc
game loop tweaks; shapes hierarchy defined
[chaz/yoink] / src / Moof / Core.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include <algorithm>
13 #include <cstdlib> // exit, srand
14 #include <ctime> // time
15 #include <list>
16 #include <string>
17
18 #include <SDL/SDL.h>
19 #include "fastevents.h"
20
21 #include "Core.hh"
22 #include "Event.hh"
23 #include "Log.hh"
24 #include "Math.hh"
25 #include "ModalDialog.hh"
26 #include "Settings.hh"
27 #include "Timer.hh"
28 #include "Video.hh"
29
30
31 namespace Mf {
32
33
34 class Core::Impl
35 {
36 public:
37
38 Impl() :
39 mError(Error::NONE),
40 mTimestep(0.01),
41 mFramerate(0.02),
42 mShowFps(false) {}
43
44 void init()
45 {
46 unsigned randomSeed;
47 if (settings.get("rngseed", randomSeed)) srand(randomSeed);
48 else srand(time(0));
49
50 Scalar timestep = 80.0;
51 settings.get("timestep", timestep);
52 mTimestep = 1.0 / timestep;
53
54 Scalar framerate = 40.0;
55 settings.get("framerate", framerate);
56 mFramerate = 1.0 / framerate;
57
58 mShowFps = false;
59 settings.get("showfps", mShowFps);
60 }
61
62
63 /**
64 * The main loop. This just calls dispatchEvents(), update(), and
65 * draw() over and over again. The timing of the update and draw are
66 * decoupled. The actual frame rate is also calculated here. This
67 * function will return the exit code used to stop the loop.
68 */
69
70 void run()
71 {
72 init();
73 ASSERT(video && "cannot run core without a current video context");
74
75 Scalar totalTime = 0.0;
76 Scalar ticks = Timer::getTicks();
77
78 Scalar nextUpdate = ticks;
79 Scalar nextDraw = ticks;
80 Scalar nextSecond = ticks + SCALAR(1.0);
81
82 mFps = 0;
83 int frameCount = 0;
84
85 const Scalar timestep = mTimestep;
86 const Scalar framerate = mFramerate;
87
88 const int MAX_FRAMESKIP = 15;
89 const Scalar inverseTimestep = SCALAR(1.0) / timestep;
90
91 do
92 {
93 Timer::fireIfExpired(); // 1. fire timers
94 dispatchEvents(); // 2. dispatch events
95
96 int i = 0;
97 while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP)
98 {
99 totalTime += timestep;
100 update(totalTime, timestep); // 3. update state
101
102 nextUpdate += timestep;
103 ++i;
104 }
105
106 if (nextDraw < (ticks = Timer::getTicks()))
107 {
108 draw((ticks + timestep - nextUpdate) * inverseTimestep);
109 video->swap(); // 4. draw state
110
111 nextDraw += framerate;
112 ++frameCount;
113
114 if (nextSecond < Timer::getTicks())
115 {
116 mFps = frameCount;
117 frameCount = 0;
118
119 if (mShowFps) logInfo << mFps << " fps" << std::endl;
120
121 nextSecond += SCALAR(1.0);
122 }
123 }
124
125 ticks = Timer::getTicks(); // 5. yield timeslice
126 if (ticks < nextUpdate && ticks < nextDraw) Timer::sleep(0.0);
127 }
128 while (!mStack.empty());
129
130 mDispatch.dispatch("engine.stopping");
131 }
132
133
134 void dispatchEvents()
135 {
136 SDL_Event event;
137
138 while (FE_PollEvent(&event) == 1)
139 {
140 switch (event.type)
141 {
142 case SDL_KEYDOWN:
143 if (event.key.keysym.sym == SDLK_ESCAPE &&
144 (SDL_GetModState() & KMOD_CTRL) )
145 {
146 // emergency escape
147 logWarning("escape forced");
148 exit(1);
149 }
150 break;
151
152 case SDL_VIDEORESIZE:
153 video->resize(event.resize.w, event.resize.h);
154 break;
155 }
156
157 handleEvent(event);
158 }
159 }
160
161
162 void update(Scalar t, Scalar dt)
163 {
164 for (mStackIt = mStack.begin(); mStackIt != mStack.end();
165 ++mStackIt)
166 {
167 (*mStackIt)->update(t, dt);
168 }
169 }
170
171 void draw(Scalar alpha)
172 {
173 // FIXME - this will crash if the layer being drawn pops itself
174 std::list<LayerP>::reverse_iterator it;
175 for (it = mStack.rbegin(); it != mStack.rend(); ++it)
176 {
177 (*it)->draw(alpha);
178 }
179 }
180
181 void handleEvent(const Event& event)
182 {
183 for (mStackIt = mStack.begin(); mStackIt != mStack.end();
184 ++mStackIt)
185 {
186 if ((*mStackIt)->handleEvent(event)) break;
187 }
188 }
189
190
191 void push(LayerP layer)
192 {
193 ASSERT(layer && "cannot push null layer");
194 mStack.push_front(layer);
195 logInfo << "stack: " << mStack.size()
196 << " [pushed " << layer.get() << "]" << std::endl;
197 layer->addedToCore();
198 }
199
200 LayerP pop()
201 {
202 bool fixIt = false;
203 if (mStack.begin() == mStackIt) fixIt = true;
204
205 LayerP layer = mStack.front();
206 mStack.pop_front();
207 logInfo << "stack: " << mStack.size()
208 << " [popped " << layer.get() << "]" << std::endl;
209 layer->removedFromCore();
210
211 if (fixIt) mStackIt = --mStack.begin();
212
213 return layer;
214 }
215
216 LayerP pop(Layer* layer)
217 {
218 bool fixIt = false;
219
220 std::list<LayerP> layers;
221
222 std::list<LayerP>::iterator it;
223 for (it = mStack.begin(); it != mStack.end(); ++it)
224 {
225 layers.push_back(*it);
226
227 if (it == mStackIt) fixIt = true;
228
229 if ((*it).get() == layer)
230 {
231 ++it;
232 mStack.erase(mStack.begin(), it);
233
234 for (it = layers.begin(); it != layers.end(); ++it)
235 {
236 (*it)->removedFromCore();
237 logInfo << "stack: " << mStack.size()
238 << " [popped " << (*it).get() << "]"
239 << std::endl;
240 }
241
242 if (fixIt) mStackIt = --mStack.begin();
243
244 return layers.back();
245 }
246 }
247
248 return LayerP();
249 }
250
251 void clear()
252 {
253 mStack.clear();
254 mStackIt = mStack.begin();
255 logInfo("stack: 0 [cleared]");
256 }
257
258
259 Error mError;
260
261 Dispatch mDispatch;
262
263 std::list<LayerP> mStack;
264 std::list<LayerP>::iterator mStackIt;
265
266 Scalar mTimestep;
267 Scalar mFramerate;
268
269 int mFps;
270 bool mShowFps;
271 };
272
273
274 Core::Core() :
275 // pass through
276 mImpl(new Core::Impl) {}
277
278
279 void Core::init()
280 {
281 // pass through
282 mImpl->init();
283 }
284
285
286 int Core::getFps() const
287 {
288 return mImpl->mFps;
289 }
290
291
292 void Core::push(LayerP layer)
293 {
294 // pass through
295 mImpl->push(layer);
296 }
297
298 LayerP Core::pop()
299 {
300 // pass through
301 return mImpl->pop();
302 }
303
304 LayerP Core::pop(Layer* layer)
305 {
306 // pass through
307 return mImpl->pop(layer);
308 }
309
310 void Core::clear()
311 {
312 // pass through
313 mImpl->clear();
314 }
315
316 int Core::getSize() const
317 {
318 return mImpl->mStack.size();
319 }
320
321
322 void Core::run()
323 {
324 // pass through
325 return mImpl->run();
326 }
327
328
329 Dispatch::Handler Core::addHandler(const std::string& event,
330 const Dispatch::Function& callback)
331 {
332 return mImpl->mDispatch.addHandler(event, callback);
333 }
334
335 Dispatch::Handler Core::addHandler(const std::string& event,
336 const Dispatch::Function& callback,
337 Dispatch::Handler handler)
338 {
339 return mImpl->mDispatch.addHandler(event, callback, handler);
340 }
341
342 void Core::dispatch(const std::string& event,
343 const Dispatch::Message* message)
344 {
345 mImpl->mDispatch.dispatch(event, message);
346 }
347
348
349 Core core;
350
351
352 class Backend_;
353 typedef boost::shared_ptr<Backend_> BackendP;
354
355 class Backend_
356 {
357 public:
358
359 Backend_()
360 {
361 #if defined(_WIN32) || defined(__WIN32__)
362 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
363 #else
364 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
365 #endif
366 {
367 const char* error = SDL_GetError();
368 gError.init(Error::SDL_INIT, error);
369 return; // fatal
370 }
371 else
372 {
373 char name[128];
374 SDL_VideoDriverName(name, sizeof(name));
375 logInfo << "initialized SDL; using video driver `"
376 << name << "'" << std::endl;
377 }
378
379 if (FE_Init() != 0)
380 {
381 const char* error = FE_GetError();
382 gError.init(Error::FASTEVENTS_INIT, error);
383 return; // fatal
384 }
385
386 gError.init(Error::NONE);
387 }
388
389 ~Backend_()
390 {
391 FE_Quit();
392 SDL_Quit();
393 }
394
395 static void retain()
396 {
397 if (gRetainCount++ == 0)
398 {
399 gInstance = BackendP(new Backend_);
400 }
401 }
402
403 static void release()
404 {
405 if (--gRetainCount == 0)
406 {
407 gInstance.reset();
408 gError.reset();
409 }
410 }
411
412 static const Error& getError()
413 {
414 return gError;
415 }
416
417 private:
418
419 static Error gError;
420 static int gRetainCount;
421 static BackendP gInstance;
422 };
423
424 Error Backend_::gError(Error::UNINITIALIZED);
425 int Backend_::gRetainCount = 0;
426 BackendP Backend_::gInstance;
427
428
429 Backend::Backend()
430 {
431 Backend_::retain();
432 }
433
434 Backend::~Backend()
435 {
436 Backend_::release();
437 }
438
439 bool Backend::isInitialized()
440 {
441 return getError().code() == Error::NONE;
442 }
443
444 const Error& Backend::getError()
445 {
446 return Backend_::getError();
447 }
448
449
450 } // namespace Mf
451
This page took 0.049542 seconds and 4 git commands to generate.