2 #include <msp/core/maputils.h>
3 #include <msp/datafile/collection.h>
5 #include "animationeventobserver.h"
15 Animation::Animation():
20 // Avoid synthesizing ~RefPtr in files including animation.h
21 Animation::~Animation()
24 void Animation::set_armature(const Armature &a)
26 if(!keyframes.empty() && &a!=armature)
27 throw invalid_operation("Animation::set_armature");
31 unsigned Animation::get_slot_for_uniform(const string &n) const
33 for(unsigned i=0; i<uniforms.size(); ++i)
34 if(uniforms[i].name==n)
39 const string &Animation::get_uniform_name(unsigned i) const
41 if(i>=uniforms.size())
42 throw out_of_range("Animation::get_uniform_name");
43 return uniforms[i].name;
46 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
48 add_keyframe(t, kf, 1.0f, 1.0f);
51 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float slope)
53 add_keyframe(t, kf, slope, slope);
56 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float ss, float es)
58 RefPtr<const KeyFrame> kfr(&kf);
60 add_keyframe(t, kfr, ss, es);
63 void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf, float ss, float es)
65 if(keyframes.empty() && t!=Time::zero)
66 throw invalid_argument("Animation::add_keyframe");
67 if(!keyframes.empty() && t<keyframes.back().time)
68 throw invalid_argument("Animation::add_keyframe");
69 if(kf->get_pose() && armature && kf->get_pose()->get_armature()!=armature)
70 throw invalid_argument("Animation::add_keyframe");
72 bool realloc = (keyframes.size()>=keyframes.capacity());
74 if(kf->get_pose() && !armature)
75 armature = kf->get_pose()->get_armature();
77 keyframes.push_back(TimedKeyFrame());
78 TimedKeyFrame &tkf = keyframes.back();
86 for(unsigned i=1; i<keyframes.size(); ++i)
88 keyframes[i].prev = &keyframes[i-1];
90 if(keyframes.size()>1 && t>(&tkf-1)->time)
93 prepare_keyframe(tkf);
96 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
98 const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
99 for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
102 for(unsigned j=0; (!found && j<uniforms.size()); ++j)
103 if(uniforms[j].name==i->first)
105 if(uniforms[j].size!=i->second.size)
106 throw invalid_operation("Animation::prepare_keyframe");
111 uniforms.push_back(UniformInfo(i->first, i->second.size));
117 void Animation::add_event(const Time::TimeDelta &t, const string &n, const Variant &v)
123 events.push_back(event);
126 const Time::TimeDelta &Animation::get_duration() const
128 if(keyframes.empty())
131 return keyframes.back().time;
134 void Animation::set_looping(bool l)
140 Animation::AxisInterpolation::AxisInterpolation():
145 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
147 // Compute a normalized vector halfway between the two endpoints
151 for(unsigned i=0; i<3; ++i)
153 float half_i = (axis1[i]+axis2[i])/2;
154 cos_half += axis1[i]*half_i;
155 a1_len += axis1[i]*axis1[i];
156 h_len += half_i*half_i;
159 // Compute correction factors for smooth interpolation
160 cos_half = min(max(cos_half/sqrt(a1_len*h_len), -1.0f), 1.0f);
161 float angle = acos(cos_half);
162 slope = (angle ? angle/tan(angle) : 1);
167 Animation::MatrixInterpolation::MatrixInterpolation():
172 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
176 const float *m1_data = matrix1->data();
177 const float *m2_data = matrix2->data();
178 for(unsigned i=0; i<3; ++i)
179 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
182 Matrix Animation::MatrixInterpolation::get(float t) const
184 float u = t*2.0f-1.0f;
187 for(unsigned i=0; i<4; ++i)
189 const float *m1_col = matrix1->data()+i*4;
190 const float *m2_col = matrix2->data()+i*4;
191 float *out_col = matrix+i*4;
195 /* Linear interpolation will produce vectors that fall on the line
196 between the two endpoints, and has a higher angular velocity near the
197 middle. We compensate for the velocity by interpolating the angle
198 around the halfway point and computing its tangent. This is
199 approximated by a third degree polynomial, scaled so that the result
200 will be in the range [-1, 1]. */
201 float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
203 /* The interpolated vectors will also be shorter than unit length. At
204 the halfway point the length will be equal to the cosine of half the
205 angle, which was computed earlier. Use a second degree polynomial to
207 float n = (axes[i].scale+(1-axes[i].scale)*u*u);
209 for(unsigned j=0; j<3; ++j)
210 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
214 for(unsigned j=0; j<3; ++j)
215 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
228 Animation::TimedKeyFrame::TimedKeyFrame():
234 void Animation::TimedKeyFrame::prepare(const Animation &animation)
236 const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
237 for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
239 unsigned j = animation.get_slot_for_uniform(i->first);
240 uniforms.reserve(j+1);
241 for(unsigned k=uniforms.size(); k<=j; ++k)
242 uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
244 uniforms[j] = i->second;
250 delta_t = time-prev->time;
251 matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
253 if(animation.armature)
255 unsigned max_index = animation.armature->get_max_link_index();
256 pose_matrices.resize(max_index+1);
257 const Pose *pose1 = prev->keyframe->get_pose();
258 const Pose *pose2 = keyframe->get_pose();
259 static Matrix identity;
260 for(unsigned i=0; i<=max_index; ++i)
262 const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
263 const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
264 pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
270 Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
276 Animation::Iterator::Iterator(const Animation &a):
278 iter(animation->keyframes.begin()),
279 event_iter(animation->events.begin()),
283 if(iter==animation->keyframes.end())
284 throw invalid_argument("Animation::Iterator::Iterator");
287 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
289 time_since_keyframe += t;
290 while(time_since_keyframe>iter->delta_t)
292 vector<TimedKeyFrame>::const_iterator next = iter;
294 if(next==animation->keyframes.end())
296 if(animation->looping)
297 next = animation->keyframes.begin();
301 time_since_keyframe = iter->delta_t;
306 time_since_keyframe -= iter->delta_t;
310 x = time_since_keyframe/iter->delta_t;
311 x += (iter->start_slope-1)*((x-2)*x+1)*x + (1-iter->end_slope)*(1-x)*x*x;
316 void Animation::Iterator::dispatch_events(AnimationEventObserver &observer)
318 vector<Event>::const_iterator events_end = animation->events.end();
321 for(; event_iter!=events_end; ++event_iter)
322 observer.animation_event(0, event_iter->name, event_iter->value);
324 else if(event_iter!=events_end)
326 Time::TimeDelta t = time_since_keyframe;
328 t += iter->prev->time;
329 for(; (event_iter!=events_end && event_iter->time<=t); ++event_iter)
330 observer.animation_event(0, event_iter->name, event_iter->value);
334 Matrix Animation::Iterator::get_matrix() const
337 return iter->keyframe->get_matrix();
339 return iter->matrix.get(x);
342 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
346 if(iter->uniforms.size()>i)
347 return iter->uniforms[i];
349 return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
352 unsigned size = animation->uniforms[i].size;
353 KeyFrame::AnimatedUniform result(size, 0.0f);
354 for(unsigned j=0; j<size; ++j)
355 result.values[j] = iter->prev->uniforms[i].values[j]*(1-x)+iter->uniforms[i].values[j]*x;
359 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
361 if(!animation->armature)
362 throw invalid_operation("Animation::Iterator::get_pose_matrix");
363 if(link>animation->armature->get_max_link_index())
364 throw out_of_range("Animation::Iterator::get_pose_matrix");
368 if(const Pose *pose = iter->keyframe->get_pose())
369 return pose->get_link_matrix(link);
374 // We must redo the base point correction since interpolation throws it off
375 // XXX This should probably be done on local matrices
376 Matrix result = iter->pose_matrices[link].get(x);
377 const Vector3 &base = animation->armature->get_link(link).get_base();
378 Vector3 new_base = result*base;
379 result = Matrix::translation(base-new_base)*result;
384 Animation::Loader::Loader(Animation &a):
385 DataFile::CollectionObjectLoader<Animation>(a, 0)
390 Animation::Loader::Loader(Animation &a, Collection &c):
391 DataFile::CollectionObjectLoader<Animation>(a, &c)
396 void Animation::Loader::init()
400 add("armature", &Animation::armature);
401 add("event", &Loader::event);
402 add("event", &Loader::event1i);
403 add("event", &Loader::event1f);
404 add("event", &Loader::event2f);
405 add("event", &Loader::event3f);
406 add("event", &Loader::event4f);
407 add("interval", &Loader::interval);
408 add("keyframe", &Loader::keyframe);
409 add("keyframe", &Loader::keyframe_inline);
410 add("looping", &Animation::looping);
411 add("slopes", &Loader::slopes);
414 void Animation::Loader::event(const string &n)
416 obj.add_event(current_time, n);
419 void Animation::Loader::event1i(const string &n, int v)
421 obj.add_event(current_time, n, v);
424 void Animation::Loader::event1f(const string &n, float v)
426 obj.add_event(current_time, n, v);
429 void Animation::Loader::event2f(const string &n, float v0, float v1)
431 obj.add_event(current_time, n, LinAl::Vector<float, 2>(v0, v1));
434 void Animation::Loader::event3f(const string &n, float v0, float v1, float v2)
436 obj.add_event(current_time, n, Vector3(v0, v1, v2));
439 void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3)
441 obj.add_event(current_time, n, Vector4(v0, v1, v2, v3));
444 void Animation::Loader::interval(float t)
446 current_time += t*Time::sec;
449 void Animation::Loader::keyframe(const string &n)
451 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n), start_slope, end_slope);
452 start_slope = end_slope;
456 void Animation::Loader::keyframe_inline()
458 RefPtr<KeyFrame> kf = new KeyFrame;
460 load_sub(*kf, get_collection());
464 obj.add_keyframe(current_time, kf, start_slope, end_slope);
465 start_slope = end_slope;
469 void Animation::Loader::slopes(float s, float e)