]> git.tdb.fi Git - libs/gl.git/blobdiff - source/animation.cpp
Support slopes in keyframe interpolation
[libs/gl.git] / source / animation.cpp
index 8f7dea03bd90e8c8513c5e2ac77379699072ed16..f0e2630a502f6f43339ef7cbd1be8bcbe3aed7a3 100644 (file)
@@ -3,6 +3,7 @@
 #include <msp/datafile/collection.h>
 #include <msp/time/units.h>
 #include "animation.h"
+#include "animationeventobserver.h"
 #include "armature.h"
 #include "error.h"
 #include "pose.h"
@@ -42,27 +43,49 @@ 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);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float slope)
+{
+       add_keyframe(t, kf, slope, slope);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float ss, float es)
+{
+       RefPtr<const KeyFrame> kfr(&kf);
+       kfr.keep();
+       add_keyframe(t, kfr, ss, es);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf, float ss, float es)
 {
        if(!keyframes.empty() && t<keyframes.back().time)
                throw invalid_argument("Animation::add_keyframe");
 
-       TimedKeyFrame tkf(*this);
+       bool realloc = (keyframes.size()>=keyframes.capacity());
+
+       keyframes.push_back(TimedKeyFrame());
+       TimedKeyFrame &tkf = keyframes.back();
        tkf.time = t;
-       tkf.keyframe = &kf;
-       tkf.keyframe.keep();
-       prepare_keyframe(tkf);
-       keyframes.push_back(tkf);
-}
+       tkf.start_slope = ss;
+       tkf.end_slope = es;
+       tkf.keyframe = kf;
 
-void Animation::set_looping(bool l)
-{
-       looping = l;
+       if(realloc)
+       {
+               for(unsigned i=1; i<keyframes.size(); ++i)
+                       keyframes[i].prev = &keyframes[i-1];
+       }
+       else if(keyframes.size()>1)
+               tkf.prev = &tkf-1;
+
+       prepare_keyframe(tkf);
 }
 
 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
 {
-       tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
-
        const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
        for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
        {
@@ -79,7 +102,21 @@ void Animation::prepare_keyframe(TimedKeyFrame &tkf)
                        uniforms.push_back(UniformInfo(i->first, i->second.size));
        }
 
-       tkf.prepare();
+       tkf.prepare(*this);
+}
+
+void Animation::add_event(const Time::TimeDelta &t, const string &n, const Variant &v)
+{
+       Event event;
+       event.time = t;
+       event.name = n;
+       event.value = v;
+       events.push_back(event);
+}
+
+void Animation::set_looping(bool l)
+{
+       looping = l;
 }
 
 
@@ -91,18 +128,19 @@ Animation::AxisInterpolation::AxisInterpolation():
 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
 {
        // Compute a normalized vector halfway between the two endpoints
-       float half[3];
        float a1_len = 0;
        float h_len = 0;
+       float cos_half = 0;
        for(unsigned i=0; i<3; ++i)
        {
-               half[i] = (axis1[i]+axis2[i])/2;
+               float half_i = (axis1[i]+axis2[i])/2;
+               cos_half += axis1[i]*half_i;
                a1_len += axis1[i]*axis1[i];
-               h_len += half[i]*half[i];
+               h_len += half_i*half_i;
        }
 
        // Compute correction factors for smooth interpolation
-       float cos_half = (axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2])/sqrt(a1_len*h_len);
+       cos_half = min(max(cos_half/sqrt(a1_len*h_len), -1.0f), 1.0f);
        float angle = acos(cos_half);
        slope = (angle ? angle/tan(angle) : 1);
        scale = cos_half;
@@ -145,7 +183,7 @@ Matrix Animation::MatrixInterpolation::get(float t) const
                        will be in the range [-1, 1]. */
                        float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
 
-                       /* The interpolate vectors will also be shorter than unit length.  At
+                       /* The interpolated vectors will also be shorter than unit length.  At
                        the halfway point the length will be equal to the cosine of half the
                        angle, which was computed earlier.  Use a second degree polynomial to
                        approximate. */
@@ -170,12 +208,13 @@ Matrix Animation::MatrixInterpolation::get(float t) const
 }
 
 
-Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
-       animation(a),
-       prev(0)
+Animation::TimedKeyFrame::TimedKeyFrame():
+       prev(0),
+       start_slope(1),
+       end_slope(1)
 { }
 
