X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2FMoof%2FView.cc;fp=src%2FMoof%2FView.cc;h=e2e84b52fa600850aaadf73452f10302458d32f6;hb=e0c0a3b5e7337cde55e520801d2e59e03dc97d9c;hp=0000000000000000000000000000000000000000;hpb=ed5fcf5f1357fc42749408f705e9ec55531ff006;p=chaz%2Fyoink diff --git a/src/Moof/View.cc b/src/Moof/View.cc new file mode 100644 index 0000000..e2e84b5 --- /dev/null +++ b/src/Moof/View.cc @@ -0,0 +1,411 @@ + +/*] Copyright (c) 2009-2010, Charles McGarvey [************************** +**] All rights reserved. +* +* vi:ts=4 sw=4 tw=75 +* +* Distributable under the terms and conditions of the 2-clause BSD license; +* see the file COPYING for a complete text of the license. +* +**************************************************************************/ + +#include +#include // exit, srand +#include // time +#include + +#include +#include "fastevents.h" + +#include "Error.hh" +#include "Event.hh" +#include "Log.hh" +#include "Math.hh" +#include "ModalDialog.hh" +#include "Settings.hh" +#include "Timer.hh" +#include "Video.hh" +#include "View.hh" + + +namespace Mf { + + +class RootView : public View +{ + void update(Scalar t, Scalar dt) + { + if (children().size() == 0) stop(); + } +}; + +static RootView gRootView; + + +class View::Impl +{ +public: + + Impl(View* view, Settings& settings, Video& video) : + mView(*view), + mSettings(&settings), + mVideo(&video), + mParent(&gRootView) + { + 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); + } + + Impl(View* view) : + mView(*view), + mSettings(0), + mVideo(0), + mParent(&gRootView) + { + init(); + } + + void init() + { + mTimestep = SCALAR(0.01); + mFramerate = SCALAR(0.02); + mShowFps = false; + } + + + /** + * 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() + { + ASSERT(mVideo && "running without video set"); + + Scalar totalTime = 0.0; + Scalar ticks = Timer::getTicks(); + + Scalar nextUpdate = ticks; + Scalar nextDraw = ticks; + Scalar nextSecond = ticks + SCALAR(1.0); + + mFps = 0; + int frameCount = 0; + + const Scalar timestep = mTimestep; + const Scalar framerate = mFramerate; + + const int MAX_FRAMESKIP = 15; + const Scalar inverseTimestep = SCALAR(1.0) / timestep; + + mIsRunning = true; + for (;;) + { + Timer::fireIfExpired(); // 1. fire timers + dispatchEvents(); // 2. dispatch events + + if (!mIsRunning) break; + + int i = 0; + while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP) + { + totalTime += timestep; // 3. update state + mView.update(totalTime, timestep); + + nextUpdate += timestep; + ++i; + + if (!mIsRunning) break; + } + + if (nextDraw < (ticks = Timer::getTicks())) + { + mView.draw( + (ticks + timestep - nextUpdate) * inverseTimestep); + mVideo->swap(); // 4. draw state + + nextDraw += framerate; + ++frameCount; + + if (nextSecond < Timer::getTicks()) + { + mFps = frameCount; + frameCount = 0; + + if (mShowFps) logInfo << mFps << " fps" << std::endl; + + nextSecond += SCALAR(1.0); + } + } + + if (!mIsRunning) break; + + ticks = Timer::getTicks(); // 5. yield timeslice + if (ticks < nextUpdate && ticks < nextDraw) Timer::sleep(0.0); + } + } + + void stop() + { + mIsRunning = false; + } + + + void dispatchEvents() + { + 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: + mVideo->resize(event.resize.w, event.resize.h); + break; + } + + mView.handleEvent(event); + } + } + + bool handleEvent(const Event& event) + { + std::list::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); ++it) + { + if ((*it)->handleEvent(event)) return true; + } + + return false; + } + + void update(Scalar t, Scalar dt) + { + std::list::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); ++it) + { + (*it)->update(t, dt); + } + } + + void draw(Scalar alpha) + { + std::list::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); ++it) + { + (*it)->draw(alpha); + } + } + + + void addChild(ViewP child) + { + ASSERT(child && "adding null view"); + ASSERT(child.get() != &mView && "adding view to itself"); + + child->mImpl->mParent->removeChild(child); + mChildren.push_back(child); + + child->mImpl->mParent = &mView; + child->mImpl->percolateObjects(); + + child->didAddToView(); + } + + void percolateObjects() + { + bool recurseAgain = false; + + if (mParent->mImpl->mVideo && mParent->mImpl->mVideo != mVideo) + { + mVideo = mParent->mImpl->mVideo; + recurseAgain = true; + } + + if (mParent->mImpl->mSettings && + mParent->mImpl->mSettings != mSettings) + { + mSettings = mParent->mImpl->mSettings; + recurseAgain = true; + } + + if (recurseAgain) + { + std::list::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); ++it) + { + (*it)->mImpl->percolateObjects(); + } + } + } + + ViewP removeChild(View* child) + { + ASSERT(child && "cannot remove null child"); + + std::list::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); ++it) + { + if ((*it).get() == child) + { + ViewP found = *it; + found->willRemoveFromView(); + mChildren.erase(it); + + found->mImpl->mParent = &gRootView; + + return found; + } + } + + return ViewP(); + } + + void clear() + { + mChildren.clear(); + } + + + bool mIsRunning; + View& mView; + + Settings* mSettings; + Video* mVideo; + + View* mParent; + std::list mChildren; + + Scalar mTimestep; + Scalar mFramerate; + + int mFps; + bool mShowFps; +}; + + +View::View(Settings& settings, Video& video) : + // pass through + mImpl(new View::Impl(this, settings, video)) {} + +View::View() : + mImpl(new View::Impl(this)) {} + + +void View::update(Scalar t, Scalar dt) +{ + // pass through + mImpl->update(t, dt); +} + +void View::draw(Scalar alpha) const +{ + // pass through + mImpl->draw(alpha); +} + +bool View::handleEvent(const Event& event) +{ + // pass through + return mImpl->handleEvent(event); +} + + +void View::addChild(ViewP view) +{ + // pass through + mImpl->addChild(view); +} + +ViewP View::removeChild(View* view) +{ + // pass through + return mImpl->removeChild(view); +} + +ViewP View::removeChild(ViewP view) +{ + // pass through + return mImpl->removeChild(view.get()); +} + +void View::clear() +{ + // pass through + mImpl->clear(); +} + + +View& View::parent() const +{ + return *(mImpl->mParent); +} + +const std::list& View::children() const +{ + return mImpl->mChildren; +} + + +Settings& View::settings() const +{ + ASSERT(mImpl->mSettings && "accessing null reference"); + // pass through + return *(mImpl->mSettings); +} + +Video& View::video() const +{ + ASSERT(mImpl->mVideo && "accessing null reference"); + // pass through + return *(mImpl->mVideo); +} + + +void View::run() +{ + // pass through + return mImpl->run(); +} + +void View::stop() +{ + // pass through + return mImpl->stop(); +} + +bool View::isRunning() const +{ + // pass through + return mImpl->mIsRunning; +} + + +} // namespace Mf +