2 #include <msp/datafile/collection.h>
3 #include <msp/time/units.h>
9 #include <msp/io/print.h>
14 Animation::Animation():
18 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
20 if(!keyframes.empty() && t<keyframes.back().time)
21 throw invalid_argument("Animation::add_keyframe");
23 TimedKeyFrame tkf(*this);
27 prepare_keyframe(tkf);
28 keyframes.push_back(tkf);
31 void Animation::set_looping(bool l)
36 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
38 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
47 Animation::AxisInterpolation::AxisInterpolation():
52 Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
54 // Compute a normalized vector halfway between the two endpoints
57 for(unsigned i=0; i<3; ++i)
59 half[i] = (axis1[i]+axis2[i])/2;
60 len += half[i]*half[i];
63 for(unsigned i=0; i<3; ++i)
66 // Compute correction factors for smooth interpolation
67 double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
68 double angle = acos(cos_half);
69 slope = (angle ? angle/tan(angle) : 1);
74 Animation::MatrixInterpolation::MatrixInterpolation():
79 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
83 const double *m1_data = matrix1->data();
84 const double *m2_data = matrix2->data();
85 for(unsigned i=0; i<3; ++i)
86 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
89 Matrix Animation::MatrixInterpolation::get(float t) const
91 float u = t*2.0f-1.0f;
94 for(unsigned i=0; i<4; ++i)
96 const double *m1_col = matrix1->data()+i*4;
97 const double *m2_col = matrix2->data()+i*4;
98 double *out_col = matrix+i*4;
102 /* Linear interpolation will produce vectors that fall on the line
103 between the two endpoints, and has a higher angular velocity near the
104 middle. We compensate for the velocity by interpolating the angle
105 around the halfway point and computing its tangent. This is
106 approximated by a third degree polynomial, scaled so that the result
107 will be in the range [-1, 1]. */
108 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
110 /* The interpolate vectors will also be shorter than unit length. At
111 the halfway point the length will be equal to the cosine of half the
112 angle, which was computed earlier. Use a second degree polynomial to
114 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
116 for(unsigned j=0; j<3; ++j)
117 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
121 for(unsigned j=0; j<3; ++j)
122 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
135 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
140 void Animation::TimedKeyFrame::prepare()
142 delta_t = time-prev->time;
143 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
147 Animation::Iterator::Iterator(const Animation &a):
149 iter(animation.keyframes.begin()),
153 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
155 time_since_keyframe += t;
156 while(time_since_keyframe>iter->delta_t)
158 KeyFrameList::const_iterator next = iter;
160 if(next==animation.keyframes.end())
162 if(animation.looping)
163 next = animation.keyframes.begin();
167 time_since_keyframe = iter->delta_t;
172 time_since_keyframe -= iter->delta_t;
179 Matrix Animation::Iterator::get_matrix() const
182 return iter->keyframe->get_matrix();
184 return iter->matrix.get(time_since_keyframe/iter->delta_t);
188 Animation::Loader::Loader(Animation &a):
189 DataFile::CollectionObjectLoader<Animation>(a, 0)
194 Animation::Loader::Loader(Animation &a, Collection &c):
195 DataFile::CollectionObjectLoader<Animation>(a, &c)
200 void Animation::Loader::init()
202 add("interval", &Loader::interval);
203 add("keyframe", &Loader::keyframe);
204 add("keyframe", &Loader::keyframe_inline);
205 add("looping", &Animation::looping);
208 void Animation::Loader::keyframe(const string &n)
210 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
213 void Animation::Loader::keyframe_inline()
215 RefPtr<KeyFrame> kf = new KeyFrame;
218 TimedKeyFrame tkf(obj);
219 tkf.time = current_time;
221 obj.prepare_keyframe(tkf);
222 obj.keyframes.push_back(tkf);
225 void Animation::Loader::interval(float t)
227 current_time += t*Time::sec;