-void Animation::TimedKeyFrame::prepare()
+void Animation::TimedKeyFrame::prepare(const Animation &animation)
 {
        const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
        for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
@@ -218,8 +257,10 @@ Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
 
 
 Animation::Iterator::Iterator(const Animation &a):
-       animation(a),
-       iter(animation.keyframes.begin()),
+       animation(&a),
+       iter(animation->keyframes.begin()),
+       event_iter(animation->events.begin()),
+       x(0),
        end(false)
 { }
 
@@ -228,12 +269,12 @@ Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
        time_since_keyframe += t;
        while(time_since_keyframe>iter->delta_t)
        {
-               KeyFrameList::const_iterator next = iter;
+               vector<TimedKeyFrame>::const_iterator next = iter;
                ++next;
-               if(next==animation.keyframes.end())
+               if(next==animation->keyframes.end())
                {
-                       if(animation.looping)
-                               next = animation.keyframes.begin();
+                       if(animation->looping)
+                               next = animation->keyframes.begin();
                        else
                        {
                                end = true;
@@ -246,15 +287,36 @@ Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
                iter = next;
        }
 
+       x = time_since_keyframe/iter->delta_t;
+       x += (iter->start_slope-1)*((x-2)*x+1)*x + (1-iter->end_slope)*(1-x)*x*x;
+
        return *this;
 }
 
+void Animation::Iterator::dispatch_events(AnimationEventObserver &observer)
+{
+       vector<Event>::const_iterator events_end = animation->events.end();
+       if(end)
+       {
+               for(; event_iter!=events_end; ++event_iter)
+                       observer.animation_event(0, event_iter->name, event_iter->value);
+       }
+       else if(event_iter!=events_end)
+       {
+               Time::TimeDelta t = time_since_keyframe;
+               if(iter->prev)
+                       t += iter->prev->time;
+               for(; (event_iter!=events_end && event_iter->time<=t); ++event_iter)
+                       observer.animation_event(0, event_iter->name, event_iter->value);
+       }
+}
+
 Matrix Animation::Iterator::get_matrix() const
 {
        if(!iter->prev)
                return iter->keyframe->get_matrix();
 
-       return iter->matrix.get(time_since_keyframe/iter->delta_t);
+       return iter->matrix.get(x);
 }
 
 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
@@ -264,22 +326,21 @@ KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
                if(iter->uniforms.size()>i)
                        return iter->uniforms[i];
                else
-                       return KeyFrame::AnimatedUniform(animation.uniforms[i].size, 0.0f);
+                       return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
        }
 
-       unsigned size = animation.uniforms[i].size;
-       float t = time_since_keyframe/iter->delta_t;
+       unsigned size = animation->uniforms[i].size;
        KeyFrame::AnimatedUniform result(size, 0.0f);
        for(unsigned j=0; j<size; ++j)
-               result.values[j] = iter->prev->uniforms[i].values[j]*(1-t)+iter->uniforms[i].values[j]*t;
+               result.values[j] = iter->prev->uniforms[i].values[j]*(1-x)+iter->uniforms[i].values[j]*x;
        return result;
 }
 
 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
 {
-       if(!animation.armature)
+       if(!animation->armature)
                throw invalid_operation("Animation::Iterator::get_pose_matrix");
-       if(link>animation.armature->get_max_link_index())
+       if(link>animation->armature->get_max_link_index())
                throw out_of_range("Animation::Iterator::get_pose_matrix");
 
        if(!iter->prev)
@@ -290,10 +351,10 @@ Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
                        return Matrix();
        }
 
-       // We must redo the base point correction since interpolation throws if off
+       // We must redo the base point correction since interpolation throws it off
        // XXX This should probably be done on local matrices
-       Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
-       const Vector3 &base = animation.armature->get_link(link).get_base();
+       Matrix result = iter->pose_matrices[link].get(x);
+       const Vector3 &base = animation->armature->get_link(link).get_base();
        Vector3 new_base = result*base;
        result = Matrix::translation(base-new_base)*result;
        return result;
@@ -314,16 +375,62 @@ Animation::Loader::Loader(Animation &a, Collection &c):
 
 void Animation::Loader::init()
 {
+       start_slope = 1;
+       end_slope = 1;
        add("armature", &Animation::armature);
+       add("event", &Loader::event);
+       add("event", &Loader::event1i);
+       add("event", &Loader::event1f);
+       add("event", &Loader::event2f);
+       add("event", &Loader::event3f);
+       add("event", &Loader::event4f);
        add("interval", &Loader::interval);
        add("keyframe", &Loader::keyframe);
        add("keyframe", &Loader::keyframe_inline);
        add("looping", &Animation::looping);
+       add("slopes", &Loader::slopes);
+}
+
+void Animation::Loader::event(const string &n)
+{
+       obj.add_event(current_time, n);
+}
+
+void Animation::Loader::event1i(const string &n, int v)
+{
+       obj.add_event(current_time, n, v);
+}
+
+void Animation::Loader::event1f(const string &n, float v)
+{
+       obj.add_event(current_time, n, v);
+}
+
+void Animation::Loader::event2f(const string &n, float v0, float v1)
+{
+       obj.add_event(current_time, n, LinAl::Vector<float, 2>(v0, v1));
+}
+
+void Animation::Loader::event3f(const string &n, float v0, float v1, float v2)
+{
+       obj.add_event(current_time, n, Vector3(v0, v1, v2));
+}
+
+void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3)
+{
+       obj.add_event(current_time, n, Vector4(v0, v1, v2, v3));
+}
+
+void Animation::Loader::interval(float t)
+{
+       current_time += t*Time::sec;
 }
 
 void Animation::Loader::keyframe(const string &n)
 {
-       obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
+       obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n), start_slope, end_slope);
+       start_slope = end_slope;
+       end_slope = 1;
 }
 
 void Animation::Loader::keyframe_inline()
@@ -334,16 +441,15 @@ void Animation::Loader::keyframe_inline()
        else
                load_sub(*kf);
 
-       TimedKeyFrame tkf(obj);
-       tkf.time = current_time;
-       tkf.keyframe = kf;
-       obj.prepare_keyframe(tkf);
-       obj.keyframes.push_back(tkf);
+       obj.add_keyframe(current_time, kf, start_slope, end_slope);
+       start_slope = end_slope;
+       end_slope = 1;
 }
 
-void Animation::Loader::interval(float t)
+void Animation::Loader::slopes(float s, float e)
 {
-       current_time += t*Time::sec;
+       start_slope = s;
+       end_slope = e;
 }
 
 } // namespace GL