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