X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FCore.cc;fp=src%2FMoof%2FCore.cc;h=cc86a9118afcbd1d51f694ec478bd6c4798c3a58;hp=0000000000000000000000000000000000000000;hb=987971a961454d97082c6448fdc0bbeb540281bb;hpb=be9ebc1104574e5e81e19c5caba0c23b54df826d diff --git a/src/Moof/Core.cc b/src/Moof/Core.cc new file mode 100644 index 0000000..cc86a91 --- /dev/null +++ b/src/Moof/Core.cc @@ -0,0 +1,485 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#include +#include // exit, srand +#include // time +#include +#include + +#include +#include +#include "fastevents.h" + +#include "Core.hh" +#include "Event.hh" +#include "Log.hh" +#include "Math.hh" +#include "ModalDialog.hh" +#include "Settings.hh" +#include "Timer.hh" +#include "Video.hh" + + +namespace Mf { + + +class Core::Impl +{ +public: + + Impl() : + mError(Error::NONE), + mTimestep(0.01), + mFramerate(0.02), + mShowFps(false) {} + + void init() + { + unsigned randomSeed; + if (settings.get("rngseed", randomSeed)) srand(randomSeed); + else srand(time(0)); + + Scalar timestep = 80.0; + settings.get("timestep", timestep); + mTimestep = 1.0 / timestep; + + Scalar framerate = 40.0; + settings.get("framerate", framerate); + mFramerate = 1.0 / framerate; + + mShowFps = false; + settings.get("showfps", mShowFps); + } + + + /** + * The main loop. This just calls dispatchEvents(), update(), and draw() + * over and over again. The timing of the update and draw are decoupled. + * The actual frame rate is also calculated here. This function will return + * the exit code used to stop the loop. + */ + + void run() + { + init(); + + Scalar totalTime = 0.0; + Scalar ticks = Timer::getTicks(); + + Scalar nextUpdate = ticks; + Scalar nextDraw = ticks; + Scalar nextSecond = ticks + SCALAR(1.0); + + mFps = 0; + int frames = 0; + + const int MAX_FRAMESKIP = 15; + const Scalar inverseTimestep = SCALAR(1.0) / mTimestep; + + ASSERT(video && "cannot run core without a current video context"); + + do + { + Timer::fireIfExpired(); + dispatchEvents(); + + int i = 0; + while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP) + { + totalTime += mTimestep; + update(totalTime, mTimestep); + + nextUpdate += mTimestep; + ++i; + } + + if (nextDraw < (ticks = Timer::getTicks())) + { + ++frames; + draw((ticks + mTimestep - nextUpdate) * inverseTimestep); + video->swap(); + + nextDraw += mFramerate; + + if (mShowFps && nextSecond < ticks) + { + mFps = frames; + frames = 0; + + logInfo << mFps << " fps" << std::endl; + + nextSecond += SCALAR(1.0); + } + } + + // be a good citizen and give back what you don't need + Timer::sleep(0.0); + } + while (!mStack.empty()); + + mDispatch.dispatch("engine.stopping"); + } + + + void dispatchEvents() + { + SDL_Event event; + + while (FE_PollEvent(&event) == 1) + { + switch (event.type) + { + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE && + (SDL_GetModState() & KMOD_CTRL) ) + { + // emergency escape + logWarning("escape forced"); + exit(1); + } + break; + + case SDL_VIDEORESIZE: + video->resize(event.resize.w, event.resize.h); + break; + } + + handleEvent(event); + } + } + + + void update(Scalar t, Scalar dt) + { + for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt) + { + (*mStackIt)->update(t, dt); + } + } + + void draw(Scalar alpha) + { + // FIXME - this will crash if the layer being drawn pops itself + std::list::reverse_iterator it; + for (it = mStack.rbegin(); it != mStack.rend(); ++it) + { + (*it)->draw(alpha); + } + } + + void handleEvent(const Event& event) + { + for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt) + { + if ((*mStackIt)->handleEvent(event)) break; + } + } + + + void push(LayerP layer) + { + ASSERT(layer && "cannot push null layer"); + mStack.push_front(layer); + logInfo << "stack: " << mStack.size() + << " [pushed " << layer.get() << "]" << std::endl; + layer->addedToCore(); + } + + LayerP pop() + { + bool fixIt = false; + if (mStack.begin() == mStackIt) fixIt = true; + + LayerP layer = mStack.front(); + mStack.pop_front(); + logInfo << "stack: " << mStack.size() + << " [popped " << layer.get() << "]" << std::endl; + layer->removedFromCore(); + + if (fixIt) mStackIt = --mStack.begin(); + + return layer; + } + + LayerP pop(Layer* layer) + { + bool fixIt = false; + + std::list layers; + + std::list::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)->removedFromCore(); + logInfo << "stack: " << mStack.size() + << " [popped " << (*it).get() << "]" << std::endl; + } + + if (fixIt) mStackIt = --mStack.begin(); + + return layers.back(); + } + } + + return LayerP(); + } + + void clear() + { + mStack.clear(); + mStackIt = mStack.begin(); + logInfo("stack: 0 [cleared]"); + } + + + Error mError; + + Dispatch mDispatch; + + std::list mStack; + std::list::iterator mStackIt; + + Scalar mTimestep; + Scalar mFramerate; + + int mFps; + bool mShowFps; +}; + + +Core::Core() : + // pass through + mImpl(new Core::Impl) {} + + +void Core::init() +{ + // pass through + mImpl->init(); +} + + +int Core::getFps() const +{ + return mImpl->mFps; +} + + +void Core::push(LayerP layer) +{ + // pass through + mImpl->push(layer); +} + +LayerP Core::pop() +{ + // pass through + return mImpl->pop(); +} + +LayerP Core::pop(Layer* layer) +{ + // pass through + return mImpl->pop(layer); +} + +void Core::clear() +{ + // pass through + mImpl->clear(); +} + +int Core::getSize() const +{ + return mImpl->mStack.size(); +} + + +void Core::run() +{ + // pass through + return mImpl->run(); +} + + +Dispatch::Handler Core::addHandler(const std::string& event, + const Dispatch::Function& callback) +{ + return mImpl->mDispatch.addHandler(event, callback); +} + +Dispatch::Handler Core::addHandler(const std::string& event, + const Dispatch::Function& callback, Dispatch::Handler handler) +{ + return mImpl->mDispatch.addHandler(event, callback, handler); +} + +void Core::dispatch(const std::string& event, + const Dispatch::Message* message) +{ + mImpl->mDispatch.dispatch(event, message); +} + + +Core core; + + +class Backend_; +typedef boost::shared_ptr BackendP; + +class Backend_ +{ +public: + + void init() + { +#if defined(_WIN32) || defined(__WIN32__) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) +#else + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0) +#endif + { + const char* error = SDL_GetError(); + gError.init(Error::SDL_INIT, error); + return; // fatal + } + else + { + char name[128]; + SDL_VideoDriverName(name, sizeof(name)); + logInfo << "initialized SDL; using video driver `" + << name << "'" << std::endl; + } + + if (FE_Init() != 0) + { + const char* error = FE_GetError(); + gError.init(Error::FASTEVENTS_INIT, error); + return; // fatal + } + + mAlDevice = alcOpenDevice(0); + mAlContext = alcCreateContext(mAlDevice, 0); + if (!mAlDevice || !mAlContext) + { + const char* error = alcGetString(mAlDevice,alcGetError(mAlDevice)); + gError.init(Error::OPENAL_INIT, error); + return; + } + else + { + alcMakeContextCurrent(mAlContext); + logInfo << "opened sound device `" + << alcGetString(mAlDevice, ALC_DEFAULT_DEVICE_SPECIFIER) + << "'" << std::endl; + } + + gError.init(Error::NONE); + } + + ~Backend_() + { + alcMakeContextCurrent(0); + alcDestroyContext(mAlContext); + alcCloseDevice(mAlDevice); + + FE_Quit(); + SDL_Quit(); + } + + static void retain() + { + if (gRetainCount++ == 0) + { + gInstance = BackendP(new Backend_); + gInstance->init(); + } + } + + static void release() + { + if (--gRetainCount == 0) + { + gInstance.reset(); + gError.reset(); + } + } + + static bool check(Error& error) + { + error = gError; + return error.code() == Error::NONE; + } + +private: + + ALCdevice* mAlDevice; + ALCcontext* mAlContext; + + static Error gError; + static int gRetainCount; + static BackendP gInstance; +}; + +Error Backend_::gError(Error::UNINITIALIZED); +int Backend_::gRetainCount = 0; +BackendP Backend_::gInstance; + + +Backend::Backend() +{ + Backend_::retain(); +} + +Backend::~Backend() +{ + Backend_::release(); +} + +bool Backend::check(Error& error) +{ + return Backend_::check(error); +} + + +} // namespace Mf + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ +