/*] 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 "event.hh" #include "log.hh" #include "math.hh" #include "modal_dialog.hh" #include "settings.hh" #include "timer.hh" #include "video.hh" #include "view.hh" namespace moof { class root_view : public view { void update(scalar t, scalar dt) { if (children().size() == 0) stop(); } }; static root_view gRootView; class view::impl { public: impl(view* view, moof::settings& settings, moof::video& video) : view_(*view), settings_(&settings), video_(&video), parent_(&gRootView) { init(); unsigned randomSeed; if (settings.get("rngseed", randomSeed)) srand(randomSeed); else srand(time(0)); scalar timestep = 80.0; settings.get("timestep", timestep); timestep_ = 1.0 / timestep; scalar framerate = 40.0; settings.get("framerate", framerate); framerate_ = 1.0 / framerate; show_fps_ = false; settings.get("showfps", show_fps_); } impl(view* view) : view_(*view), settings_(0), video_(0), parent_(&gRootView) { init(); } void init() { timestep_ = SCALAR(0.01); framerate_ = SCALAR(0.02); show_fps_ = false; } /** * The main loop. This just calls dispatch_events(), 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(video_ && "running without video set"); scalar totalTime = 0.0; scalar ticks = timer::ticks(); scalar nextUpdate = ticks; scalar nextDraw = ticks; scalar nextSecond = ticks + SCALAR(1.0); fps_ = 0; int frameCount = 0; const scalar timestep = timestep_; const scalar framerate = framerate_; const int MAX_FRAMESKIP = 15; const scalar inverseTimestep = SCALAR(1.0) / timestep; is_running_ = true; for (;;) { timer::fire_expired_timers(); // 1. fire timers dispatch_events(); // 2. dispatch events if (!is_running_) break; int i = 0; while (nextUpdate < timer::ticks() && i < MAX_FRAMESKIP) { totalTime += timestep; // 3. update state view_.update(totalTime, timestep); nextUpdate += timestep; ++i; if (!is_running_) break; } if (nextDraw < (ticks = timer::ticks())) { view_.draw( (ticks + timestep - nextUpdate) * inverseTimestep); video_->swap(); // 4. draw state nextDraw += framerate; ++frameCount; if (nextSecond < timer::ticks()) { fps_ = frameCount; frameCount = 0; if (show_fps_) log_info << fps_ << " fps" << std::endl; nextSecond += SCALAR(1.0); } } if (!is_running_) break; ticks = timer::ticks(); // 5. yield timeslice if (ticks < nextUpdate && ticks < nextDraw) timer::sleep(0.0); } } void stop() { is_running_ = false; } void dispatch_events() { 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 log_warning("escape forced"); exit(1); } break; case SDL_VIDEORESIZE: video_->resize(event.resize.w, event.resize.h); break; } view_.handle_event(event); } } bool handle_event(const event& event) { std::list::iterator it; for (it = children_.begin(); it != children_.end(); ++it) { if ((*it)->handle_event(event)) return true; } return false; } void update(scalar t, scalar dt) { std::list::iterator it; for (it = children_.begin(); it != children_.end(); ++it) { (*it)->update(t, dt); } } void draw(scalar alpha) { std::list::iterator it; for (it = children_.begin(); it != children_.end(); ++it) { (*it)->draw(alpha); } } void add_child(view_ptr child) { ASSERT(child && "adding null view"); ASSERT(child.get() != &view_ && "adding view to itself"); child->impl_->parent_->remove_child(child); children_.push_back(child); child->impl_->parent_ = &view_; child->impl_->percolate_objects(); child->did_add_to_view(); } void percolate_objects() { bool recurseAgain = false; if (parent_->impl_->video_ && parent_->impl_->video_ != video_) { video_ = parent_->impl_->video_; recurseAgain = true; } if (parent_->impl_->settings_ && parent_->impl_->settings_ != settings_) { settings_ = parent_->impl_->settings_; recurseAgain = true; } if (recurseAgain) { std::list::iterator it; for (it = children_.begin(); it != children_.end(); ++it) { (*it)->impl_->percolate_objects(); } } } view_ptr remove_child(view* child) { ASSERT(child && "cannot remove null child"); std::list::iterator it; for (it = children_.begin(); it != children_.end(); ++it) { if ((*it).get() == child) { view_ptr found = *it; found->will_remove_from_view(); children_.erase(it); found->impl_->parent_ = &gRootView; return found; } } return view_ptr(); } void clear() { children_.clear(); } bool is_running_; view& view_; moof::settings* settings_; moof::video* video_; view* parent_; std::list children_; scalar timestep_; scalar framerate_; int fps_; bool show_fps_; }; view::view(moof::settings& settings, moof::video& video) : // pass through impl_(new view::impl(this, settings, video)) {} view::view() : impl_(new view::impl(this)) {} void view::update(scalar t, scalar dt) { // pass through impl_->update(t, dt); } void view::draw(scalar alpha) const { // pass through impl_->draw(alpha); } bool view::handle_event(const event& event) { // pass through return impl_->handle_event(event); } void view::add_child(view_ptr view) { // pass through impl_->add_child(view); } view_ptr view::remove_child(view* view) { // pass through return impl_->remove_child(view); } view_ptr view::remove_child(view_ptr view) { // pass through return impl_->remove_child(view.get()); } void view::clear() { // pass through impl_->clear(); } view& view::parent() const { return *(impl_->parent_); } const std::list& view::children() const { return impl_->children_; } moof::settings& view::settings() const { ASSERT(impl_->settings_ && "accessing null reference"); // pass through return *(impl_->settings_); } video& view::video() const { ASSERT(impl_->video_ && "accessing null reference"); // pass through return *(impl_->video_); } void view::run() { // pass through return impl_->run(); } void view::stop() { // pass through return impl_->stop(); } bool view::is_running() const { // pass through return impl_->is_running_; } } // namespace moof