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 `"
77 << vdName
<< "'" << std::endl
;
82 const char* error
= FE_GetError();
83 throw Exception(ErrorCode::FASTEVENTS_INIT
, error
);
86 mAlDevice
= alcOpenDevice(0);
87 mAlContext
= alcCreateContext(mAlDevice
, 0);
88 if (!mAlDevice
|| !mAlContext
)
90 const char* error
= alcGetString(mAlDevice
,alcGetError(mAlDevice
));
91 logError
<< "error while creating audio context: "
92 << error
<< std::endl
;
96 alcMakeContextCurrent(mAlContext
);
97 logDebug
<< "opened sound device `"
98 << alcGetString(mAlDevice
, ALC_DEFAULT_DEVICE_SPECIFIER
)
102 // now load the settings the engine needs
104 Settings
& settings
= Settings::getInstance();
107 if (settings
.get("rngseed", randomSeed
)) srand(randomSeed
);
110 Scalar timestep
= 80.0;
111 settings
.get("timestep", timestep
);
112 mTimestep
= 1.0 / timestep
;
114 Scalar maxFps
= 40.0;
115 settings
.get("maxfps", maxFps
);
116 mMaxFps
= 1.0 / maxFps
;
119 settings
.get("printfps", mPrintFps
);
124 // the video object must be destroyed before we can shutdown SDL
127 alcMakeContextCurrent(0);
128 alcDestroyContext(mAlContext
);
129 alcCloseDevice(mAlDevice
);
137 * The main loop. This just calls dispatchEvents(), update(), and draw()
138 * over and over again. The timing of the update and draw are decoupled.
139 * The actual frame rate is also calculated here. This function will return
140 * the exit code used to stop the loop.
145 Scalar ticksNow
= Timer::getTicks();
147 Scalar nextStep
= ticksNow
;
148 Scalar nextDraw
= ticksNow
;
149 Scalar nextFpsUpdate
= ticksNow
+ 1.0;
151 Scalar totalTime
= 0.0;
152 Scalar deltaTime
= 0.0;
153 Scalar accumulator
= mTimestep
;
160 Scalar newTicks
= Timer::getTicks();
161 deltaTime
= newTicks
- ticksNow
;
164 // don't slow the animation until 4Hz, which is unplayable anyway
165 if (deltaTime
>= 0.25) deltaTime
= 0.25;
166 accumulator
+= deltaTime
;
168 Timer::fireIfExpired(ticksNow
);
171 while (accumulator
>= mTimestep
)
173 update(totalTime
, mTimestep
);
175 totalTime
+= mTimestep
;
176 accumulator
-= mTimestep
;
178 nextStep
+= mTimestep
;
180 if (ticksNow
>= nextStep
)
182 nextStep
= ticksNow
+ mTimestep
;
185 if (ticksNow
>= nextDraw
)
189 if (ticksNow
>= nextFpsUpdate
) // determine the actual fps
194 nextFpsUpdate
+= 1.0;
195 if (ticksNow
>= nextFpsUpdate
)
197 nextFpsUpdate
= ticksNow
+ 1.0;
202 logInfo
<< mFps
<< " fps" << std::endl
;
206 draw(accumulator
/ mTimestep
);
210 if (ticksNow
>= nextDraw
)
212 // we missed some scheduled draws, so reset the schedule
213 nextDraw
= ticksNow
+ mMaxFps
;
217 // be a good citizen and give back what you don't need
218 Timer::sleep(std::min(std::min(nextStep
, nextDraw
),
219 Timer::getNextFire()), Timer::ACTUAL
);
221 while (!mStack
.empty());
223 mDispatch
.dispatch("engine.stopping");
226 void dispatchEvents()
230 while (FE_PollEvent(&event
) == 1)
235 if (event
.key
.keysym
.sym
== SDLK_ESCAPE
&&
236 (SDL_GetModState() & KMOD_CTRL
) )
239 logWarning("escape forced");
244 case SDL_VIDEORESIZE
:
245 mVideo
->resize(event
.resize
.w
, event
.resize
.h
);
254 void update(Scalar t
, Scalar dt
)
256 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
258 (*mStackIt
)->update(mInterface
, t
, dt
);
262 void draw(Scalar alpha
)
264 // FIXME - this will crash if the layer being drawn pops itself
265 std::list
<LayerP
>::reverse_iterator it
;
266 for (it
= mStack
.rbegin(); it
!= mStack
.rend(); ++it
)
268 (*it
)->draw(mInterface
, alpha
);
272 void handleEvent(const Event
& event
)
274 for (mStackIt
= mStack
.begin(); mStackIt
!= mStack
.end(); ++mStackIt
)
276 if ((*mStackIt
)->handleEvent(mInterface
, event
)) break;
281 void push(LayerP layer
)
283 ASSERT(layer
&& "cannot push null layer");
284 mStack
.push_front(layer
);
285 logDebug
<< "stack: " << mStack
.size()
286 << " [pushed " << layer
.get() << "]" << std::endl
;
287 layer
->pushed(mInterface
);
293 if (mStack
.begin() == mStackIt
) fixIt
= true;
295 LayerP layer
= mStack
.front();
297 logDebug
<< "stack: " << mStack
.size()
298 << " [popped " << layer
.get() << "]" << std::endl
;
299 layer
->popped(mInterface
);
301 if (fixIt
) mStackIt
= --mStack
.begin();
306 LayerP
pop(Layer
* layer
)
310 std::list
<LayerP
> layers
;
312 std::list
<LayerP
>::iterator it
;
313 for (it
= mStack
.begin(); it
!= mStack
.end(); ++it
)
315 layers
.push_back(*it
);
317 if (it
== mStackIt
) fixIt
= true;
319 if ((*it
).get() == layer
)
322 mStack
.erase(mStack
.begin(), it
);
324 for (it
= layers
.begin(); it
!= layers
.end(); ++it
)
326 (*it
)->popped(mInterface
);
327 logDebug
<< "stack: " << mStack
.size()
328 << " [popped " << (*it
).get() << "]" << std::endl
;
331 if (fixIt
) mStackIt
= --mStack
.begin();
333 return layers
.back();
343 mStackIt
= mStack
.begin();
344 logDebug("stack: 0 [cleared]");
350 if (mMaxFps
< mTimestep
)
352 logWarning
<< "capping maximum fps to timestep ("
353 << mTimestep
<< ")" << std::endl
;
363 ALCdevice
* mAlDevice
;
364 ALCcontext
* mAlContext
;
366 std::list
<LayerP
> mStack
;
367 std::list
<LayerP
>::iterator mStackIt
;
379 mImpl(new Engine::Impl(*this)) {}
381 Engine
& Engine::getInstance()
383 static Engine engine
;
388 void Engine::setVideo(VideoP video
)
391 mImpl
->mVideo
= video
;
394 VideoP
Engine::getVideo() const
396 return mImpl
->mVideo
;
400 void Engine::setTimestep(int ts
)
402 mImpl
->mTimestep
= 1.0 / Scalar(ts
);
406 int Engine::getTimestep() const
408 return int(1.0 / mImpl
->mTimestep
);
412 void Engine::setMaxFps(int maxFps
)
414 mImpl
->mMaxFps
= 1.0 / Scalar(maxFps
);
418 int Engine::getMaxFps() const
420 return int(1.0 / mImpl
->mMaxFps
);
424 int Engine::getFps() const
430 void Engine::push(LayerP layer
)
442 LayerP
Engine::pop(Layer
* layer
)
445 return mImpl
->pop(layer
);
454 int Engine::getSize() const
456 return mImpl
->mStack
.size();
467 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
468 const Dispatch::Function
& callback
)
470 return mImpl
->mDispatch
.addHandler(event
, callback
);
473 Dispatch::Handler
Engine::addHandler(const std::string
& event
,
474 const Dispatch::Function
& callback
, Dispatch::Handler handler
)
476 return mImpl
->mDispatch
.addHandler(event
, callback
, handler
);
479 void Engine::dispatch(const std::string
& event
,
480 const Dispatch::Message
* message
)
482 mImpl
->mDispatch
.dispatch(event
, message
);
488 /** vim: set ts=4 sw=4 tw=80: *************************************************/