2 #include <msp/datafile/collection.h>
3 #include <msp/time/units.h>
15 Animation::Animation():
20 // Avoid synthesizing ~RefPtr in files including animation.h
21 Animation::~Animation()
24 void Animation::set_armature(const Armature &a)
29 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
31 if(!keyframes.empty() && t<keyframes.back().time)
32 throw invalid_argument("Animation::add_keyframe");
34 TimedKeyFrame tkf(*this);
38 prepare_keyframe(tkf);
39 keyframes.push_back(tkf);
42 void Animation::set_looping(bool l)
47 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
49 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
57 Animation::AxisInterpolation::AxisInterpolation():
62 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
64 // Compute a normalized vector halfway between the two endpoints
68 for(unsigned i=0; i<3; ++i)
70 half[i] = (axis1[i]+axis2[i])/2;
71 a1_len += axis1[i]*axis1[i];
72 h_len += half[i]*half[i];
75 // Compute correction factors for smooth interpolation
76 float cos_half = (axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2])/sqrt(a1_len*h_len);
77 float angle = acos(cos_half);
78 slope = (angle ? angle/tan(angle) : 1);
83 Animation::MatrixInterpolation::MatrixInterpolation():
88 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
92 const float *m1_data = matrix1->data();
93 const float *m2_data = matrix2->data();
94 for(unsigned i=0; i<3; ++i)
95 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
98 Matrix Animation::MatrixInterpolation::get(float t) const
100 float u = t*2.0f-1.0f;
103 for(unsigned i=0; i<4; ++i)
105 const float *m1_col = matrix1->data()+i*4;
106 const float *m2_col = matrix2->data()+i*4;
107 float *out_col = matrix+i*4;
111 /* Linear interpolation will produce vectors that fall on the line
112 between the two endpoints, and has a higher angular velocity near the
113 middle. We compensate for the velocity by interpolating the angle
114 around the halfway point and computing its tangent. This is
115 approximated by a third degree polynomial, scaled so that the result
116 will be in the range [-1, 1]. */
117 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
119 /* The interpolate vectors will also be shorter than unit length. At
120 the halfway point the length will be equal to the cosine of half the
121 angle, which was computed earlier. Use a second degree polynomial to
123 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
125 for(unsigned j=0; j<3; ++j)
126 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
130 for(unsigned j=0; j<3; ++j)
131 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
144 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
149 void Animation::TimedKeyFrame::prepare()
151 delta_t = time-prev->time;
152 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
153 if(animation.armature)
155 unsigned max_index = animation.armature->get_max_link_index();
156 pose_matrices.resize(max_index+1);
157 const Pose *pose1 = prev->keyframe->get_pose();
158 const Pose *pose2 = keyframe->get_pose();
159 static Matrix identity;
160 for(unsigned i=0; i<=max_index; ++i)
162 const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
163 const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
164 pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
170 Animation::Iterator::Iterator(const Animation &a):
172 iter(animation.keyframes.begin()),
176 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
178 time_since_keyframe += t;
179 while(time_since_keyframe>iter->delta_t)
181 KeyFrameList::const_iterator next = iter;
183 if(next==animation.keyframes.end())
185 if(animation.looping)
186 next = animation.keyframes.begin();
190 time_since_keyframe = iter->delta_t;
195 time_since_keyframe -= iter->delta_t;
202 Matrix Animation::Iterator::get_matrix() const
205 return iter->keyframe->get_matrix();
207 return iter->matrix.get(time_since_keyframe/iter->delta_t);
210 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
212 if(!animation.armature)
213 throw invalid_operation("Animation::Iterator::get_pose_matrix");
214 if(link>animation.armature->get_max_link_index())
215 throw out_of_range("Animation::Iterator::get_pose_matrix");
219 if(const Pose *pose = iter->keyframe->get_pose())
220 return pose->get_link_matrix(link);
225 // We must redo the base point correction since interpolation throws if off
226 // XXX This should probably be done on local matrices
227 Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
228 const Vector3 &base = animation.armature->get_link(link).get_base();
229 Vector3 new_base = result*base;
230 result = Matrix::translation(base-new_base)*result;
235 Animation::Loader::Loader(Animation &a):
236 DataFile::CollectionObjectLoader<Animation>(a, 0)
241 Animation::Loader::Loader(Animation &a, Collection &c):
242 DataFile::CollectionObjectLoader<Animation>(a, &c)
247 void Animation::Loader::init()
249 add("armature", &Animation::armature);
250 add("interval", &Loader::interval);
251 add("keyframe", &Loader::keyframe);
252 add("keyframe", &Loader::keyframe_inline);
253 add("looping", &Animation::looping);
256 void Animation::Loader::keyframe(const string &n)
258 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
261 void Animation::Loader::keyframe_inline()
263 RefPtr<KeyFrame> kf = new KeyFrame;
265 load_sub(*kf, get_collection());
269 TimedKeyFrame tkf(obj);
270 tkf.time = current_time;
272 obj.prepare_keyframe(tkf);
273 obj.keyframes.push_back(tkf);
276 void Animation::Loader::interval(float t)
278 current_time += t*Time::sec;