]> Dogcows Code - chaz/yoink/blob - src/Moof/Engine.cc
new level-based controllers
[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 "Dispatcher.hh"
39 #include "Engine.hh"
40 #include "Event.hh"
41 #include "Log.hh"
42 #include "Random.hh"
43 #include "Settings.hh"
44 #include "Timer.hh"
45 #include "Video.hh"
46
47
48 namespace Mf {
49
50
51 class Engine::Impl
52 {
53 public:
54 Impl(int argc, char* argv[], const std::string& name,
55 const std::string& iconFile, const std::string& configFile,
56 Engine& engine) :
57 interface(engine),
58 timestep(0.01),
59 printFps(false)
60 {
61 #if defined(_WIN32) || defined (_WIN64) || 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 logError("sdl is complaining: %s", SDL_GetError());
68 throw Exception(Exception::SDL_ERROR);
69 }
70 if (FE_Init() != 0)
71 {
72 logError("fast events error: %s", FE_GetError());
73 throw Exception(Exception::SDL_ERROR);
74 }
75 alutInit(&argc, argv);
76
77 Settings& settings = Settings::getInstance();
78 settings.loadFromFile(configFile);
79 settings.parseArgs(argc, argv);
80
81 long randomSeed;
82 if (settings.get("rngseed", randomSeed)) setSeed(randomSeed);
83 else setSeed();
84
85 settings.get("timestep", timestep);
86
87 long maxFps = 40;
88 settings.get("maxfps", maxFps);
89 drawRate = 1.0 / Scalar(maxFps);
90
91 settings.get("printfps", printFps);
92
93 video = Video::alloc(name, iconFile);
94 video->makeActive();
95 }
96
97 ~Impl()
98 {
99 // the video object must be destroyed before we can shutdown SDL
100 video.reset();
101
102 alutExit();
103 FE_Quit();
104 SDL_Quit();
105 }
106
107
108 /**
109 * The main loop. This just calls dispatchEvents(), update(), and draw()
110 * over and over again. The timing of the update and draw are decoupled.
111 * The actual frame rate is also calculated here. This function will return
112 * the exit code used to stop the loop.
113 */
114
115 void run()
116 {
117 Scalar ticksNow = Timer::getTicks();
118
119 Scalar nextStep = ticksNow;
120 Scalar nextDraw = ticksNow;
121 Scalar nextFpsUpdate = ticksNow + 1.0;
122
123 Scalar totalTime = 0.0;
124 Scalar deltaTime = 0.0;
125 Scalar accumulator = timestep;
126
127 fps = 0;
128 int frameAccum = 0;
129
130 do
131 {
132 Scalar newTicks = Timer::getTicks();
133 deltaTime = newTicks - ticksNow;
134 ticksNow = newTicks;
135
136 if (deltaTime >= 0.25) deltaTime = 0.25;
137 accumulator += deltaTime;
138
139 Timer::fireIfExpired(ticksNow);
140
141 while (accumulator >= timestep)
142 {
143 dispatchEvents();
144 update(totalTime, timestep);
145
146 totalTime += timestep;
147 accumulator -= timestep;
148
149 nextStep += timestep;
150 }
151 if (ticksNow >= nextStep)
152 {
153 nextStep = ticksNow + timestep;
154 }
155
156 if (ticksNow >= nextDraw)
157 {
158 frameAccum++;
159
160 if (ticksNow >= nextFpsUpdate) // determine the actual fps
161 {
162 fps = frameAccum;
163 frameAccum = 0;
164
165 nextFpsUpdate += 1.0;
166 if (ticksNow >= nextFpsUpdate)
167 {
168 nextFpsUpdate = ticksNow + 1.0;
169 }
170
171 if (printFps)
172 {
173 logInfo("%d fps", fps);
174 }
175 }
176
177 draw(accumulator / timestep);
178 video->swap();
179
180 nextDraw += drawRate;
181 if (ticksNow >= nextDraw)
182 {
183 // we missed some scheduled draws, so reset the schedule
184 nextDraw = ticksNow + drawRate;
185 }
186 }
187
188 // be a good citizen and give back what you don't need
189 Timer::sleep(std::min(std::min(nextStep, nextDraw),
190 Timer::getNextFire()), true);
191 }
192 while (!stack.empty());
193 }
194
195 void dispatchEvents()
196 {
197 SDL_Event event;
198
199 while (FE_PollEvent(&event) == 1)
200 {
201 switch (event.type)
202 {
203 case SDL_KEYDOWN:
204 if (event.key.keysym.sym == SDLK_ESCAPE &&
205 (SDL_GetModState() & KMOD_CTRL) )
206 {
207 // emergency escape
208 exit(0);
209 }
210 break;
211
212 case SDL_VIDEORESIZE:
213 video->resize(event.resize.w, event.resize.h);
214 break;
215 }
216
217 handleEvent(event);
218 }
219 }
220
221
222 void update(Scalar t, Scalar dt)
223 {
224 for (stackIt = stack.begin(); stackIt != stack.end(); ++stackIt)
225 {
226 (*stackIt)->update(t, dt);
227 }
228 }
229
230 void draw(Scalar alpha)
231 {
232 // FIXME - this will crash if the layer being drawn pops itself
233 std::list<LayerP>::reverse_iterator it;
234 for (it = stack.rbegin(); it != stack.rend(); ++it)
235 {
236 (*it)->draw(alpha);
237 }
238 }
239
240 void handleEvent(const Event& event)
241 {
242 for (stackIt = stack.begin(); stackIt != stack.end(); ++stackIt)
243 {
244 if ((*stackIt)->handleEvent(event)) break;
245 }
246 }
247
248
249 void pushLayer(LayerP layer)
250 {
251 ASSERT(layer && "cannot push null layer");
252 stack.push_front(layer);
253 layer->pushed(interface);
254 }
255
256 void popLayer()
257 {
258 bool fixIt = false;
259 if (stack.begin() == stackIt) fixIt = true;
260
261 LayerP popped = stack.front();
262 stack.pop_front();
263 popped->popped(interface);
264
265 if (fixIt) stackIt = --stack.begin();
266 }
267
268 void popLayer(Layer* layer)
269 {
270 bool fixIt = false;
271
272 std::list<LayerP>::iterator it;
273 for (it = stack.begin(); it != stack.end(); ++it)
274 {
275 if (it == stackIt) fixIt = true;
276
277 if ((*it).get() == layer)
278 {
279 ++it;
280 do
281 {
282 LayerP popped = stack.front();
283 stack.pop_front();
284 popped->popped(interface);
285 }
286 while (stack.begin() != it);
287
288 if (fixIt) stackIt = --stack.begin();
289 return;
290 }
291 }
292 }
293
294 void clearLayers()
295 {
296 stack.clear();
297 stackIt = stack.begin();
298 }
299
300
301 Engine& interface;
302
303 VideoP video;
304
305 std::list<LayerP> stack;
306 std::list<LayerP>::iterator stackIt;
307
308 Scalar timestep;
309 Scalar drawRate;
310
311 long fps;
312 bool printFps;
313 };
314
315
316 static Engine* instance = 0;
317
318 Engine::Engine(int argc, char* argv[], const std::string& name,
319 const std::string& iconFile, const std::string& configFile) :
320 impl_(new Engine::Impl(argc, argv, name, iconFile, configFile, *this))
321 {
322 instance = this;
323 }
324
325
326 Engine& Engine::getInstance()
327 {
328 ASSERT(instance && "dereferencing null pointer");
329 return *instance;
330 }
331
332
333 void Engine::run()
334 {
335 return impl_->run();
336 }
337
338 void Engine::setTimestep(Scalar ts)
339 {
340 impl_->timestep = ts;
341 }
342
343 Scalar Engine::getTimestep() const
344 {
345 return impl_->timestep;
346 }
347
348 void Engine::setMaxFrameRate(long maxFps)
349 {
350 impl_->drawRate = 1.0 / Scalar(maxFps);
351 }
352
353 long Engine::getMaxFrameRate() const
354 {
355 return long(1.0 / impl_->drawRate);
356 }
357
358
359 Video& Engine::getVideo() const
360 {
361 return *impl_->video;
362 }
363
364 long Engine::getFrameRate() const
365 {
366 return impl_->fps;
367 }
368
369
370 void Engine::pushLayer(LayerP layer)
371 {
372 // pass through
373 impl_->pushLayer(layer);
374 }
375
376 void Engine::popLayer()
377 {
378 // pass through
379 impl_->popLayer();
380 }
381
382 void Engine::popLayer(Layer* layer)
383 {
384 // pass through
385 impl_->popLayer(layer);
386 }
387
388 void Engine::clearLayers()
389 {
390 // pass through
391 impl_->clearLayers();
392 }
393
394
395 } // namespace Mf
396
397 /** vim: set ts=4 sw=4 tw=80: *************************************************/
398
This page took 0.050414 seconds and 4 git commands to generate.