From 6f3b6a674f57f630eaff4da3908eeb02275bd463 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Wed, 5 Aug 2020 15:59:37 -0700 Subject: [PATCH] Add support for pausing, playing, looping, and speeding up animations --- src/animation/animation.cpp | 49 ++++++++++++- src/animation/animation.hpp | 141 ++++++++++++++++++++++++++++++++---- src/application.cpp | 46 ++++++------ 3 files changed, 198 insertions(+), 38 deletions(-) diff --git a/src/animation/animation.cpp b/src/animation/animation.cpp index 061cf12..dc65711 100644 --- a/src/animation/animation.cpp +++ b/src/animation/animation.cpp @@ -20,7 +20,12 @@ #include "animation.hpp" animation_base::animation_base(): - position(0.0f), + looped(false), + loop_count(0), + paused(false), + stopped(true), + position(0.0), + speed(1.0), start_callback(nullptr), end_callback(nullptr), loop_callback(nullptr) @@ -31,11 +36,49 @@ void animation_base::seek(double t) position = t; } -void animation_base::reset() +void animation_base::rewind() { seek(0.0); } +void animation_base::loop(bool enabled) +{ + looped = enabled; +} + +void animation_base::pause() +{ + paused = true; +} + +void animation_base::play() +{ + if (stopped) + { + stopped = false; + + if (start_callback) + { + start_callback(); + } + } + + paused = false; +} + +void animation_base::stop() +{ + rewind(); + stopped = true; + paused = false; + loop_count = 0; +} + +void animation_base::set_speed(double speed) +{ + this->speed = speed; +} + void animation_base::set_start_callback(std::function callback) { start_callback = callback; @@ -46,7 +89,7 @@ void animation_base::set_end_callback(std::function callback) end_callback = callback; } -void animation_base::set_loop_callback(std::function callback) +void animation_base::set_loop_callback(std::function callback) { loop_callback = callback; } diff --git a/src/animation/animation.hpp b/src/animation/animation.hpp index be522e3..e5f7347 100644 --- a/src/animation/animation.hpp +++ b/src/animation/animation.hpp @@ -47,27 +47,94 @@ public: */ void seek(double t); - void reset(); + /// Sets the animation position to `0.0`. + void rewind(); + + /// Enables or disables looping of the animation. + void loop(bool enabled); + + /// Pauses the animation. + void pause(); + + /// Plays the animation. + void play(); + + /// Stops the animation, rewinds it, and resets the loop count. + void stop(); + + /** + * Sets the speed of the animation. + * + * @param speed Speed multiplier. + */ + void set_speed(double speed); + + /// Returns `true` if looping of the animation is enabled, `false` otherwise. + bool is_looped() const; + + /// Returns `true` if the animation is paused, `false` otherwise. + bool is_paused() const; + + /// Returns `true` if the animation is stopped, `false` otherwise. + bool is_stopped() const; /// Returns the current position in time of the animation. double get_position() const; - /// Sets the callback that's executed when the animation is started. + /// Returns the current loop count of the animation. + int get_loop_count() const; + + /// Sets the callback that's executed when the animation is started from a stopped state. void set_start_callback(std::function callback); - /// Sets the callback that's executed when the animation ends. + /// Sets the callback that's executed when a non-looped animation has finished. void set_end_callback(std::function callback); - /// Sets the callback that's executed when the animation loops. - void set_loop_callback(std::function callback); + /** + * Sets the callback that's executed when the animation loops. + * + * @param callback Loop callback function which is passed the current loop count. + */ + void set_loop_callback(std::function callback); protected: + bool looped; + int loop_count; + bool paused; + bool stopped; double position; + double speed; + std::function start_callback; std::function end_callback; - std::function loop_callback; + std::function loop_callback; }; +inline bool animation_base::is_looped() const +{ + return looped; +} + +inline bool animation_base::is_paused() const +{ + return paused; +} + +inline bool animation_base::is_stopped() const +{ + return stopped; +} + +inline double animation_base::get_position() const +{ + return position; +} + +inline int animation_base::get_loop_count() const +{ + return loop_count; +} + /** * Templated keyframe animation class. */ @@ -162,16 +229,24 @@ animation::animation(): template void animation::advance(double dt) { - position += dt; + if (paused || stopped || keyframes.empty()) + { + return; + } + + // Advance position by dt + position += dt * speed; + + // Find the following keyframe + auto upper_bound = keyframes.upper_bound({position, T()}); + + // Find the preceding keyframe + auto lower_bound = upper_bound; + --lower_bound; - if (frame_callback != nullptr && interpolator != nullptr) + if (upper_bound != keyframes.end()) { - - auto upper_bound = keyframes.upper_bound({position, T()}); - auto lower_bound = upper_bound; - --lower_bound; - - if (lower_bound != keyframes.end() && upper_bound != keyframes.end()) + if (frame_callback != nullptr && interpolator != nullptr) { // Calculate interpolated frame double t0 = std::get<0>(*lower_bound); @@ -183,6 +258,44 @@ void animation::advance(double dt) frame_callback(frame); } } + else + { + if (looped) + { + ++loop_count; + + // Subtract duration of animation from position + position -= std::get<0>(*lower_bound); + + // Execute loop callback + if (loop_callback) + { + loop_callback(loop_count); + } + + // Call frame callback on looped frame + if (frame_callback) + { + advance(0.0); + } + } + else + { + // Call frame callback on final keyframe + if (frame_callback) + { + frame_callback(std::get<1>(*lower_bound)); + } + + stopped = true; + + // Call end callback + if (end_callback) + { + end_callback(); + } + } + } } template diff --git a/src/application.cpp b/src/application.cpp index 46507af..6ceac16 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -257,9 +257,9 @@ application::application(int argc, char** argv): int window_height = 1080; fullscreen = true; - //window_width = 1280; - //window_height = 720; - //fullscreen = false; + window_width = 1280; + window_height = 720; + fullscreen = false; viewport = {0.0f, 0.0f, static_cast(window_width), static_cast(window_height)}; @@ -480,18 +480,7 @@ application::application(int argc, char** argv): underground_color_texture_property->set_value(framebuffer_hdr_color); underworld_final_pass->get_material()->update_tweens(); - float radial_transition_time = 0.5f; - radial_transition_in = new animation(); - radial_transition_in->insert_keyframe({0.0f, 0.0f}); - radial_transition_in->insert_keyframe({radial_transition_time, 1.0f}); - radial_transition_in->set_frame_callback(std::bind(&material_property::set_val, underground_transition_property, std::placeholders::_1)); - radial_transition_in->set_interpolator(ease_in_quad); - - radial_transition_out = new animation(); - radial_transition_out->insert_keyframe({0.0f, 1.0f}); - radial_transition_out->insert_keyframe({radial_transition_time, 0.0f}); - radial_transition_out->set_frame_callback(std::bind(&material_property::set_val, underground_transition_property, std::placeholders::_1)); - radial_transition_out->set_interpolator(ease_out_quad); + // Setup underworld compositor underworld_compositor.add_pass(underworld_clear_pass); @@ -512,6 +501,23 @@ application::application(int argc, char** argv): // ... animator = new ::animator(); + float radial_transition_time = 0.5f; + radial_transition_in = new animation(); + radial_transition_in->insert_keyframe({0.0f, 0.0f}); + radial_transition_in->insert_keyframe({radial_transition_time, 1.0f}); + radial_transition_in->set_frame_callback(std::bind(&material_property::set_val, underground_transition_property, std::placeholders::_1)); + radial_transition_in->set_interpolator(ease_in_quad); + radial_transition_in->set_start_callback([this](){this->logger.log("animation started\n");}); + radial_transition_in->set_end_callback([this](){this->logger.log("animation ended\n");}); + animator->add_animation(radial_transition_in); + + radial_transition_out = new animation(); + radial_transition_out->insert_keyframe({0.0f, 1.0f}); + radial_transition_out->insert_keyframe({radial_transition_time, 0.0f}); + radial_transition_out->set_frame_callback(std::bind(&material_property::set_val, underground_transition_property, std::placeholders::_1)); + radial_transition_out->set_interpolator(ease_out_quad); + animator->add_animation(radial_transition_out); + // ECS terrain_system = new ::terrain_system(ecs_registry, resource_manager); terrain_system->set_patch_size(TERRAIN_PATCH_SIZE); @@ -671,9 +677,8 @@ application::application(int argc, char** argv): //this->overworld_camera.set_active(false); this->underworld_camera.set_active(true); this->active_scene = &this->underworld_scene; - this->animator->remove_animation(this->radial_transition_out); - this->animator->add_animation(this->radial_transition_in); - this->radial_transition_in->reset(); + this->radial_transition_out->stop(); + this->radial_transition_in->play(); } else { @@ -681,9 +686,8 @@ application::application(int argc, char** argv): //this->underworld_camera.set_active(false); this->overworld_camera.set_active(true); this->active_scene = &this->overworld_scene; - this->animator->remove_animation(this->radial_transition_in); - this->animator->add_animation(this->radial_transition_out); - this->radial_transition_out->reset(); + this->radial_transition_in->stop(); + this->radial_transition_out->play(); } });