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::prepare_keyframe(TimedKeyFrame &tkf)
33 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
42 Animation::AxisInterpolation::AxisInterpolation():
47 Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
49 // Compute a normalized vector halfway between the two endpoints
52 for(unsigned i=0; i<3; ++i)
54 half[i] = (axis1[i]+axis2[i])/2;
55 len += half[i]*half[i];
58 for(unsigned i=0; i<3; ++i)
61 // Compute correction factors for smooth interpolation
62 double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
63 double angle = acos(cos_half);
64 slope = (angle ? angle/tan(angle) : 1);
69 Animation::MatrixInterpolation::MatrixInterpolation():
74 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
78 const double *m1_data = matrix1->data();
79 const double *m2_data = matrix2->data();
80 for(unsigned i=0; i<3; ++i)
81 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
84 Matrix Animation::MatrixInterpolation::get(float t) const
86 float u = t*2.0f-1.0f;
89 for(unsigned i=0; i<4; ++i)
91 const double *m1_col = matrix1->data()+i*4;
92 const double *m2_col = matrix2->data()+i*4;
93 double *out_col = matrix+i*4;
97 /* Linear interpolation will produce vectors that fall on the line
98 between the two endpoints, and has a higher angular velocity near the
99 middle. We compensate for the velocity by interpolating the angle
100 around the halfway point and computing its tangent. This is
101 approximated by a third degree polynomial, scaled so that the result
102 will be in the range [-1, 1]. */
103 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
105 /* The interpolate vectors will also be shorter than unit length. At
106 the halfway point the length will be equal to the cosine of half the
107 angle, which was computed earlier. Use a second degree polynomial to
109 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
111 for(unsigned j=0; j<3; ++j)
112 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
116 for(unsigned j=0; j<3; ++j)
117 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
130 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
135 void Animation::TimedKeyFrame::prepare()
137 delta_t = time-prev->time;
138 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
142 Animation::Iterator::Iterator(const Animation &a):
144 iter(animation.keyframes.begin()),
148 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
150 time_since_keyframe += t;
151 while(time_since_keyframe>iter->delta_t)
153 KeyFrameList::const_iterator next = iter;
155 if(next==animation.keyframes.end())
157 if(animation.looping)
158 next = animation.keyframes.begin();
162 time_since_keyframe = iter->delta_t;
167 time_since_keyframe -= iter->delta_t;
174 Matrix Animation::Iterator::get_matrix() const
177 return iter->keyframe->get_matrix();
179 return iter->matrix.get(time_since_keyframe/iter->delta_t);
183 Animation::Loader::Loader(Animation &a):
184 DataFile::CollectionObjectLoader<Animation>(a, 0)
189 Animation::Loader::Loader(Animation &a, Collection &c):
190 DataFile::CollectionObjectLoader<Animation>(a, &c)
195 void Animation::Loader::init()
197 add("interval", &Loader::interval);
198 add("keyframe", &Loader::keyframe);
199 add("keyframe", &Loader::keyframe_inline);
200 add("looping", &Animation::looping);
203 void Animation::Loader::keyframe(const string &n)
205 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
208 void Animation::Loader::keyframe_inline()
210 RefPtr<KeyFrame> kf = new KeyFrame;
213 TimedKeyFrame tkf(obj);
214 tkf.time = current_time;
216 obj.prepare_keyframe(tkf);
217 obj.keyframes.push_back(tkf);
220 void Animation::Loader::interval(float t)
222 current_time += t*Time::sec;