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