Browse Source

Add support for pausing, playing, looping, and speeding up animations

master
C. J. Howard 4 years ago
parent
commit
6f3b6a674f
3 changed files with 198 additions and 38 deletions
  1. +46
    -3
      src/animation/animation.cpp
  2. +127
    -14
      src/animation/animation.hpp
  3. +25
    -21
      src/application.cpp

+ 46
- 3
src/animation/animation.cpp View File

@ -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<void()> 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<void()> callback)
void animation_base::set_loop_callback(std::function<void(int)> callback)
{
loop_callback = callback;
}

+ 127
- 14
src/animation/animation.hpp View File

@ -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<void()> 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<void()> callback);
/// Sets the callback that's executed when the animation loops.
void set_loop_callback(std::function<void()> 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<void(int)> callback);
protected:
bool looped;
int loop_count;
bool paused;
bool stopped;
double position;
double speed;
std::function<void()> start_callback;
std::function<void()> end_callback;
std::function<void()> loop_callback;
std::function<void(int)> 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 <typename T>
void animation<T>::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 <typename T>

+ 25
- 21
src/application.cpp View File

@ -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<float>(window_width), static_cast<float>(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<float>();
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<float>::set_val, underground_transition_property, std::placeholders::_1));
radial_transition_in->set_interpolator(ease_in_quad<float>);
radial_transition_out = new animation<float>();
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<float>::set_val, underground_transition_property, std::placeholders::_1));
radial_transition_out->set_interpolator(ease_out_quad<float>);
// 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<float>();
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<float>::set_val, underground_transition_property, std::placeholders::_1));
radial_transition_in->set_interpolator(ease_in_quad<float>);
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<float>();
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<float>::set_val, underground_transition_property, std::placeholders::_1));
radial_transition_out->set_interpolator(ease_out_quad<float>);
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();
}
});

Loading…
Cancel
Save