]> git.tdb.fi Git - libs/gl.git/blobdiff - source/animation.cpp
Refactor the Animation reference out of TimedKeyFrame
[libs/gl.git] / source / animation.cpp
index efb67c972ded72ad916c286def76bd34bf5de28c..2f78d1eafad574b4d1c060a8209d0527309cc393 100644 (file)
@@ -1,9 +1,10 @@
 #include <cmath>
+#include <msp/core/maputils.h>
 #include <msp/datafile/collection.h>
 #include <msp/time/units.h>
 #include "animation.h"
 #include "armature.h"
-#include "keyframe.h"
+#include "error.h"
 #include "pose.h"
 
 using namespace std;
@@ -16,22 +17,58 @@ Animation::Animation():
        looping(false)
 { }
 
+// Avoid synthesizing ~RefPtr in files including animation.h
+Animation::~Animation()
+{ }
+
 void Animation::set_armature(const Armature &a)
 {
        armature = &a;
 }
 
+unsigned Animation::get_slot_for_uniform(const string &n) const
+{
+       for(unsigned i=0; i<uniforms.size(); ++i)
+               if(uniforms[i].name==n)
+                       return i;
+       throw key_error(n);
+}
+
+const string &Animation::get_uniform_name(unsigned i) const
+{
+       if(i>=uniforms.size())
+               throw out_of_range("Animation::get_uniform_name");
+       return uniforms[i].name;
+}
+
 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
+{
+       RefPtr<const KeyFrame> kfr(&kf);
+       kfr.keep();
+       add_keyframe(t, kfr);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf)
 {
        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();
+       tkf.keyframe = kf;
+
+       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);
-       keyframes.push_back(tkf);
 }
 
 void Animation::set_looping(bool l)
@@ -41,11 +78,23 @@ void Animation::set_looping(bool l)
 
 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
 {
-       tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
-       if(!tkf.prev)
-               return;
+       const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
+       for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
+       {
+               bool found = false;
+               for(unsigned j=0; (!found && j<uniforms.size()); ++j)
+                       if(uniforms[j].name==i->first)
+                       {
+                               if(uniforms[j].size!=i->second.size)
+                                       throw invalid_operation("Animation::prepare_keyframe");
+                               found = true;
+                       }
+
+               if(!found)
+                       uniforms.push_back(UniformInfo(i->first, i->second.size));
+       }
 
-       tkf.prepare();
+       tkf.prepare(*this);
 }
 
 
@@ -54,23 +103,22 @@ Animation::AxisInterpolation::AxisInterpolation():
        scale(0)
 { }
 
-Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
+Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
 {
        // Compute a normalized vector halfway between the two endpoints
-       double half[3];
-       double len = 0;
+       float half[3];
+       float a1_len = 0;
+       float h_len = 0;
        for(unsigned i=0; i<3; ++i)
        {
                half[i] = (axis1[i]+axis2[i])/2;
-               len += half[i]*half[i];
+               a1_len += axis1[i]*axis1[i];
+               h_len += half[i]*half[i];
        }
-       len = sqrt(len);
-       for(unsigned i=0; i<3; ++i)
-               half[i] /= len;
 
        // Compute correction factors for smooth interpolation
-       double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
-       double angle = acos(cos_half);
+       float cos_half = (axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2])/sqrt(a1_len*h_len);
+       float angle = acos(cos_half);
        slope = (angle ? angle/tan(angle) : 1);
        scale = cos_half;
 }
@@ -85,8 +133,8 @@ Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matr
        matrix1(&m1),
        matrix2(&m2)
 {
-       const double *m1_data = matrix1->data();
-       const double *m2_data = matrix2->data();
+       const float *m1_data = matrix1->data();
+       const float *m2_data = matrix2->data();
        for(unsigned i=0; i<3; ++i)
                axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
 }
@@ -95,12 +143,12 @@ Matrix Animation::MatrixInterpolation::get(float t) const
 {
        float u = t*2.0f-1.0f;
 
-       double matrix[16];
+       float matrix[16];
        for(unsigned i=0; i<4; ++i)
        {
-               const double *m1_col = matrix1->data()+i*4;
-               const double *m2_col = matrix2->data()+i*4;
-               double *out_col = matrix+i*4;
+               const float *m1_col = matrix1->data()+i*4;
+               const float *m2_col = matrix2->data()+i*4;
+               float *out_col = matrix+i*4;
 
                if(i<3)
                {
@@ -112,7 +160,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. */
@@ -137,15 +185,29 @@ Matrix Animation::MatrixInterpolation::get(float t) const
 }
 
 
-Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
-       animation(a),
+Animation::TimedKeyFrame::TimedKeyFrame():
        prev(0)
 { }
 
-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)
+       {
+               unsigned j = animation.get_slot_for_uniform(i->first);
+               uniforms.reserve(j+1);
+               for(unsigned k=uniforms.size(); k<=j; ++k)
+                       uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
+
+               uniforms[j] = i->second;
+       }
+
+       if(!prev)
+               return;
+
        delta_t = time-prev->time;
        matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
+
        if(animation.armature)
        {
                unsigned max_index = animation.armature->get_max_link_index();
@@ -163,9 +225,15 @@ void Animation::TimedKeyFrame::prepare()
 }
 
 
+Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
+       name(n),
+       size(s)
+{ }
+
+
 Animation::Iterator::Iterator(const Animation &a):
-       animation(a),
-       iter(animation.keyframes.begin()),
+       animation(&a),
+       iter(animation->keyframes.begin()),
        end(false)
 { }
 
@@ -174,12 +242,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;
@@ -203,11 +271,29 @@ Matrix Animation::Iterator::get_matrix() const
        return iter->matrix.get(time_since_keyframe/iter->delta_t);
 }
 
+KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
+{
+       if(!iter->prev)
+       {
+               if(iter->uniforms.size()>i)
+                       return iter->uniforms[i];
+               else
+                       return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
+       }
+
+       unsigned size = animation->uniforms[i].size;
+       float t = time_since_keyframe/iter->delta_t;
+       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;
+       return result;
+}
+
 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
 {
-       if(!animation.armature)
-               throw logic_error("Animation::Iterator::get_pose_matrix");
-       if(link>animation.armature->get_max_link_index())
+       if(!animation->armature)
+               throw invalid_operation("Animation::Iterator::get_pose_matrix");
+       if(link>animation->armature->get_max_link_index())
                throw out_of_range("Animation::Iterator::get_pose_matrix");
 
        if(!iter->prev)
@@ -219,10 +305,11 @@ Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
        }
 
        // We must redo the base point correction since interpolation throws if 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();
+       const Vector3 &base = animation->armature->get_link(link).get_base();
        Vector3 new_base = result*base;
-       result = Matrix::translation(base.x-new_base.x, base.y-new_base.y, base.z-new_base.z)*result;
+       result = Matrix::translation(base-new_base)*result;
        return result;
 }
 
@@ -261,11 +348,7 @@ 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);
 }
 
 void Animation::Loader::interval(float t)