de7c0cba94356afdc190110e97afcef3121cdc73
[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 <AL/alc.h>
35 #include <SDL/SDL.h>
36 #include "fastevents.h"
37
38
39 #include "Engine.hh"
40 #include "Event.hh"
41 #include "Exception.hh"
42 #include "Log.hh"
43 #include "Math.hh"
44 #include "Random.hh"
45 #include "Settings.hh"
46 #include "Timer.hh"
47
48
49 namespace Mf {
50
51
52 class Engine::Impl
53 {
54 public:
55
56 Impl(Engine& engine) :
57 mInterface(engine),
58 mTimestep(0.01),
59 mPrintFps(false)
60 {
61 // first, initialize the libraries
62
63 #if defined(_WIN32) || defined(__WIN32__)
64 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
65 #else
66 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
67 #endif
68 {
69 const char* error = SDL_GetError();
70 throw Exception(ErrorCode::SDL_INIT, error);
71 }
72
73 if (FE_Init() != 0)
74 {
75 const char* error = FE_GetError();
76 throw Exception(ErrorCode::FASTEVENTS_INIT, error);
77 }
78
79 mAlDevice = alcOpenDevice(0);
80 mAlContext = alcCreateContext(mAlDevice, 0);
81 if (!mAlDevice || !mAlContext)
82 {
83 const char* error = alcGetString(mAlDevice,alcGetError(mAlDevice));
84 logError("error while creating audio context: %s", error);
85 }
86 else
87 {
88 alcMakeContextCurrent(mAlContext);
89 logDebug("opened sound device \"%s\"",
90 alcGetString(mAlDevice, ALC_DEFAULT_DEVICE_SPECIFIER));
91 }
92
93 // now load the settings the engine needs
94
95 Settings& settings = Settings::getInstance();
96
97 unsigned randomSeed;
98 if (settings.get("rngseed", randomSeed)) setSeed(randomSeed);
99 else setSeed();
100
101 Scalar timestep = 80.0;
102 settings.get("timestep", timestep);
103 mTimestep = 1.0 / timestep;
104
105 Scalar maxFps = 40.0;
106 settings.get("maxfps", maxFps);
107 mMaxFps = 1.0 / maxFps;
108 capFps();
109
110 settings.get("printfps", mPrintFps);
111 }
112
113 ~Impl()
114 {
115 // the video object must be destroyed before we can shutdown SDL
116 mVideo.reset();
117
118 alcMakeContextCurrent(0);
119 alcDestroyContext(mAlContext);
120 alcCloseDevice(mAlDevice);
121
122 FE_Quit();
123 SDL_Quit();
124 }
125
126
127 /**
128 * The main loop. This just calls dispatchEvents(), update(), and draw()
129 * over and over again. The timing of the update and draw are decoupled.
130 * The actual frame rate is also calculated here. This function will return
131 * the exit code used to stop the loop.
132 */
133
134 void run()
135 {
136 Scalar ticksNow = Timer::getTicks();
137
138 Scalar nextStep = ticksNow;
139 Scalar nextDraw = ticksNow;
140 Scalar nextFpsUpdate = ticksNow + 1.0;
141
142 Scalar totalTime = 0.0;
143 Scalar deltaTime = 0.0;
144 Scalar accumulator = mTimestep;
145
146 mFps = 0;
147 int frameAccum = 0;
148
149 do
150 {
151 Scalar newTicks = Timer::getTicks();
152 deltaTime = newTicks - ticksNow;
153 ticksNow = newTicks;
154
155 // don't slow the animation until 4Hz, which is unplayable anyway
156 if (deltaTime >= 0.25) deltaTime = 0.25;
157 accumulator += deltaTime;
158
159 Timer::fireIfExpired(ticksNow);
160 dispatchEvents();
161
162 while (accumulator >= mTimestep)
163 {
164 update(totalTime, mTimestep);
165
166 totalTime += mTimestep;
167 accumulator -= mTimestep;
168
169 nextStep += mTimestep;
170 }
171 if (ticksNow >= nextStep)
172 {
173 nextStep = ticksNow + mTimestep;
174 }
175
176 if (ticksNow >= nextDraw)
177 {
178 frameAccum++;
179
180 if (ticksNow >= nextFpsUpdate) // determine the actual fps
181 {
182 mFps = frameAccum;
183 frameAccum = 0;
184
185 nextFpsUpdate += 1.0;
186 if (ticksNow >= nextFpsUpdate)
187 {
188 nextFpsUpdate = ticksNow + 1.0;
189 }
190
191 if (mPrintFps)
192 {
193 logInfo("%d fps", mFps);
194 }
195 }
196
197 draw(accumulator / mTimestep);
198 mVideo->swap();
199
200 nextDraw += mMaxFps;
201 if (ticksNow >= nextDraw)
202 {
203 // we missed some scheduled draws, so reset the schedule
204 nextDraw = ticksNow + mMaxFps;
205 }
206 }
207
208 // be a good citizen and give back what you don't need
209 Timer::sleep(std::min(std::min(nextStep, nextDraw),
210 Timer::getNextFire()), Timer::ACTUAL);
211 }
212 while (!mStack.empty());
213 }
214
215 void dispatchEvents()
216 {
217 SDL_Event event;
218
219 while (FE_PollEvent(&event) == 1)
220 {
221 switch (event.type)
222 {
223 case SDL_KEYDOWN:
224 if (event.key.keysym.sym == SDLK_ESCAPE &&
225 (SDL_GetModState() & KMOD_CTRL) )
226 {
227 // emergency escape
228 logWarning("escape forced");
229 exit(1);
230 }
231 break;
232
233 case SDL_VIDEORESIZE:
234 mVideo->resize(event.resize.w, event.resize.h);
235 break;
236 }
237
238 handleEvent(event);
239 }
240 }
241
242
243 void update(Scalar t, Scalar dt)
244 {
245 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
246 {
247 (*mStackIt)->update(mInterface, t, dt);
248 }
249 }
250
251 void draw(Scalar alpha)
252 {
253 // FIXME - this will crash if the layer being drawn pops itself
254 std::list<LayerP>::reverse_iterator it;
255 for (it = mStack.rbegin(); it != mStack.rend(); ++it)
256 {
257 (*it)->draw(mInterface, alpha);
258 }
259 }
260
261 void handleEvent(const Event& event)
262 {
263 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
264 {
265 if ((*mStackIt)->handleEvent(mInterface, event)) break;
266 }
267 }
268
269
270 void push(LayerP layer)
271 {
272 ASSERT(layer && "cannot push null layer");
273 mStack.push_front(layer);
274 logDebug("stack: %d [pushed %X]", mStack.size(), layer.get());
275 layer->pushed(mInterface);
276 }
277
278 LayerP pop()
279 {
280 bool fixIt = false;
281 if (mStack.begin() == mStackIt) fixIt = true;
282
283 LayerP layer = mStack.front();
284 mStack.pop_front();
285 logDebug("stack: %d [popped %X]", mStack.size(), layer.get());
286 layer->popped(mInterface);
287
288 if (fixIt) mStackIt = --mStack.begin();
289
290 return layer;
291 }
292
293 LayerP pop(Layer* layer)
294 {
295 bool fixIt = false;
296
297 std::list<LayerP> layers;
298
299 std::list<LayerP>::iterator it;
300 for (it = mStack.begin(); it != mStack.end(); ++it)
301 {
302 layers.push_back(*it);
303
304 if (it == mStackIt) fixIt = true;
305
306 if ((*it).get() == layer)
307 {
308 ++it;
309 mStack.erase(mStack.begin(), it);
310
311 for (it = layers.begin(); it != layers.end(); ++it)
312 {
313 (*it)->popped(mInterface);
314 logDebug("stack: %d [popped %X]", mStack.size(), (*it).get());
315 }
316
317 if (fixIt) mStackIt = --mStack.begin();
318
319 return layers.back();
320 }
321 }
322
323 return LayerP();
324 }
325
326 void clear()
327 {
328 mStack.clear();
329 mStackIt = mStack.begin();
330 logDebug("stack: 0 [cleared]");
331 }
332
333
334 void capFps()
335 {
336 if (mMaxFps < mTimestep)
337 {
338 logWarning("capping maximum fps to timestep (%f)", mTimestep);
339 mMaxFps = mTimestep;
340 }
341 }
342
343
344 Engine& mInterface;
345 VideoP mVideo;
346 Dispatch mDispatch;
347
348 ALCdevice* mAlDevice;
349 ALCcontext* mAlContext;
350
351 std::list<LayerP> mStack;
352 std::list<LayerP>::iterator mStackIt;
353
354 Scalar mTimestep;
355 Scalar mMaxFps;
356
357 int mFps;
358 bool mPrintFps;
359 };
360
361
362 Engine::Engine() :
363 // pass through
364 mImpl(new Engine::Impl(*this)) {}
365
366 Engine& Engine::getInstance()
367 {
368 static Engine engine;
369 return engine;
370 }
371
372
373 void Engine::setVideo(VideoP video)
374 {
375 // pass through
376 mImpl->mVideo = video;
377 }
378
379 VideoP Engine::getVideo() const
380 {
381 return mImpl->mVideo;
382 }
383
384
385 void Engine::setTimestep(int ts)
386 {
387 mImpl->mTimestep = 1.0 / Scalar(ts);
388 mImpl->capFps();
389 }
390
391 int Engine::getTimestep() const
392 {
393 return int(1.0 / mImpl->mTimestep);
394 }
395
396
397 void Engine::setMaxFps(int maxFps)
398 {
399 mImpl->mMaxFps = 1.0 / Scalar(maxFps);
400 mImpl->capFps();
401 }
402
403 int Engine::getMaxFps() const
404 {
405 return int(1.0 / mImpl->mMaxFps);
406 }
407
408
409 int Engine::getFps() const
410 {
411 return mImpl->mFps;
412 }
413
414
415 void Engine::push(LayerP layer)
416 {
417 // pass through
418 mImpl->push(layer);
419 }
420
421 LayerP Engine::pop()
422 {
423 // pass through
424 return mImpl->pop();
425 }
426
427 LayerP Engine::pop(Layer* layer)
428 {
429 // pass through
430 return mImpl->pop(layer);
431 }
432
433 void Engine::clear()
434 {
435 // pass through
436 mImpl->clear();
437 }
438
439 int Engine::getSize() const
440 {
441 return mImpl->mStack.size();
442 }
443
444
445 void Engine::run()
446 {
447 // pass through
448 return mImpl->run();
449 }
450
451
452 Dispatch::Handler Engine::addHandler(const std::string& event,
453 const Dispatch::Function& callback)
454 {
455 return mImpl->mDispatch.addHandler(event, callback);
456 }
457
458 Dispatch::Handler Engine::addHandler(const std::string& event,
459 const Dispatch::Function& callback, Dispatch::Handler handler)
460 {
461 return mImpl->mDispatch.addHandler(event, callback, handler);
462 }
463
464 void Engine::dispatch(const std::string& event,
465 const Dispatch::Message* message)
466 {
467 mImpl->mDispatch.dispatch(event, message);
468 }
469
470
471 } // namespace Mf
472
473 /** vim: set ts=4 sw=4 tw=80: *************************************************/
474
This page took 0.047773 seconds and 3 git commands to generate.