]> git.tdb.fi Git - libs/gl.git/commitdiff
Restructure Animation to make matrix interpolation code more reusable
authorMikko Rasa <tdb@tdb.fi>
Sat, 11 Aug 2012 18:28:15 +0000 (21:28 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 11 Aug 2012 18:28:15 +0000 (21:28 +0300)
source/animation.cpp
source/animation.h

index eb22ab29f0462fa5c686b2542b9b21ba155f5a6e..53543d1b89b8426f576305604c046651cea156a7 100644 (file)
@@ -20,7 +20,7 @@ void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
        if(!keyframes.empty() && t<keyframes.back().time)
                throw invalid_argument("Animation::add_keyframe");
 
-       TimedKeyFrame tkf;
+       TimedKeyFrame tkf(*this);
        tkf.time = t;
        tkf.keyframe = &kf;
        tkf.keyframe.keep();
@@ -34,53 +34,62 @@ void Animation::prepare_keyframe(TimedKeyFrame &tkf)
        if(!tkf.prev)
                return;
 
-       tkf.delta_t = tkf.time-tkf.prev->time;
+       tkf.prepare();
 
-       const double *m1_data = tkf.prev->keyframe->get_matrix().data();
-       const double *m2_data = tkf.keyframe->get_matrix().data();
+}
+
+
+Animation::AxisInterpolation::AxisInterpolation():
+       slope(0),
+       scale(0)
+{ }
+
+Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
+{
+       // Compute a normalized vector halfway between the two endpoints
+       double half[3];
+       double len = 0;
        for(unsigned i=0; i<3; ++i)
        {
-               const double *m1_col = m1_data+i*4;
-               const double *m2_col = m2_data+i*4;
-
-               // Compute a normalized vector halfway between the two endpoints
-               double half[3];
-               double len = 0;
-               for(unsigned j=0; j<3; ++j)
-               {
-                       half[j] = (m1_col[j]+m2_col[j])/2;
-                       len += half[j]*half[j];
-               }
-               len = sqrt(len);
-               for(unsigned j=0; j<3; ++j)
-                       half[j] /= len;
-
-               // Compute correction factors for smooth interpolation
-               double cos_half = m1_col[0]*half[0]+m1_col[1]*half[1]+m1_col[2]*half[2];
-               double angle = acos(cos_half);
-               tkf.axes[i].slope = (angle ? angle/tan(angle) : 1);
-               tkf.axes[i].scale = cos_half;
+               half[i] = (axis1[i]+axis2[i])/2;
+               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);
+       slope = (angle ? angle/tan(angle) : 1);
+       scale = cos_half;
 }
 
-Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta &dt) const
+
+Animation::MatrixInterpolation::MatrixInterpolation():
+       matrix1(0),
+       matrix2(0)
+{ }
+
+Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
+       matrix1(&m1),
+       matrix2(&m2)
 {
-       if(!dt)
-               return tkf.keyframe->get_matrix();
-       if(!tkf.prev)
-               throw invalid_argument("Animation::compute_matrix");
-       const TimedKeyFrame &prev = *tkf.prev;
+       const double *m1_data = matrix1->data();
+       const double *m2_data = matrix2->data();
+       for(unsigned i=0; i<3; ++i)
+               axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
+}
 
-       float t = dt/tkf.delta_t;
+Matrix Animation::MatrixInterpolation::get(float t) const
+{
        float u = t*2.0f-1.0f;
 
        double matrix[16];
-       const double *m1_data = prev.keyframe->get_matrix().data();
-       const double *m2_data = tkf.keyframe->get_matrix().data();
        for(unsigned i=0; i<4; ++i)
        {
-               const double *m1_col = m1_data+i*4;
-               const double *m2_col = m2_data+i*4;
+               const double *m1_col = matrix1->data()+i*4;
+               const double *m2_col = matrix2->data()+i*4;
                double *out_col = matrix+i*4;
 
                if(i<3)
@@ -91,13 +100,13 @@ Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta
                        around the halfway point and computing its tangent.  This is
                        approximated by a third degree polynomial, scaled so that the result
                        will be in the range [-1, 1]. */
-                       float w = (tkf.axes[i].slope+(1-tkf.axes[i].slope)*u*u)*u*0.5f+0.5f;
+                       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 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. */
-                       float n = (tkf.axes[i].scale+(1-tkf.axes[i].scale)*u*u);
+                       float n = (axes[i].scale+(1-axes[i].scale)*u*u);
 
                        for(unsigned j=0; j<3; ++j)
                                out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
@@ -118,11 +127,17 @@ Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta
 }
 
 
-Animation::AxisInterpolation::AxisInterpolation():
-       slope(0),
-       scale(0)
+Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
+       animation(a),
+       prev(0)
 { }
 
+void Animation::TimedKeyFrame::prepare()
+{
+       delta_t = time-prev->time;
+       matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
+}
+
 
 Animation::Iterator::Iterator(const Animation &a):
        animation(a),
@@ -158,7 +173,10 @@ Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
 
 Matrix Animation::Iterator::get_matrix() const
 {
-       return animation.compute_matrix(*iter, time_since_keyframe);
+       if(!iter->prev)
+               return iter->keyframe->get_matrix();
+
+       return iter->matrix.get(time_since_keyframe/iter->delta_t);
 }
 
 
@@ -192,7 +210,7 @@ void Animation::Loader::keyframe_inline()
        RefPtr<KeyFrame> kf = new KeyFrame;
        load_sub(*kf);
 
-       TimedKeyFrame tkf;
+       TimedKeyFrame tkf(obj);
        tkf.time = current_time;
        tkf.keyframe = kf;
        obj.prepare_keyframe(tkf);
index 1460c944894350ed5aa44ea57f5c0465c8da6a44..a4bdabdd95401757b32c032ca56b646c2e58c92b 100644 (file)
@@ -41,15 +41,32 @@ private:
                float scale;
 
                AxisInterpolation();
+               AxisInterpolation(const double *, const double *);
+       };
+
+       struct MatrixInterpolation
+       {
+               const Matrix *matrix1;
+               const Matrix *matrix2;
+               AxisInterpolation axes[3];
+
+               MatrixInterpolation();
+               MatrixInterpolation(const Matrix &, const Matrix &);
+
+               Matrix get(float) const;
        };
 
        struct TimedKeyFrame
        {
+               const Animation &animation;
                const TimedKeyFrame *prev;
                Time::TimeDelta time;
                Time::TimeDelta delta_t;
                RefPtr<const KeyFrame> keyframe;
-               AxisInterpolation axes[3];
+               MatrixInterpolation matrix;
+
+               TimedKeyFrame(const Animation &);
+               void prepare();
        };
 
        typedef std::list<TimedKeyFrame> KeyFrameList;
@@ -80,11 +97,11 @@ public:
        Animation();
 
        void add_keyframe(const Time::TimeDelta &, const KeyFrame &);
-       void set_looping(bool);
 private:
        void prepare_keyframe(TimedKeyFrame &);
 
-       Matrix compute_matrix(const TimedKeyFrame &, const Time::TimeDelta &) const;
+public:
+       void set_looping(bool);
 };
 
 } // namespace GL