2 #include <msp/core/maputils.h>
3 #include <msp/datafile/collection.h>
4 #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 unsigned Animation::get_slot_for_uniform(const string &n) const
31 for(unsigned i=0; i<uniforms.size(); ++i)
32 if(uniforms[i].name==n)
37 const string &Animation::get_uniform_name(unsigned i) const
39 if(i>=uniforms.size())
40 throw out_of_range("Animation::get_uniform_name");
41 return uniforms[i].name;
44 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
46 if(!keyframes.empty() && t<keyframes.back().time)
47 throw invalid_argument("Animation::add_keyframe");
49 TimedKeyFrame tkf(*this);
53 prepare_keyframe(tkf);
54 keyframes.push_back(tkf);
57 void Animation::set_looping(bool l)
62 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
64 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
66 const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
67 for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
70 for(unsigned j=0; (!found && j<uniforms.size()); ++j)
71 if(uniforms[j].name==i->first)
73 if(uniforms[j].size!=i->second.size)
74 throw invalid_operation("Animation::prepare_keyframe");
79 uniforms.push_back(UniformInfo(i->first, i->second.size));
86 Animation::AxisInterpolation::AxisInterpolation():
91 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
93 // Compute a normalized vector halfway between the two endpoints
97 for(unsigned i=0; i<3; ++i)
99 half[i] = (axis1[i]+axis2[i])/2;
100 a1_len += axis1[i]*axis1[i];
101 h_len += half[i]*half[i];
104 // Compute correction factors for smooth interpolation
105 float cos_half = (axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2])/sqrt(a1_len*h_len);
106 float angle = acos(cos_half);
107 slope = (angle ? angle/tan(angle) : 1);
112 Animation::MatrixInterpolation::MatrixInterpolation():
117 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
121 const float *m1_data = matrix1->data();
122 const float *m2_data = matrix2->data();
123 for(unsigned i=0; i<3; ++i)
124 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
127 Matrix Animation::MatrixInterpolation::get(float t) const
129 float u = t*2.0f-1.0f;
132 for(unsigned i=0; i<4; ++i)
134 const float *m1_col = matrix1->data()+i*4;
135 const float *m2_col = matrix2->data()+i*4;
136 float *out_col = matrix+i*4;
140 /* Linear interpolation will produce vectors that fall on the line
141 between the two endpoints, and has a higher angular velocity near the
142 middle. We compensate for the velocity by interpolating the angle
143 around the halfway point and computing its tangent. This is
144 approximated by a third degree polynomial, scaled so that the result
145 will be in the range [-1, 1]. */
146 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
148 /* The interpolated vectors will also be shorter than unit length. At
149 the halfway point the length will be equal to the cosine of half the
150 angle, which was computed earlier. Use a second degree polynomial to
152 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
154 for(unsigned j=0; j<3; ++j)
155 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
159 for(unsigned j=0; j<3; ++j)
160 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
173 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
178 void Animation::TimedKeyFrame::prepare()
180 const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
181 for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
183 unsigned j = animation.get_slot_for_uniform(i->first);
184 uniforms.reserve(j+1);
185 for(unsigned k=uniforms.size(); k<=j; ++k)
186 uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
188 uniforms[j] = i->second;
194 delta_t = time-prev->time;
195 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
197 if(animation.armature)
199 unsigned max_index = animation.armature->get_max_link_index();
200 pose_matrices.resize(max_index+1);
201 const Pose *pose1 = prev->keyframe->get_pose();
202 const Pose *pose2 = keyframe->get_pose();
203 static Matrix identity;
204 for(unsigned i=0; i<=max_index; ++i)
206 const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
207 const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
208 pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
214 Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
220 Animation::Iterator::Iterator(const Animation &a):
222 iter(animation->keyframes.begin()),
226 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
228 time_since_keyframe += t;
229 while(time_since_keyframe>iter->delta_t)
231 vector<TimedKeyFrame>::const_iterator next = iter;
233 if(next==animation->keyframes.end())
235 if(animation->looping)
236 next = animation->keyframes.begin();
240 time_since_keyframe = iter->delta_t;
245 time_since_keyframe -= iter->delta_t;
252 Matrix Animation::Iterator::get_matrix() const
255 return iter->keyframe->get_matrix();
257 return iter->matrix.get(time_since_keyframe/iter->delta_t);
260 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
264 if(iter->uniforms.size()>i)
265 return iter->uniforms[i];
267 return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
270 unsigned size = animation->uniforms[i].size;
271 float t = time_since_keyframe/iter->delta_t;
272 KeyFrame::AnimatedUniform result(size, 0.0f);
273 for(unsigned j=0; j<size; ++j)
274 result.values[j] = iter->prev->uniforms[i].values[j]*(1-t)+iter->uniforms[i].values[j]*t;
278 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
280 if(!animation->armature)
281 throw invalid_operation("Animation::Iterator::get_pose_matrix");
282 if(link>animation->armature->get_max_link_index())
283 throw out_of_range("Animation::Iterator::get_pose_matrix");
287 if(const Pose *pose = iter->keyframe->get_pose())
288 return pose->get_link_matrix(link);
293 // We must redo the base point correction since interpolation throws if off
294 // XXX This should probably be done on local matrices
295 Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
296 const Vector3 &base = animation->armature->get_link(link).get_base();
297 Vector3 new_base = result*base;
298 result = Matrix::translation(base-new_base)*result;
303 Animation::Loader::Loader(Animation &a):
304 DataFile::CollectionObjectLoader<Animation>(a, 0)
309 Animation::Loader::Loader(Animation &a, Collection &c):
310 DataFile::CollectionObjectLoader<Animation>(a, &c)
315 void Animation::Loader::init()
317 add("armature", &Animation::armature);
318 add("interval", &Loader::interval);
319 add("keyframe", &Loader::keyframe);
320 add("keyframe", &Loader::keyframe_inline);
321 add("looping", &Animation::looping);
324 void Animation::Loader::keyframe(const string &n)
326 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
329 void Animation::Loader::keyframe_inline()
331 RefPtr<KeyFrame> kf = new KeyFrame;
333 load_sub(*kf, get_collection());
337 TimedKeyFrame tkf(obj);
338 tkf.time = current_time;
340 obj.prepare_keyframe(tkf);
341 obj.keyframes.push_back(tkf);
344 void Animation::Loader::interval(float t)
346 current_time += t*Time::sec;