2 /*******************************************************************************
4 Copyright (c) 2009, Charles McGarvey
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
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.
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.
27 *******************************************************************************/
30 #include <cstdlib> // exit, srand
31 #include <ctime> // time
37 #include "fastevents.h"
42 #include "Exception.hh"
45 #include "Settings.hh"
56 Impl(Engine
& engine
) :
61 // first, initialize the libraries
63 #if defined(_WIN32) || defined(__WIN32__)
64 if (SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_TIMER
) != 0)
66 if (SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_EVENTTHREAD
) != 0)
69 const char* error
= SDL_GetError();
70 throw Exception(ErrorCode::SDL_INIT
, error
);
75 SDL_VideoDriverName(vdName
, sizeof(vdName
));
76 logDebug("initialized SDL; using video driver `%s'", vdName
);
81 const char* error
= FE_GetError();
82 throw Exception(ErrorCode::FASTEVENTS_INIT
, error
);
85 mAlDevice
= alcOpenDevice(0);
86 mAlContext
= alcCreateContext(mAlDevice
, 0);
87 if (!mAlDevice
|| !mAlContext
)
89 const char* error
= alcGetString(mAlDevice
,alcGetError(mAlDevice
));
90 logError("error while creating audio context: %s", error
);
94 alcMakeContextCurrent(mAlContext
);
95 logDebug("opened sound device `%s'",
96 alcGetString(mAlDevice
, ALC_DEFAULT_DEVICE_SPECIFIER
));
99 // now load the settings the engine needs
101 Settings
& settings
= Settings::getInstance();
104 if (settings
.get("rngseed", randomSeed
)) srand(randomSeed
);
107 Scalar timestep
= 80.0;
108 settings
.get("timestep", timestep
);
109 mTimestep
= 1.0 / timestep
;
111 Scalar maxFps
= 40.0;
112 settings
.get("maxfps", maxFps
);
113 mMaxFps
= 1.0 / maxFps
;
116 settings
.get("printfps", mPrintFps
);
121 // the video object must be destroyed before we can shutdown SDL
124 alcMakeContextCurrent(0);
125 alcDestroyContext(mAlContext
);
126 alcCloseDevice(mAlDevice
);
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.
142 Scalar ticksNow
= Timer::getTicks();
144 Scalar nextStep
= ticksNow
;
145 Scalar nextDraw
= ticksNow
;
146 Scalar nextFpsUpdate
= ticksNow
+ 1.0;
148 Scalar totalTime
= 0.0;
149 Scalar deltaTime
= 0.0;
150 Scalar accumulator
= mTimestep
;
157 Scalar newTicks
= Timer::getTicks();
158 deltaTime
= newTicks
- ticksNow
;
161 // don't slow the animation until 4Hz, which is unplayable anyway
162 if (deltaTime
>= 0.25) deltaTime
= 0.25;
163 accumulator
+= deltaTime
;
165 Timer::fireIfExpired(ticksNow
);
168 while (accumulator
>= mTimestep
)
170 update(totalTime
, mTimestep
);
172 totalTime
+= mTimestep
;
173 accumulator
-= mTimestep
;
175 nextStep
+= mTimestep
;
177 if (ticksNow
>= nextStep
)
179 nextStep
= ticksNow
+ mTimestep
;
182 if (ticksNow
>= nextDraw
)
186 if (ticksNow
>= nextFpsUpdate
) // determine the actual fps
191 nextFpsUpdate
+= 1.0;
192 if (ticksNow
>= nextFpsUpdate
)
194 nextFpsUpdate
= ticksNow
+ 1.0;
199 logInfo("%d fps", mFps
);
203 draw(accumulator
/ mTimestep
);
207 if (ticksNow
>= nextDraw
)
209 // we missed some scheduled draws, so reset the schedule
210 nextDraw
= ticksNow
+ mMaxFps
;
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
);
218 while (!mStack
.empty());
220 mDispatch
.dispatch("engine.stopping");
223 void dispatchEvents()
227 while (FE_PollEvent(&event
) == 1)
232 if (event
.key
.keysym
.sym
== SDLK_ESCAPE
&&
233 (SDL_GetModState() & KMOD_CTRL
) )
236 logWarning("escape forced");
241 case SDL_VIDEORESIZE
:
242 mVideo
->resize(event
.resize
.w
, event
.resize
.h
);
251 void update(Scalar t
, Scalar dt
)
253 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
255 (*mStackIt
)->update(mInterface
, t
, dt
);
259 void draw(Scalar alpha
)
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
)
265 (*it
)->draw(mInterface
, alpha
);
269 void handleEvent(const Event
& event
)
271 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
273 if ((*mStackIt
)->handleEvent(mInterface
, event
)) break;
278 void push(LayerP layer
)
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
);
289 if (mStack
.begin() == mStackIt
) fixIt
= true;
291 LayerP layer
= mStack
.front();
293 logDebug("stack: %d [popped %X]", mStack
.size(), layer
.get());
294 layer
->popped(mInterface
);
296 if (fixIt
) mStackIt
= --mStack
.begin();
301 LayerP
pop(Layer
* layer
)
305 std::list
<LayerP
> layers
;
307 std::list
<LayerP
>::iterator it
;
308 for (it
= mStack
.begin(); it
!= mStack
.end(); ++it
)
310 layers
.push_back(*it
);
312 if (it
== mStackIt
) fixIt
= true;
314 if ((*it
).get() == layer
)
317 mStack
.erase(mStack
.begin(), it
);
319 for (it
= layers
.begin(); it
!= layers
.end(); ++it
)
321 (*it
)->popped(mInterface
);
322 logDebug("stack: %d [popped %X]", mStack
.size(), (*it
).get());
325 if (fixIt
) mStackIt
= --mStack
.begin();
327 return layers
.back();
337 mStackIt
= mStack
.begin();
338 logDebug("stack: 0 [cleared]");
344 if (mMaxFps
< mTimestep
)
346 logWarning("capping maximum fps to timestep (%f)", mTimestep
);
356 ALCdevice
* mAlDevice
;
357 ALCcontext
* mAlContext
;
359 std::list
<LayerP
> mStack
;
360 std::list
<LayerP
>::iterator mStackIt
;
372 mImpl(new Engine::Impl(*this)) {}
374 Engine
& Engine::getInstance()
376 static Engine engine
;
381 void Engine::setVideo(VideoP video
)
384 mImpl
->mVideo
= video
;
387 VideoP
Engine::getVideo() const
389 return mImpl
->mVideo
;
393 void Engine::setTimestep(int ts
)
395 mImpl
->mTimestep
= 1.0 / Scalar(ts
);
399 int Engine::getTimestep() const
401 return int(1.0 / mImpl
->mTimestep
);
405 void Engine::setMaxFps(int maxFps
)
407 mImpl
->mMaxFps
= 1.0 / Scalar(maxFps
);
411 int Engine::getMaxFps() const
413 return int(1.0 / mImpl
->mMaxFps
);
417 int Engine::getFps() const
423 void Engine::push(LayerP layer
)
435 LayerP
Engine::pop(Layer
* layer
)
438 return mImpl
->pop(layer
);
447 int Engine::getSize() const
449 return mImpl
->mStack
.size();
460 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
461 const Dispatch::Function
& callback
)
463 return mImpl
->mDispatch
.addHandler(event
, callback
);
466 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
467 const Dispatch::Function
& callback
, Dispatch::Handler handler
)
469 return mImpl
->mDispatch
.addHandler(event
, callback
, handler
);
472 void Engine::dispatch(const std::string
& event
,
473 const Dispatch::Message
* message
)
475 mImpl
->mDispatch
.dispatch(event
, message
);
481 /** vim: set ts=4 sw=4 tw=80: *************************************************/