*******************************************************************************/
-#include <cstdlib> // exit
+#include <algorithm>
+#include <cstdlib> // exit, srand
+#include <ctime> // time
+#include <list>
#include <string>
+#include <AL/alc.h>
#include <SDL/SDL.h>
#include "fastevents.h"
-#include <SDL/SDL_sound.h>
-#include <AL/alut.h>
-#include "Dispatcher.hh"
+
#include "Engine.hh"
+#include "Event.hh"
+#include "Exception.hh"
#include "Log.hh"
-#include "Random.hh"
+#include "Math.hh"
#include "Settings.hh"
#include "Timer.hh"
-#include "Video.hh"
namespace Mf {
class Engine::Impl
{
public:
- Impl(int argc, char* argv[], const std::string& configFile,
- const std::string& name, const std::string& iconFile,
- Engine* outer) :
- interface(outer)
+
+ Impl(Engine& engine) :
+ mInterface(engine),
+ mTimestep(0.01),
+ mPrintFps(false)
{
-#if defined(_WIN32) || defined (_WIN64) || defined(__WIN32__)
- if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
+ // first, initialize the libraries
+
+#if defined(_WIN32) || defined(__WIN32__)
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
#else
- if (SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_EVENTTHREAD) != 0)
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
#endif
{
- logError("sdl is complaining: %s", SDL_GetError());
- throw Exception(Exception::SDL_ERROR);
+ const char* error = SDL_GetError();
+ throw Exception(ErrorCode::SDL_INIT, error);
}
+ else
+ {
+ char vdName[128];
+ SDL_VideoDriverName(vdName, sizeof(vdName));
+ logDebug("initialized SDL; using video driver `%s'", vdName);
+ }
+
if (FE_Init() != 0)
{
- logError("fast events error: %s", FE_GetError());
- throw Exception(Exception::SDL_ERROR);
+ const char* error = FE_GetError();
+ throw Exception(ErrorCode::FASTEVENTS_INIT, error);
}
- if (Sound_Init() == 0)
+
+ mAlDevice = alcOpenDevice(0);
+ mAlContext = alcCreateContext(mAlDevice, 0);
+ if (!mAlDevice || !mAlContext)
+ {
+ const char* error = alcGetString(mAlDevice,alcGetError(mAlDevice));
+ logError("error while creating audio context: %s", error);
+ }
+ else
{
- logError("sound initialization failed: %s", Sound_GetError());
- throw Exception(Exception::SDL_ERROR);
+ alcMakeContextCurrent(mAlContext);
+ logDebug("opened sound device `%s'",
+ alcGetString(mAlDevice, ALC_DEFAULT_DEVICE_SPECIFIER));
}
- alutInit(&argc, argv);
- Settings& settings = Settings::getInstance();
- settings.parseArgs(argc, argv);
- settings.loadFromFile(configFile);
+ // now load the settings the engine needs
- long randomSeed;
- if (settings.get("engine.rngseed", randomSeed)) setSeed(randomSeed);
- else setSeed();
+ Settings& settings = Settings::getInstance();
- double ts = 0.01;
- settings.get("engine.timestep", ts);
- timestep = Scalar(ts);
+ unsigned randomSeed;
+ if (settings.get("rngseed", randomSeed)) srand(randomSeed);
+ else srand(time(0));
- long maxFps = 40;
- settings.getNumber("video.maxfps", maxFps);
- drawRate = 1.0 / Scalar(maxFps);
+ Scalar timestep = 80.0;
+ settings.get("timestep", timestep);
+ mTimestep = 1.0 / timestep;
- printFps = false;
- settings.get("video.printfps", printFps);
+ Scalar maxFps = 40.0;
+ settings.get("maxfps", maxFps);
+ mMaxFps = 1.0 / maxFps;
+ capFps();
- video = Video::alloc(name, iconFile);
- video->makeActive();
+ settings.get("printfps", mPrintFps);
}
~Impl()
{
// the video object must be destroyed before we can shutdown SDL
- video.reset();
+ mVideo.reset();
+
+ alcMakeContextCurrent(0);
+ alcDestroyContext(mAlContext);
+ alcCloseDevice(mAlDevice);
- alutExit();
- Sound_Quit();
FE_Quit();
SDL_Quit();
}
* the exit code used to stop the loop.
*/
- int run()
+ void run()
{
- Scalar ticksNow = getTicks();
+ Scalar ticksNow = Timer::getTicks();
Scalar nextStep = ticksNow;
Scalar nextDraw = ticksNow;
Scalar totalTime = 0.0;
Scalar deltaTime = 0.0;
- Scalar accumulator = timestep;
+ Scalar accumulator = mTimestep;
- fps = 0;
+ mFps = 0;
int frameAccum = 0;
- running = true;
do
{
- Scalar newTicks = getTicks();
+ Scalar newTicks = Timer::getTicks();
deltaTime = newTicks - ticksNow;
ticksNow = newTicks;
+ // don't slow the animation until 4Hz, which is unplayable anyway
if (deltaTime >= 0.25) deltaTime = 0.25;
accumulator += deltaTime;
- while (accumulator >= timestep)
+ Timer::fireIfExpired(ticksNow);
+ dispatchEvents();
+
+ while (accumulator >= mTimestep)
{
- dispatchEvents();
- interface->update(totalTime, timestep);
+ update(totalTime, mTimestep);
- totalTime += timestep;
- accumulator -= timestep;
+ totalTime += mTimestep;
+ accumulator -= mTimestep;
- nextStep += timestep;
+ nextStep += mTimestep;
}
if (ticksNow >= nextStep)
{
- nextStep = ticksNow + timestep;
+ nextStep = ticksNow + mTimestep;
}
if (ticksNow >= nextDraw)
if (ticksNow >= nextFpsUpdate) // determine the actual fps
{
- fps = frameAccum;
+ mFps = frameAccum;
frameAccum = 0;
nextFpsUpdate += 1.0;
nextFpsUpdate = ticksNow + 1.0;
}
- if (printFps)
+ if (mPrintFps)
{
- logInfo("framerate: %d fps", fps);
+ logInfo("%d fps", mFps);
}
}
- interface->draw(accumulator / timestep);
- video->swap();
+ draw(accumulator / mTimestep);
+ mVideo->swap();
- nextDraw += drawRate;
+ nextDraw += mMaxFps;
if (ticksNow >= nextDraw)
{
// we missed some scheduled draws, so reset the schedule
- nextDraw = ticksNow + drawRate;
+ nextDraw = ticksNow + mMaxFps;
}
}
// be a good citizen and give back what you don't need
- sleep(std::min(nextStep, nextDraw), true);
+ Timer::sleep(std::min(std::min(nextStep, nextDraw),
+ Timer::getNextFire()), Timer::ACTUAL);
}
- while (running);
+ while (!mStack.empty());
- return exitCode;
+ mDispatch.dispatch("engine.stopping");
}
-
void dispatchEvents()
{
SDL_Event event;
if (event.key.keysym.sym == SDLK_ESCAPE &&
(SDL_GetModState() & KMOD_CTRL) )
{
- exit(0);
+ // emergency escape
+ logWarning("escape forced");
+ exit(1);
}
break;
case SDL_VIDEORESIZE:
- video->resize(event.resize.w, event.resize.h);
+ mVideo->resize(event.resize.w, event.resize.h);
break;
}
- interface->handleEvent(event);
+ handleEvent(event);
+ }
+ }
+
+
+ void update(Scalar t, Scalar dt)
+ {
+ for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
+ {
+ (*mStackIt)->update(mInterface, t, dt);
+ }
+ }
+
+ void draw(Scalar alpha)
+ {
+ // FIXME - this will crash if the layer being drawn pops itself
+ std::list<LayerP>::reverse_iterator it;
+ for (it = mStack.rbegin(); it != mStack.rend(); ++it)
+ {
+ (*it)->draw(mInterface, alpha);
+ }
+ }
+
+ void handleEvent(const Event& event)
+ {
+ for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt)
+ {
+ if ((*mStackIt)->handleEvent(mInterface, event)) break;
+ }
+ }
+
+
+ void push(LayerP layer)
+ {
+ ASSERT(layer && "cannot push null layer");
+ mStack.push_front(layer);
+ logDebug("stack: %d [pushed %X]", mStack.size(), layer.get());
+ layer->pushed(mInterface);
+ }
+
+ LayerP pop()
+ {
+ bool fixIt = false;
+ if (mStack.begin() == mStackIt) fixIt = true;
+
+ LayerP layer = mStack.front();
+ mStack.pop_front();
+ logDebug("stack: %d [popped %X]", mStack.size(), layer.get());
+ layer->popped(mInterface);
+
+ if (fixIt) mStackIt = --mStack.begin();
+
+ return layer;
+ }
+
+ LayerP pop(Layer* layer)
+ {
+ bool fixIt = false;
+
+ std::list<LayerP> layers;
+
+ std::list<LayerP>::iterator it;
+ for (it = mStack.begin(); it != mStack.end(); ++it)
+ {
+ layers.push_back(*it);
+
+ if (it == mStackIt) fixIt = true;
+
+ if ((*it).get() == layer)
+ {
+ ++it;
+ mStack.erase(mStack.begin(), it);
+
+ for (it = layers.begin(); it != layers.end(); ++it)
+ {
+ (*it)->popped(mInterface);
+ logDebug("stack: %d [popped %X]", mStack.size(), (*it).get());
+ }
+
+ if (fixIt) mStackIt = --mStack.begin();
+
+ return layers.back();
+ }
+ }
+
+ return LayerP();
+ }
+
+ void clear()
+ {
+ mStack.clear();
+ mStackIt = mStack.begin();
+ logDebug("stack: 0 [cleared]");
+ }
+
+
+ void capFps()
+ {
+ if (mMaxFps < mTimestep)
+ {
+ logWarning("capping maximum fps to timestep (%f)", mTimestep);
+ mMaxFps = mTimestep;
}
}
- Engine* interface;
+ Engine& mInterface;
+ VideoP mVideo;
+ Dispatch mDispatch;
- VideoP video;
+ ALCdevice* mAlDevice;
+ ALCcontext* mAlContext;
- bool running;
- int exitCode;
+ std::list<LayerP> mStack;
+ std::list<LayerP>::iterator mStackIt;
- Scalar timestep;
- Scalar drawRate;
+ Scalar mTimestep;
+ Scalar mMaxFps;
- long fps;
- bool printFps;
+ int mFps;
+ bool mPrintFps;
};
-Engine::Engine(int argc, char* argv[], const std::string& configFile,
- const std::string& name, const std::string& iconFile) :
- impl_(new Engine::Impl(argc, argv, configFile, name, iconFile, this)) {}
+Engine::Engine() :
+ // pass through
+ mImpl(new Engine::Impl(*this)) {}
-Engine::~Engine() {}
+Engine& Engine::getInstance()
+{
+ static Engine engine;
+ return engine;
+}
-int Engine::run()
+void Engine::setVideo(VideoP video)
{
- return impl_->run();
+ // pass through
+ mImpl->mVideo = video;
}
-void Engine::stop(int exitCode)
+VideoP Engine::getVideo() const
{
- impl_->running = false;
- impl_->exitCode = exitCode;
+ return mImpl->mVideo;
}
-void Engine::setTimestep(Scalar ts)
+void Engine::setTimestep(int ts)
{
- impl_->timestep = ts;
+ mImpl->mTimestep = 1.0 / Scalar(ts);
+ mImpl->capFps();
}
-Scalar Engine::getTimestep()
+int Engine::getTimestep() const
{
- return impl_->timestep;
+ return int(1.0 / mImpl->mTimestep);
}
-void Engine::setMaxFrameRate(long maxFps)
+
+void Engine::setMaxFps(int maxFps)
{
- impl_->drawRate = 1.0 / Scalar(maxFps);
+ mImpl->mMaxFps = 1.0 / Scalar(maxFps);
+ mImpl->capFps();
}
-long Engine::getMaxFrameRate()
+int Engine::getMaxFps() const
{
- return long(1.0 / impl_->drawRate);
+ return int(1.0 / mImpl->mMaxFps);
}
-Video& Engine::getVideo()
+int Engine::getFps() const
{
- return *impl_->video;
+ return mImpl->mFps;
}
-long Engine::getFrameRate()
+
+void Engine::push(LayerP layer)
{
- return impl_->fps;
+ // pass through
+ mImpl->push(layer);
+}
+
+LayerP Engine::pop()
+{
+ // pass through
+ return mImpl->pop();
+}
+
+LayerP Engine::pop(Layer* layer)
+{
+ // pass through
+ return mImpl->pop(layer);
+}
+
+void Engine::clear()
+{
+ // pass through
+ mImpl->clear();
+}
+
+int Engine::getSize() const
+{
+ return mImpl->mStack.size();
}
-void Engine::update(Scalar t, Scalar dt) {}
-void Engine::draw(Scalar alpha) {}
-void Engine::handleEvent(const Event& event) {}
+void Engine::run()
+{
+ // pass through
+ return mImpl->run();
+}
+
+
+Dispatch::Handler Engine::addHandler(const std::string& event,
+ const Dispatch::Function& callback)
+{
+ return mImpl->mDispatch.addHandler(event, callback);
+}
+
+Dispatch::Handler Engine::addHandler(const std::string& event,
+ const Dispatch::Function& callback, Dispatch::Handler handler)
+{
+ return mImpl->mDispatch.addHandler(event, callback, handler);
+}
+
+void Engine::dispatch(const std::string& event,
+ const Dispatch::Message* message)
+{
+ mImpl->mDispatch.dispatch(event, message);
+}
} // namespace Mf