]> Dogcows Code - chaz/yoink/blob - src/Moof/Engine.cc
experimental shapes hierarchy and raycasting
[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 mDispatch.dispatch("engine.stopping");
221 }
222
223 void dispatchEvents()
224 {
225 SDL_Event event;
226
227 while (FE_PollEvent(&event) == 1)
228 {
229 switch (event.type)
230 {
231 case SDL_KEYDOWN:
232 if (event.key.keysym.sym == SDLK_ESCAPE &&
233 (SDL_GetModState() & KMOD_CTRL) )
234 {
235 // emergency escape
236 logWarning("escape forced");
237 exit(1);
238 }
239 break;
240
241 case SDL_VIDEORESIZE:
242 mVideo->resize(event.resize.w, event.resize.h);
243 break;
244 }
245
246 handleEvent(event);
247 }
248 }
249
250
251 void update(Scalar t, Scalar dt)
252 {
253 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
254 {
255 (*mStackIt)->update(mInterface, t, dt);
256 }
257 }
258
259 void draw(Scalar alpha)
260 {
261 // FIXME - this will crash if the layer being drawn pops itself
262 std::list<LayerP>::reverse_iterator it;
263 for (it = mStack.rbegin(); it != mStack.rend(); ++it)
264 {
265 (*it)->draw(mInterface, alpha);
266 }
267 }
268
269 void handleEvent(const Event& event)
270 {
271 for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
272 {
273 if ((*mStackIt)->handleEvent(mInterface, event)) break;
274 }
275 }
276
277
278 void push(LayerP layer)
279 {
280 ASSERT(layer && "cannot push null layer");
281 mStack.push_front(layer);
282 logDebug("stack: %d [pushed %X]", mStack.size(), layer.get());
283 layer->pushed(mInterface);
284 }
285
286 LayerP pop()
287 {
288 bool fixIt = false;
289 if (mStack.begin() == mStackIt) fixIt = true;
290
291 LayerP layer = mStack.front();
292 mStack.pop_front();
293 logDebug("stack: %d [popped %X]", mStack.size(), layer.get());
294 layer->popped(mInterface);
295
296 if (fixIt) mStackIt = --mStack.begin();
297
298 return layer;
299 }
300
301 LayerP pop(Layer* layer)
302 {
303 bool fixIt = false;
304
305 std::list<LayerP> layers;
306
307 std::list<LayerP>::iterator it;
308 for (it = mStack.begin(); it != mStack.end(); ++it)
309 {
310 layers.push_back(*it);
311
312 if (it == mStackIt) fixIt = true;
313
314 if ((*it).get() == layer)
315 {
316 ++it;
317 mStack.erase(mStack.begin(), it);
318
319 for (it = layers.begin(); it != layers.end(); ++it)
320 {
321 (*it)->popped(mInterface);
322 logDebug("stack: %d [popped %X]", mStack.size(), (*it).get());
323 }
324
325 if (fixIt) mStackIt = --mStack.begin();
326
327 return layers.back();
328 }
329 }
330
331 return LayerP();
332 }
333
334 void clear()
335 {
336 mStack.clear();
337 mStackIt = mStack.begin();
338 logDebug("stack: 0 [cleared]");
339 }
340
341
342 void capFps()
343 {
344 if (mMaxFps < mTimestep)
345 {
346 logWarning("capping maximum fps to timestep (%f)", mTimestep);
347 mMaxFps = mTimestep;
348 }
349 }
350
351
352 Engine& mInterface;
353 VideoP mVideo;
354 Dispatch mDispatch;
355
356 ALCdevice* mAlDevice;
357 ALCcontext* mAlContext;
358
359 std::list<LayerP> mStack;
360 std::list<LayerP>::iterator mStackIt;
361
362 Scalar mTimestep;
363 Scalar mMaxFps;
364
365 int mFps;
366 bool mPrintFps;
367 };
368
369
370 Engine::Engine() :
371 // pass through
372 mImpl(new Engine::Impl(*this)) {}
373
374 Engine& Engine::getInstance()
375 {
376 static Engine engine;
377 return engine;
378 }
379
380
381 void Engine::setVideo(VideoP video)
382 {
383 // pass through
384 mImpl->mVideo = video;
385 }
386
387 VideoP Engine::getVideo() const
388 {
389 return mImpl->mVideo;
390 }
391
392
393 void Engine::setTimestep(int ts)
394 {
395 mImpl->mTimestep = 1.0 / Scalar(ts);
396 mImpl->capFps();
397 }
398
399 int Engine::getTimestep() const
400 {
401 return int(1.0 / mImpl->mTimestep);
402 }
403
404
405 void Engine::setMaxFps(int maxFps)
406 {
407 mImpl->mMaxFps = 1.0 / Scalar(maxFps);
408 mImpl->capFps();
409 }
410
411 int Engine::getMaxFps() const
412 {
413 return int(1.0 / mImpl->mMaxFps);
414 }
415
416
417 int Engine::getFps() const
418 {
419 return mImpl->mFps;
420 }
421
422
423 void Engine::push(LayerP layer)
424 {
425 // pass through
426 mImpl->push(layer);
427 }
428
429 LayerP Engine::pop()
430 {
431 // pass through
432 return mImpl->pop();
433 }
434
435 LayerP Engine::pop(Layer* layer)
436 {
437 // pass through
438 return mImpl->pop(layer);
439 }
440
441 void Engine::clear()
442 {
443 // pass through
444 mImpl->clear();
445 }
446
447 int Engine::getSize() const
448 {
449 return mImpl->mStack.size();
450 }
451
452
453 void Engine::run()
454 {
455 // pass through
456 return mImpl->run();
457 }
458
459
460 Dispatch::Handler Engine::addHandler(const std::string& event,
461 const Dispatch::Function& callback)
462 {
463 return mImpl->mDispatch.addHandler(event, callback);
464 }
465
466 Dispatch::Handler Engine::addHandler(const std::string& event,
467 const Dispatch::Function& callback, Dispatch::Handler handler)
468 {
469 return mImpl->mDispatch.addHandler(event, callback, handler);
470 }
471
472 void Engine::dispatch(const std::string& event,
473 const Dispatch::Message* message)
474 {
475 mImpl->mDispatch.dispatch(event, message);
476 }
477
478
479 } // namespace Mf
480
481 /** vim: set ts=4 sw=4 tw=80: *************************************************/
482
This page took 0.054571 seconds and 4 git commands to generate.