2 #include <msp/datafile/collection.h>
3 #include <msp/time/units.h>
15 Animation::Animation():
20 void Animation::set_armature(const Armature &a)
25 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
27 if(!keyframes.empty() && t<keyframes.back().time)
28 throw invalid_argument("Animation::add_keyframe");
30 TimedKeyFrame tkf(*this);
34 prepare_keyframe(tkf);
35 keyframes.push_back(tkf);
38 void Animation::set_looping(bool l)
43 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
45 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
53 Animation::AxisInterpolation::AxisInterpolation():
58 Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
60 // Compute a normalized vector halfway between the two endpoints
63 for(unsigned i=0; i<3; ++i)
65 half[i] = (axis1[i]+axis2[i])/2;
66 len += half[i]*half[i];
69 for(unsigned i=0; i<3; ++i)
72 // Compute correction factors for smooth interpolation
73 double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
74 double angle = acos(cos_half);
75 slope = (angle ? angle/tan(angle) : 1);
80 Animation::MatrixInterpolation::MatrixInterpolation():
85 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
89 const double *m1_data = matrix1->data();
90 const double *m2_data = matrix2->data();
91 for(unsigned i=0; i<3; ++i)
92 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
95 Matrix Animation::MatrixInterpolation::get(float t) const
97 float u = t*2.0f-1.0f;
100 for(unsigned i=0; i<4; ++i)
102 const double *m1_col = matrix1->data()+i*4;
103 const double *m2_col = matrix2->data()+i*4;
104 double *out_col = matrix+i*4;
108 /* Linear interpolation will produce vectors that fall on the line
109 between the two endpoints, and has a higher angular velocity near the
110 middle. We compensate for the velocity by interpolating the angle
111 around the halfway point and computing its tangent. This is
112 approximated by a third degree polynomial, scaled so that the result
113 will be in the range [-1, 1]. */
114 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
116 /* The interpolate vectors will also be shorter than unit length. At
117 the halfway point the length will be equal to the cosine of half the
118 angle, which was computed earlier. Use a second degree polynomial to
120 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
122 for(unsigned j=0; j<3; ++j)
123 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
127 for(unsigned j=0; j<3; ++j)
128 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
141 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
146 void Animation::TimedKeyFrame::prepare()
148 delta_t = time-prev->time;
149 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
150 if(animation.armature)
152 unsigned max_index = animation.armature->get_max_link_index();
153 pose_matrices.resize(max_index+1);
154 const Pose *pose1 = prev->keyframe->get_pose();
155 const Pose *pose2 = keyframe->get_pose();
156 static Matrix identity;
157 for(unsigned i=0; i<=max_index; ++i)
159 const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
160 const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
161 pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
167 Animation::Iterator::Iterator(const Animation &a):
169 iter(animation.keyframes.begin()),
173 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
175 time_since_keyframe += t;
176 while(time_since_keyframe>iter->delta_t)
178 KeyFrameList::const_iterator next = iter;
180 if(next==animation.keyframes.end())
182 if(animation.looping)
183 next = animation.keyframes.begin();
187 time_since_keyframe = iter->delta_t;
192 time_since_keyframe -= iter->delta_t;
199 Matrix Animation::Iterator::get_matrix() const
202 return iter->keyframe->get_matrix();
204 return iter->matrix.get(time_since_keyframe/iter->delta_t);
207 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
209 if(!animation.armature)
210 throw invalid_operation("Animation::Iterator::get_pose_matrix");
211 if(link>animation.armature->get_max_link_index())
212 throw out_of_range("Animation::Iterator::get_pose_matrix");
216 if(const Pose *pose = iter->keyframe->get_pose())
217 return pose->get_link_matrix(link);
222 // We must redo the base point correction since interpolation throws if off
223 Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
224 const Vector3 &base = animation.armature->get_link(link).get_base();
225 Vector3 new_base = result*base;
226 result = Matrix::translation(base.x-new_base.x, base.y-new_base.y, base.z-new_base.z)*result;
231 Animation::Loader::Loader(Animation &a):
232 DataFile::CollectionObjectLoader<Animation>(a, 0)
237 Animation::Loader::Loader(Animation &a, Collection &c):
238 DataFile::CollectionObjectLoader<Animation>(a, &c)
243 void Animation::Loader::init()
245 add("armature", &Animation::armature);
246 add("interval", &Loader::interval);
247 add("keyframe", &Loader::keyframe);
248 add("keyframe", &Loader::keyframe_inline);
249 add("looping", &Animation::looping);
252 void Animation::Loader::keyframe(const string &n)
254 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
257 void Animation::Loader::keyframe_inline()
259 RefPtr<KeyFrame> kf = new KeyFrame;
261 load_sub(*kf, get_collection());
265 TimedKeyFrame tkf(obj);
266 tkf.time = current_time;
268 obj.prepare_keyframe(tkf);
269 obj.keyframes.push_back(tkf);
272 void Animation::Loader::interval(float t)
274 current_time += t*Time::sec;