2 #include <msp/datafile/collection.h>
3 #include <msp/time/units.h>
14 Animation::Animation():
19 void Animation::set_armature(const Armature &a)
24 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
26 if(!keyframes.empty() && t<keyframes.back().time)
27 throw invalid_argument("Animation::add_keyframe");
29 TimedKeyFrame tkf(*this);
33 prepare_keyframe(tkf);
34 keyframes.push_back(tkf);
37 void Animation::set_looping(bool l)
42 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
44 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
52 Animation::AxisInterpolation::AxisInterpolation():
57 Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
59 // Compute a normalized vector halfway between the two endpoints
62 for(unsigned i=0; i<3; ++i)
64 half[i] = (axis1[i]+axis2[i])/2;
65 len += half[i]*half[i];
68 for(unsigned i=0; i<3; ++i)
71 // Compute correction factors for smooth interpolation
72 double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
73 double angle = acos(cos_half);
74 slope = (angle ? angle/tan(angle) : 1);
79 Animation::MatrixInterpolation::MatrixInterpolation():
84 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
88 const double *m1_data = matrix1->data();
89 const double *m2_data = matrix2->data();
90 for(unsigned i=0; i<3; ++i)
91 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
94 Matrix Animation::MatrixInterpolation::get(float t) const
96 float u = t*2.0f-1.0f;
99 for(unsigned i=0; i<4; ++i)
101 const double *m1_col = matrix1->data()+i*4;
102 const double *m2_col = matrix2->data()+i*4;
103 double *out_col = matrix+i*4;
107 /* Linear interpolation will produce vectors that fall on the line
108 between the two endpoints, and has a higher angular velocity near the
109 middle. We compensate for the velocity by interpolating the angle
110 around the halfway point and computing its tangent. This is
111 approximated by a third degree polynomial, scaled so that the result
112 will be in the range [-1, 1]. */
113 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
115 /* The interpolate vectors will also be shorter than unit length. At
116 the halfway point the length will be equal to the cosine of half the
117 angle, which was computed earlier. Use a second degree polynomial to
119 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
121 for(unsigned j=0; j<3; ++j)
122 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
126 for(unsigned j=0; j<3; ++j)
127 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
140 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
145 void Animation::TimedKeyFrame::prepare()
147 delta_t = time-prev->time;
148 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
149 if(animation.armature)
151 unsigned max_index = animation.armature->get_max_link_index();
152 pose_matrices.resize(max_index+1);
153 const Pose *pose1 = prev->keyframe->get_pose();
154 const Pose *pose2 = keyframe->get_pose();
155 static Matrix identity;
156 for(unsigned i=0; i<=max_index; ++i)
158 const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
159 const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
160 pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
166 Animation::Iterator::Iterator(const Animation &a):
168 iter(animation.keyframes.begin()),
172 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
174 time_since_keyframe += t;
175 while(time_since_keyframe>iter->delta_t)
177 KeyFrameList::const_iterator next = iter;
179 if(next==animation.keyframes.end())
181 if(animation.looping)
182 next = animation.keyframes.begin();
186 time_since_keyframe = iter->delta_t;
191 time_since_keyframe -= iter->delta_t;
198 Matrix Animation::Iterator::get_matrix() const
201 return iter->keyframe->get_matrix();
203 return iter->matrix.get(time_since_keyframe/iter->delta_t);
206 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
208 if(!animation.armature)
209 throw logic_error("Animation::Iterator::get_pose_matrix");
210 if(link>animation.armature->get_max_link_index())
211 throw out_of_range("Animation::Iterator::get_pose_matrix");
215 if(const Pose *pose = iter->keyframe->get_pose())
216 return pose->get_link_matrix(link);
221 // We must redo the base point correction since interpolation throws if off
222 Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
223 const Vector3 &base = animation.armature->get_link(link).get_base();
224 Vector3 new_base = result*base;
225 result = Matrix::translation(base.x-new_base.x, base.y-new_base.y, base.z-new_base.z)*result;
230 Animation::Loader::Loader(Animation &a):
231 DataFile::CollectionObjectLoader<Animation>(a, 0)
236 Animation::Loader::Loader(Animation &a, Collection &c):
237 DataFile::CollectionObjectLoader<Animation>(a, &c)
242 void Animation::Loader::init()
244 add("armature", &Animation::armature);
245 add("interval", &Loader::interval);
246 add("keyframe", &Loader::keyframe);
247 add("keyframe", &Loader::keyframe_inline);
248 add("looping", &Animation::looping);
251 void Animation::Loader::keyframe(const string &n)
253 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
256 void Animation::Loader::keyframe_inline()
258 RefPtr<KeyFrame> kf = new KeyFrame;
260 load_sub(*kf, get_collection());
264 TimedKeyFrame tkf(obj);
265 tkf.time = current_time;
267 obj.prepare_keyframe(tkf);
268 obj.keyframes.push_back(tkf);
271 void Animation::Loader::interval(float t)
273 current_time += t*Time::sec;