/*] 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 { // Timestep Example Source Code // Copyright (c) Glenn Fiedler 2004 // http://www.gaffer.org/articles struct State { float x; float v; }; struct Derivative { float dx; float dv; }; State interpolate(const State &previous, const State ¤t, float alpha) { State state; state.x = current.x*alpha + previous.x*(1-alpha); state.v = current.v*alpha + previous.v*(1-alpha); return state; } float acceleration(const State &state, float t) { const float k = 10; const float b = 1; return - k*state.x - b*state.v; } Derivative evaluate(const State &initial, float t) { Derivative output; output.dx = initial.v; output.dv = acceleration(initial, t); return output; } Derivative evaluate(const State &initial, float t, float dt, const Derivative &d) { State state; state.x = initial.x + d.dx*dt; state.v = initial.v + d.dv*dt; Derivative output; output.dx = state.v; output.dv = acceleration(state, t+dt); return output; } void integrate(State &state, float t, float dt) { Derivative a = evaluate(state, t); Derivative b = evaluate(state, t, dt*0.5f, a); Derivative c = evaluate(state, t, dt*0.5f, b); Derivative d = evaluate(state, t, dt, c); const float dxdt = 1.0f/6.0f * (a.dx + 2.0f*(b.dx + c.dx) + d.dx); const float dvdt = 1.0f/6.0f * (a.dv + 2.0f*(b.dv + c.dv) + d.dv); state.x = state.x + dxdt*dt; state.v = state.v + dvdt*dt; } 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. */ scalar nextUpdate; scalar totalTime; void U(timer& T, scalar t) { const int MAX_FRAMESKIP = 15; log_info("update"); int i = 0; while (nextUpdate < t && ++i < MAX_FRAMESKIP) { totalTime += timestep_; // 3. update state view_.update(totalTime, timestep_); //previous = current; //integrate(current, totalTime, timestep); nextUpdate += timestep_; } } void D(timer& T, scalar t) { const scalar inverseTimestep = SCALAR(1.0) / timestep_; log_info("draw"); scalar alpha = (t + timestep_ - nextUpdate) * inverseTimestep; //scalar alpha = (nextUpdate - t) * inverseTimestep; if (alpha < SCALAR(0.0)) log_error("UH OH!!!!! It's NEGATIVE", alpha); if (alpha > SCALAR(1.0)) log_error("UH OH!!!!! It's POSITIVE", alpha); log_info("alpha:", alpha); view_.draw(alpha); video_->swap(); // 4. draw state } timer utimer, dtimer; void run() { ASSERT(video_ && "running without video set"); utimer.init(boost::bind(&impl::U, this, _1, _2), timestep_, timer::repeat); dtimer.init(boost::bind(&impl::D, this, _1, _2), framerate_, timer::repeat); totalTime = 0.0f; nextUpdate = timer::ticks(); 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 = SCALAR(0.01);//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); ////previous = current; ////integrate(current, totalTime, timestep); //nextUpdate += timestep; //++i; //if (!is_running_) break; //} ////const float newTime = timer::ticks(); ////float deltaTime = newTime - currentTime; ////currentTime = newTime; ////if (deltaTime>0.25f) ////deltaTime = 0.25f; ////accumulator += deltaTime; ////while (accumulator>=dt) ////{ ////accumulator -= dt; ////previous = current; ////integrate(current, t, dt); ////t += dt; ////} ////if (nextDraw < (ticks = timer::ticks())) ////{ //ticks = timer::ticks(); //scalar diff = ticks - nextDraw; ////log_info("difference:", diff); //scalar alpha = (ticks + timestep - nextUpdate) * inverseTimestep; ////scalar alpha = (nextUpdate - ticks) * inverseTimestep; ////float alpha = accumulator/dt; //if (alpha < SCALAR(0.0)) log_error("UH OH!!!!! It's NEGATIVE", alpha); //if (alpha > SCALAR(1.0)) log_error("UH OH!!!!! It's POSITIVE", alpha); //log_info("alpha:", alpha); //view_.draw(alpha); //// TEMP ////State state = interpolate(previous, current, alpha); ////glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ////glBegin(GL_POINTS); ////glColor3f(1,1,1); ////glVertex3f(state.x, 0, 0); ////glEnd(); //video_->swap(); // 4. draw state //nextDraw += framerate; //++frameCount; //if (nextSecond < ticks)//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 //scalar next = std::min(nextUpdate, nextDraw); //next = std::min(next, timer::next_event()); //if (ticks < next) timer::sleep(next, timer::absolute); timer::sleep(timer::next_event(), timer::absolute); // Animation is choppy... the sound timer makes the draw occur // late. It's not usually enough to make the FPS drop, but it // certainly is noticeably choppy. Maybe update and draw // should both be scheduled like timers. That should reduce // the number of times either update or draw occur late. It // doesn't really matter if update is late, but it's ugly if // draw is late. } } 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