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
36 #include "fastevents.h"
41 #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 const char* error
= FE_GetError();
76 throw Exception(ErrorCode::FASTEVENTS_INIT
, error
);
79 mAlDevice
= alcOpenDevice(0);
80 mAlContext
= alcCreateContext(mAlDevice
, 0);
81 if (!mAlDevice
|| !mAlContext
)
83 const char* error
= alcGetString(mAlDevice
,alcGetError(mAlDevice
));
84 logError("error while creating audio context: %s", error
);
88 alcMakeContextCurrent(mAlContext
);
89 logDebug("opened sound device \"%s\"",
90 alcGetString(mAlDevice
, ALC_DEFAULT_DEVICE_SPECIFIER
));
93 // now load the settings the engine needs
95 Settings
& settings
= Settings::getInstance();
98 if (settings
.get("rngseed", randomSeed
)) setSeed(randomSeed
);
101 Scalar timestep
= 80.0;
102 settings
.get("timestep", timestep
);
103 mTimestep
= 1.0 / timestep
;
105 Scalar maxFps
= 40.0;
106 settings
.get("maxfps", maxFps
);
107 mMaxFps
= 1.0 / maxFps
;
110 settings
.get("printfps", mPrintFps
);
115 // the video object must be destroyed before we can shutdown SDL
118 alcMakeContextCurrent(0);
119 alcDestroyContext(mAlContext
);
120 alcCloseDevice(mAlDevice
);
128 * The main loop. This just calls dispatchEvents(), update(), and draw()
129 * over and over again. The timing of the update and draw are decoupled.
130 * The actual frame rate is also calculated here. This function will return
131 * the exit code used to stop the loop.
136 Scalar ticksNow
= Timer::getTicks();
138 Scalar nextStep
= ticksNow
;
139 Scalar nextDraw
= ticksNow
;
140 Scalar nextFpsUpdate
= ticksNow
+ 1.0;
142 Scalar totalTime
= 0.0;
143 Scalar deltaTime
= 0.0;
144 Scalar accumulator
= mTimestep
;
151 Scalar newTicks
= Timer::getTicks();
152 deltaTime
= newTicks
- ticksNow
;
155 // don't slow the animation until 4Hz, which is unplayable anyway
156 if (deltaTime
>= 0.25) deltaTime
= 0.25;
157 accumulator
+= deltaTime
;
159 Timer::fireIfExpired(ticksNow
);
162 while (accumulator
>= mTimestep
)
164 update(totalTime
, mTimestep
);
166 totalTime
+= mTimestep
;
167 accumulator
-= mTimestep
;
169 nextStep
+= mTimestep
;
171 if (ticksNow
>= nextStep
)
173 nextStep
= ticksNow
+ mTimestep
;
176 if (ticksNow
>= nextDraw
)
180 if (ticksNow
>= nextFpsUpdate
) // determine the actual fps
185 nextFpsUpdate
+= 1.0;
186 if (ticksNow
>= nextFpsUpdate
)
188 nextFpsUpdate
= ticksNow
+ 1.0;
193 logInfo("%d fps", mFps
);
197 draw(accumulator
/ mTimestep
);
201 if (ticksNow
>= nextDraw
)
203 // we missed some scheduled draws, so reset the schedule
204 nextDraw
= ticksNow
+ mMaxFps
;
208 // be a good citizen and give back what you don't need
209 Timer::sleep(std::min(std::min(nextStep
, nextDraw
),
210 Timer::getNextFire()), Timer::ACTUAL
);
212 while (!mStack
.empty());
215 void dispatchEvents()
219 while (FE_PollEvent(&event
) == 1)
224 if (event
.key
.keysym
.sym
== SDLK_ESCAPE
&&
225 (SDL_GetModState() & KMOD_CTRL
) )
228 logWarning("escape forced");
233 case SDL_VIDEORESIZE
:
234 mVideo
->resize(event
.resize
.w
, event
.resize
.h
);
243 void update(Scalar t
, Scalar dt
)
245 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
247 (*mStackIt
)->update(mInterface
, t
, dt
);
251 void draw(Scalar alpha
)
253 // FIXME - this will crash if the layer being drawn pops itself
254 std::list
<LayerP
>::reverse_iterator it
;
255 for (it
= mStack
.rbegin(); it
!= mStack
.rend(); ++it
)
257 (*it
)->draw(mInterface
, alpha
);
261 void handleEvent(const Event
& event
)
263 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
265 if ((*mStackIt
)->handleEvent(mInterface
, event
)) break;
270 void push(LayerP layer
)
272 ASSERT(layer
&& "cannot push null layer");
273 mStack
.push_front(layer
);
274 logDebug("stack: %d [pushed %X]", mStack
.size(), layer
.get());
275 layer
->pushed(mInterface
);
281 if (mStack
.begin() == mStackIt
) fixIt
= true;
283 LayerP layer
= mStack
.front();
285 logDebug("stack: %d [popped %X]", mStack
.size(), layer
.get());
286 layer
->popped(mInterface
);
288 if (fixIt
) mStackIt
= --mStack
.begin();
293 LayerP
pop(Layer
* layer
)
297 std::list
<LayerP
> layers
;
299 std::list
<LayerP
>::iterator it
;
300 for (it
= mStack
.begin(); it
!= mStack
.end(); ++it
)
302 layers
.push_back(*it
);
304 if (it
== mStackIt
) fixIt
= true;
306 if ((*it
).get() == layer
)
309 mStack
.erase(mStack
.begin(), it
);
311 for (it
= layers
.begin(); it
!= layers
.end(); ++it
)
313 (*it
)->popped(mInterface
);
314 logDebug("stack: %d [popped %X]", mStack
.size(), (*it
).get());
317 if (fixIt
) mStackIt
= --mStack
.begin();
319 return layers
.back();
329 mStackIt
= mStack
.begin();
330 logDebug("stack: 0 [cleared]");
336 if (mMaxFps
< mTimestep
)
338 logWarning("capping maximum fps to timestep (%f)", mTimestep
);
348 ALCdevice
* mAlDevice
;
349 ALCcontext
* mAlContext
;
351 std::list
<LayerP
> mStack
;
352 std::list
<LayerP
>::iterator mStackIt
;
364 mImpl(new Engine::Impl(*this)) {}
366 Engine
& Engine::getInstance()
368 static Engine engine
;
373 void Engine::setVideo(VideoP video
)
376 mImpl
->mVideo
= video
;
379 VideoP
Engine::getVideo() const
381 return mImpl
->mVideo
;
385 void Engine::setTimestep(int ts
)
387 mImpl
->mTimestep
= 1.0 / Scalar(ts
);
391 int Engine::getTimestep() const
393 return int(1.0 / mImpl
->mTimestep
);
397 void Engine::setMaxFps(int maxFps
)
399 mImpl
->mMaxFps
= 1.0 / Scalar(maxFps
);
403 int Engine::getMaxFps() const
405 return int(1.0 / mImpl
->mMaxFps
);
409 int Engine::getFps() const
415 void Engine::push(LayerP layer
)
427 LayerP
Engine::pop(Layer
* layer
)
430 return mImpl
->pop(layer
);
439 int Engine::getSize() const
441 return mImpl
->mStack
.size();
452 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
453 const Dispatch::Function
& callback
)
455 return mImpl
->mDispatch
.addHandler(event
, callback
);
458 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
459 const Dispatch::Function
& callback
, Dispatch::Handler handler
)
461 return mImpl
->mDispatch
.addHandler(event
, callback
, handler
);
464 void Engine::dispatch(const std::string
& event
,
465 const Dispatch::Message
* message
)
467 mImpl
->mDispatch
.dispatch(event
, message
);
473 /** vim: set ts=4 sw=4 tw=80: *************************************************/