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