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 RefPtr<const KeyFrame> kfr(&kf);
51 void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf)
53 if(!keyframes.empty() && t<keyframes.back().time)
54 throw invalid_argument("Animation::add_keyframe");
56 bool realloc = (keyframes.size()>=keyframes.capacity());
58 keyframes.push_back(TimedKeyFrame());
59 TimedKeyFrame &tkf = keyframes.back();
65 for(unsigned i=1; i<keyframes.size(); ++i)
66 keyframes[i].prev = &keyframes[i-1];
68 else if(keyframes.size()>1)
71 prepare_keyframe(tkf);
74 void Animation::set_looping(bool l)
79 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
81 const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
82 for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
85 for(unsigned j=0; (!found && j<uniforms.size()); ++j)
86 if(uniforms[j].name==i->first)
88 if(uniforms[j].size!=i->second.size)
89 throw invalid_operation("Animation::prepare_keyframe");
94 uniforms.push_back(UniformInfo(i->first, i->second.size));
101 Animation::AxisInterpolation::AxisInterpolation():
106 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
108 // Compute a normalized vector halfway between the two endpoints
112 for(unsigned i=0; i<3; ++i)
114 half[i] = (axis1[i]+axis2[i])/2;
115 a1_len += axis1[i]*axis1[i];
116 h_len += half[i]*half[i];
119 // Compute correction factors for smooth interpolation
120 float cos_half = (axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2])/sqrt(a1_len*h_len);
121 float angle = acos(cos_half);
122 slope = (angle ? angle/tan(angle) : 1);
127 Animation::MatrixInterpolation::MatrixInterpolation():
132 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
136 const float *m1_data = matrix1->data();
137 const float *m2_data = matrix2->data();
138 for(unsigned i=0; i<3; ++i)
139 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
142 Matrix Animation::MatrixInterpolation::get(float t) const
144 float u = t*2.0f-1.0f;
147 for(unsigned i=0; i<4; ++i)
149 const float *m1_col = matrix1->data()+i*4;
150 const float *m2_col = matrix2->data()+i*4;
151 float *out_col = matrix+i*4;
155 /* Linear interpolation will produce vectors that fall on the line
156 between the two endpoints, and has a higher angular velocity near the
157 middle. We compensate for the velocity by interpolating the angle
158 around the halfway point and computing its tangent. This is
159 approximated by a third degree polynomial, scaled so that the result
160 will be in the range [-1, 1]. */
161 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
163 /* The interpolated vectors will also be shorter than unit length. At
164 the halfway point the length will be equal to the cosine of half the
165 angle, which was computed earlier. Use a second degree polynomial to
167 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
169 for(unsigned j=0; j<3; ++j)
170 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
174 for(unsigned j=0; j<3; ++j)
175 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
188 Animation::TimedKeyFrame::TimedKeyFrame():
192 void Animation::TimedKeyFrame::prepare(const Animation &animation)
194 const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
195 for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
197 unsigned j = animation.get_slot_for_uniform(i->first);
198 uniforms.reserve(j+1);
199 for(unsigned k=uniforms.size(); k<=j; ++k)
200 uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
202 uniforms[j] = i->second;
208 delta_t = time-prev->time;
209 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
211 if(animation.armature)
213 unsigned max_index = animation.armature->get_max_link_index();
214 pose_matrices.resize(max_index+1);
215 const Pose *pose1 = prev->keyframe->get_pose();
216 const Pose *pose2 = keyframe->get_pose();
217 static Matrix identity;
218 for(unsigned i=0; i<=max_index; ++i)
220 const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
221 const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
222 pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
228 Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
234 Animation::Iterator::Iterator(const Animation &a):
236 iter(animation->keyframes.begin()),
240 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
242 time_since_keyframe += t;
243 while(time_since_keyframe>iter->delta_t)
245 vector<TimedKeyFrame>::const_iterator next = iter;
247 if(next==animation->keyframes.end())
249 if(animation->looping)
250 next = animation->keyframes.begin();
254 time_since_keyframe = iter->delta_t;
259 time_since_keyframe -= iter->delta_t;
266 Matrix Animation::Iterator::get_matrix() const
269 return iter->keyframe->get_matrix();
271 return iter->matrix.get(time_since_keyframe/iter->delta_t);
274 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
278 if(iter->uniforms.size()>i)
279 return iter->uniforms[i];
281 return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
284 unsigned size = animation->uniforms[i].size;
285 float t = time_since_keyframe/iter->delta_t;
286 KeyFrame::AnimatedUniform result(size, 0.0f);
287 for(unsigned j=0; j<size; ++j)
288 result.values[j] = iter->prev->uniforms[i].values[j]*(1-t)+iter->uniforms[i].values[j]*t;
292 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
294 if(!animation->armature)
295 throw invalid_operation("Animation::Iterator::get_pose_matrix");
296 if(link>animation->armature->get_max_link_index())
297 throw out_of_range("Animation::Iterator::get_pose_matrix");
301 if(const Pose *pose = iter->keyframe->get_pose())
302 return pose->get_link_matrix(link);
307 // We must redo the base point correction since interpolation throws if off
308 // XXX This should probably be done on local matrices
309 Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
310 const Vector3 &base = animation->armature->get_link(link).get_base();
311 Vector3 new_base = result*base;
312 result = Matrix::translation(base-new_base)*result;
317 Animation::Loader::Loader(Animation &a):
318 DataFile::CollectionObjectLoader<Animation>(a, 0)
323 Animation::Loader::Loader(Animation &a, Collection &c):
324 DataFile::CollectionObjectLoader<Animation>(a, &c)
329 void Animation::Loader::init()
331 add("armature", &Animation::armature);
332 add("interval", &Loader::interval);
333 add("keyframe", &Loader::keyframe);
334 add("keyframe", &Loader::keyframe_inline);
335 add("looping", &Animation::looping);
338 void Animation::Loader::keyframe(const string &n)
340 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
343 void Animation::Loader::keyframe_inline()
345 RefPtr<KeyFrame> kf = new KeyFrame;
347 load_sub(*kf, get_collection());
351 obj.add_keyframe(current_time, kf);
354 void Animation::Loader::interval(float t)
356 current_time += t*Time::sec;