]> Dogcows Code - chaz/yoink/blob - src/Moof/Engine.cc
foundational changes; tying up some loose ends
[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 "Engine.hh"
39 #include "Event.hh"
40 #include "Exception.hh"
41 #include "Log.hh"
42 #include "Math.hh"
43 #include "Random.hh"
44 #include "Settings.hh"
45 #include "Timer.hh"
46
47
48 namespace Mf {
49
50
51 class Engine::Impl
52 {
53 public:
54
55 Impl(Engine& engine) :
56 mInterface(engine),
57 mTimestep(0.01),
58 mPrintFps(false)
59 {
60 // first, initialize the libraries
61
62 #if defined(_WIN32) || 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 const char* error = SDL_GetError();
69 throw Exception(ErrorCode::SDL_INIT, error);
70 }
71 if (FE_Init() != 0)
72 {
73 const char* error = FE_GetError();
74 throw Exception(ErrorCode::FASTEVENTS_INIT, error);
75 }
76 int argc = 1;
77 char name[] = "hello";
78 alutInit(&argc, (char**)&name);
79
80 // now load the settings the engine needs
81
82 Settings& settings = Settings::getInstance();
83
84 unsigned randomSeed;
85 if (settings.get("rngseed", randomSeed)) setSeed(randomSeed);
86 else setSeed();
87
88 Scalar timestep = 80.0;
89 settings.get("timestep", timestep);
90 mTimestep = 1.0 / timestep;
91
92 Scalar maxFps = 40.0;
93 settings.get("maxfps", maxFps);
94 mMaxFps = 1.0 / maxFps;
95 capFps();
96
97 settings.get("printfps", mPrintFps);
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 // don't slow the animation until 4Hz, which is unplayable anyway
140 if (deltaTime >= 0.25) deltaTime = 0.25;
141 accumulator += deltaTime;
142
143 Timer::fireIfExpired(ticksNow);
144 dispatchEvents();
145
146 while (accumulator >= mTimestep)
147 {
148 update(totalTime, mTimestep);
149
150 totalTime += mTimestep;
151 accumulator -= mTimestep;
152
153 nextStep += mTimestep;
154 }
155 if (ticksNow >= nextStep)
156 {
157 nextStep = ticksNow + mTimestep;
158 }
159
160 if (ticksNow >= nextDraw)
161 {
162 frameAccum++;
163
164 if (ticksNow >= nextFpsUpdate) // determine the actual fps
165 {
166 mFps = frameAccum;
167 frameAccum = 0;
168
169 nextFpsUpdate += 1.0;
170 if (ticksNow >= nextFpsUpdate)
171 {
172 nextFpsUpdate = ticksNow + 1.0;
173 }
174
175 if (mPrintFps)
176 {
177 logInfo("%d fps", mFps);
178 }
179 }
180
181 draw(accumulator / mTimestep);
182 mVideo->swap();
183
184 nextDraw += mMaxFps;
185 if (ticksNow >= nextDraw)
186 {
187 // we missed some scheduled draws, so reset the schedule
188 nextDraw = ticksNow + mMaxFps;
189 }
190 }
191
192 // be a good citizen and give back what you don't need
193 Timer::sleep(std::min(std::min(nextStep, nextDraw),
194 Timer::getNextFire()), Timer::ACTUAL);
195 }
196 while (!mStack.empty());
197 }
198
199 void dispatchEvents()
200 {
201 SDL_Event event;
202
203 while (FE_PollEvent(&event) == 1)
204 {
205 switch (event.type)
206 {
207 case SDL_KEYDOWN:
208 if (event.key.keysym.sym == SDLK_ESCAPE &&
209 (SDL_GetModState() & KMOD_CTRL) )
210 {
211 // emergency escape
212 logWarning("escape forced");
213 exit(1);
214 }
215 break;
216
217 case SDL_VIDEORESIZE:
218 mVideo->resize(event.resize.w, event.resize.h);
219 break;
220 }
221
222 handleEvent(event);
223 }
224 }
225
226
227 void update(Scalar t, Scalar dt)
228 {
229 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
230 {
231 (*mStackIt)->update(mInterface, t, dt);
232 }
233 }
234
235 void draw(Scalar alpha)
236 {
237 // FIXME - this will crash if the layer being drawn pops itself
238 std::list<LayerP>::reverse_iterator it;
239 for (it = mStack.rbegin(); it != mStack.rend(); ++it)
240 {
241 (*it)->draw(mInterface, alpha);
242 }
243 }
244
245 void handleEvent(const Event& event)
246 {
247 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
248 {
249 if ((*mStackIt)->handleEvent(mInterface, event)) break;
250 }
251 }
252
253
254 void push(LayerP layer)
255 {
256 ASSERT(layer && "cannot push null layer");
257 mStack.push_front(layer);
258 logDebug("stack: %d [pushed %X]", mStack.size(), layer.get());
259 layer->pushed(mInterface);
260 }
261
262 LayerP pop()
263 {
264 bool fixIt = false;
265 if (mStack.begin() == mStackIt) fixIt = true;
266
267 LayerP layer = mStack.front();
268 mStack.pop_front();
269 logDebug("stack: %d [popped %X]", mStack.size(), layer.get());
270 layer->popped(mInterface);
271
272 if (fixIt) mStackIt = --mStack.begin();
273
274 return layer;
275 }
276
277 LayerP pop(Layer* layer)
278 {
279 bool fixIt = false;
280
281 std::list<LayerP> layers;
282
283 std::list<LayerP>::iterator it;
284 for (it = mStack.begin(); it != mStack.end(); ++it)
285 {
286 layers.push_back(*it);
287
288 if (it == mStackIt) fixIt = true;
289
290 if ((*it).get() == layer)
291 {
292 ++it;
293 mStack.erase(mStack.begin(), it);
294
295 for (it = layers.begin(); it != layers.end(); ++it)
296 {
297 (*it)->popped(mInterface);
298 logDebug("stack: %d [popped %X]", mStack.size(), (*it).get());
299 }
300
301 if (fixIt) mStackIt = --mStack.begin();
302
303 return layers.back();
304 }
305 }
306
307 return LayerP();
308 }
309
310 void clear()
311 {
312 mStack.clear();
313 mStackIt = mStack.begin();
314 logDebug("stack: 0 [cleared]");
315 }
316
317
318 void capFps()
319 {
320 if (mMaxFps < mTimestep)
321 {
322 logWarning("capping maximum fps to timestep (%f)", mTimestep);
323 mMaxFps = mTimestep;
324 }
325 }
326
327
328 Engine& mInterface;
329 VideoP mVideo;
330 Dispatch mDispatch;
331
332 std::list<LayerP> mStack;
333 std::list<LayerP>::iterator mStackIt;
334
335 Scalar mTimestep;
336 Scalar mMaxFps;
337
338 int mFps;
339 bool mPrintFps;
340 };
341
342
343 Engine::Engine() :
344 // pass through
345 mImpl(new Engine::Impl(*this)) {}
346
347 Engine& Engine::getInstance()
348 {
349 static Engine engine;
350 return engine;
351 }
352
353
354 void Engine::setVideo(VideoP video)
355 {
356 // pass through
357 mImpl->mVideo = video;
358 }
359
360 VideoP Engine::getVideo() const
361 {
362 return mImpl->mVideo;
363 }
364
365
366 void Engine::setTimestep(int ts)
367 {
368 mImpl->mTimestep = 1.0 / Scalar(ts);
369 mImpl->capFps();
370 }
371
372 int Engine::getTimestep() const
373 {
374 return int(1.0 / mImpl->mTimestep);
375 }
376
377
378 void Engine::setMaxFps(int maxFps)
379 {
380 mImpl->mMaxFps = 1.0 / Scalar(maxFps);
381 mImpl->capFps();
382 }
383
384 int Engine::getMaxFps() const
385 {
386 return int(1.0 / mImpl->mMaxFps);
387 }
388
389
390 int Engine::getFps() const
391 {
392 return mImpl->mFps;
393 }
394
395
396 void Engine::push(LayerP layer)
397 {
398 // pass through
399 mImpl->push(layer);
400 }
401
402 LayerP Engine::pop()
403 {
404 // pass through
405 return mImpl->pop();
406 }
407
408 LayerP Engine::pop(Layer* layer)
409 {
410 // pass through
411 return mImpl->pop(layer);
412 }
413
414 void Engine::clear()
415 {
416 // pass through
417 mImpl->clear();
418 }
419
420 int Engine::getSize() const
421 {
422 return mImpl->mStack.size();
423 }
424
425
426 void Engine::run()
427 {
428 // pass through
429 return mImpl->run();
430 }
431
432
433 Dispatch::Handler Engine::addHandler(const std::string& event,
434 const Dispatch::Function& callback)
435 {
436 return mImpl->mDispatch.addHandler(event, callback);
437 }
438
439 Dispatch::Handler Engine::addHandler(const std::string& event,
440 const Dispatch::Function& callback, Dispatch::Handler handler)
441 {
442 return mImpl->mDispatch.addHandler(event, callback, handler);
443 }
444
445 void Engine::dispatch(const std::string& event,
446 const Dispatch::Message* message)
447 {
448 mImpl->mDispatch.dispatch(event, message);
449 }
450
451
452 } // namespace Mf
453
454 /** vim: set ts=4 sw=4 tw=80: *************************************************/
455
This page took 0.054716 seconds and 5 git commands to generate.