From 49f1812b3e5ad73748015df52d0e4dee17246036 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 8 Jun 2019 02:17:43 +0300 Subject: [PATCH] Use bezier splines in Animation Control keyframes can now be added to affect the shape of curves. Zero, one or two control keyframes can be present between any two regular keyframes, resulting in a linear, quadratic or cubic segment. --- source/animation.cpp | 105 ++++++++++++++++++++++++++++++++++--------- source/animation.h | 13 ++++-- 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/source/animation.cpp b/source/animation.cpp index 4be00d73..c58a33d8 100644 --- a/source/animation.cpp +++ b/source/animation.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "animation.h" #include "animationeventobserver.h" #include "armature.h" @@ -46,7 +46,10 @@ const string &Animation::get_uniform_name(unsigned i) const void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf) { - add_keyframe(t, kf, 1.0f, 1.0f); + RefPtr kfr(&kf); + kfr.keep(); + add_keyframe(t, kfr, false); + create_curves(); } void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float slope) @@ -54,16 +57,25 @@ void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float add_keyframe(t, kf, slope, slope); } -void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float ss, float es) +void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float, float) +{ + add_keyframe(t, kf); +} + +void Animation::add_control_keyframe(const KeyFrame &kf) { + if(keyframes.empty()) + throw invalid_operation("Animation::add_control_keyframe"); + RefPtr kfr(&kf); kfr.keep(); - add_keyframe(t, kfr, ss, es); - create_curves(); + add_keyframe(keyframes.back().time, kfr, true); } -void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr &kf, float ss, float es) +void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr &kf, bool c) { + if(c && keyframes.empty()) + throw invalid_argument("Animation::add_keyframe"); if(keyframes.empty() && t!=Time::zero) throw invalid_argument("Animation::add_keyframe"); if(!keyframes.empty() && t::Knot Knot; vector knots; + unsigned n_control = 0; for(vector::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i) { + if(i->control && knots.empty()) + continue; + typename Interpolate::SplineValue::Type value; if(extract(*i->keyframe, value)) - knots.push_back(Knot(i->time/Time::sec, value)); + { + float x = i->time/Time::sec; + if(i->control) + { + ++n_control; + if(n_control>2) + throw logic_error("too many control keyframes"); + } + else + { + if(n_control==1) + { + typename Knot::Value cv = knots.back().y; + knots.back().y = (knots[knots.size()-2].y+cv*2.0f)/3.0f; + knots.push_back(Knot(x, (value+cv*2.0f)/3.0f)); + } + else if(n_control==0 && !knots.empty()) + { + typename Knot::Value prev = knots.back().y; + knots.push_back(Knot(knots.back().x, (prev*2.0f+value)/3.0f)); + knots.push_back(Knot(x, (prev+value*2.0f)/3.0f)); + } + n_control = 0; + } + knots.push_back(Knot(x, value)); + } } + + while(n_control--) + knots.pop_back(); curves.push_back(new ValueCurve(target, knots)); } @@ -191,7 +234,7 @@ Animation::Curve::Curve(CurveTarget t): template Animation::ValueCurve::ValueCurve(CurveTarget t, const vector &k): Curve(t), - spline(Interpolate::LinearSpline(k)) + spline(Interpolate::BezierSpline(k)) { } template @@ -327,6 +370,8 @@ void Animation::Loader::init() start_slope = 1; end_slope = 1; add("armature", &Animation::armature); + add("control_keyframe", &Loader::control_keyframe); + add("control_keyframe", &Loader::control_keyframe_inline); add("event", &Loader::event); add("event", &Loader::event1i); add("event", &Loader::event1f); @@ -345,6 +390,32 @@ void Animation::Loader::finish() obj.create_curves(); } +void Animation::Loader::load_kf(const string &n, bool c) +{ + obj.add_keyframe(current_time, get_collection().get(n), c); +} + +void Animation::Loader::load_kf_inline(bool c) +{ + RefPtr kf = new KeyFrame; + if(coll) + load_sub(*kf, get_collection()); + else + load_sub(*kf); + + obj.add_keyframe(current_time, kf, c); +} + +void Animation::Loader::control_keyframe(const string &n) +{ + load_kf(n, true); +} + +void Animation::Loader::control_keyframe_inline() +{ + load_kf_inline(true); +} + void Animation::Loader::event(const string &n) { obj.add_event(current_time, n); @@ -382,22 +453,12 @@ void Animation::Loader::interval(float t) void Animation::Loader::keyframe(const string &n) { - obj.add_keyframe(current_time, get_collection().get(n), start_slope, end_slope); - start_slope = end_slope; - end_slope = 1; + load_kf(n, false); } void Animation::Loader::keyframe_inline() { - RefPtr kf = new KeyFrame; - if(coll) - load_sub(*kf, get_collection()); - else - load_sub(*kf); - - obj.add_keyframe(current_time, kf, start_slope, end_slope); - start_slope = end_slope; - end_slope = 1; + load_kf_inline(false); } void Animation::Loader::slopes(float s, float e) diff --git a/source/animation.h b/source/animation.h index 92c177b6..43cfc289 100644 --- a/source/animation.h +++ b/source/animation.h @@ -36,6 +36,11 @@ public: void init(); virtual void finish(); + void load_kf(const std::string &, bool); + void load_kf_inline(bool); + + void control_keyframe(const std::string &); + void control_keyframe_inline(); void event(const std::string &); void event1i(const std::string &, int); void event1f(const std::string &, float); @@ -77,7 +82,7 @@ private: typedef typename Interpolate::SplineKnot Knot; private: - Interpolate::Spline spline; + Interpolate::Spline spline; public: ValueCurve(CurveTarget, const std::vector &); @@ -99,9 +104,8 @@ private: struct TimedKeyFrame { Time::TimeDelta time; - float start_slope; - float end_slope; RefPtr keyframe; + bool control; }; struct Event @@ -162,8 +166,9 @@ public: void add_keyframe(const Time::TimeDelta &, const KeyFrame &); void add_keyframe(const Time::TimeDelta &, const KeyFrame &, float); void add_keyframe(const Time::TimeDelta &, const KeyFrame &, float, float); + void add_control_keyframe(const KeyFrame &); private: - void add_keyframe(const Time::TimeDelta &, const RefPtr &, float, float); + void add_keyframe(const Time::TimeDelta &, const RefPtr &, bool); void prepare_keyframe(TimedKeyFrame &); void create_curves(); template -- 2.45.2