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