2 #include <msp/datafile/collection.h>
3 #include <msp/time/units.h>
9 #include <msp/io/print.h>
14 Animation::Animation():
18 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
20 if(!keyframes.empty() && t<keyframes.back().time)
21 throw invalid_argument("Animation::add_keyframe");
27 prepare_keyframe(tkf);
28 keyframes.push_back(tkf);
31 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
33 tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
37 tkf.delta_t = tkf.time-tkf.prev->time;
39 const double *m1_data = tkf.prev->keyframe->get_matrix().data();
40 const double *m2_data = tkf.keyframe->get_matrix().data();
41 for(unsigned i=0; i<3; ++i)
43 const double *m1_col = m1_data+i*4;
44 const double *m2_col = m2_data+i*4;
46 // Compute a normalized vector halfway between the two endpoints
49 for(unsigned j=0; j<3; ++j)
51 half[j] = (m1_col[j]+m2_col[j])/2;
52 len += half[j]*half[j];
55 for(unsigned j=0; j<3; ++j)
58 // Compute correction factors for smooth interpolation
59 double cos_half = m1_col[0]*half[0]+m1_col[1]*half[1]+m1_col[2]*half[2];
60 double angle = acos(cos_half);
61 tkf.axes[i].slope = (angle ? angle/tan(angle) : 1);
62 tkf.axes[i].scale = cos_half;
66 Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta &dt) const
69 return tkf.keyframe->get_matrix();
71 throw invalid_argument("Animation::compute_matrix");
72 const TimedKeyFrame &prev = *tkf.prev;
74 float t = dt/tkf.delta_t;
75 float u = t*2.0f-1.0f;
78 const double *m1_data = prev.keyframe->get_matrix().data();
79 const double *m2_data = tkf.keyframe->get_matrix().data();
80 for(unsigned i=0; i<4; ++i)
82 const double *m1_col = m1_data+i*4;
83 const double *m2_col = m2_data+i*4;
84 double *out_col = matrix+i*4;
88 /* Linear interpolation will produce vectors that fall on the line
89 between the two endpoints, and has a higher angular velocity near the
90 middle. We compensate for the velocity by interpolating the angle
91 around the halfway point and computing its tangent. This is
92 approximated by a third degree polynomial, scaled so that the result
93 will be in the range [-1, 1]. */
94 float w = (tkf.axes[i].slope+(1-tkf.axes[i].slope)*u*u)*u*0.5f+0.5f;
96 /* The interpolate vectors will also be shorter than unit length. At
97 the halfway point the length will be equal to the cosine of half the
98 angle, which was computed earlier. Use a second degree polynomial to
100 float n = (tkf.axes[i].scale+(1-tkf.axes[i].scale)*u*u);
102 for(unsigned j=0; j<3; ++j)
103 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
107 for(unsigned j=0; j<3; ++j)
108 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
121 Animation::AxisInterpolation::AxisInterpolation():
127 Animation::Iterator::Iterator(const Animation &a):
129 iter(animation.keyframes.begin()),
133 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
135 time_since_keyframe += t;
136 while(time_since_keyframe>iter->delta_t)
138 KeyFrameList::const_iterator next = iter;
140 if(next==animation.keyframes.end())
142 if(animation.looping)
143 next = animation.keyframes.begin();
147 time_since_keyframe = iter->delta_t;
152 time_since_keyframe -= iter->delta_t;
159 Matrix Animation::Iterator::get_matrix() const
161 return animation.compute_matrix(*iter, time_since_keyframe);
165 Animation::Loader::Loader(Animation &a):
166 DataFile::CollectionObjectLoader<Animation>(a, 0)
171 Animation::Loader::Loader(Animation &a, Collection &c):
172 DataFile::CollectionObjectLoader<Animation>(a, &c)
177 void Animation::Loader::init()
179 add("interval", &Loader::interval);
180 add("keyframe", &Loader::keyframe);
181 add("keyframe", &Loader::keyframe_inline);
182 add("looping", &Animation::looping);
185 void Animation::Loader::keyframe(const string &n)
187 obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
190 void Animation::Loader::keyframe_inline()
192 RefPtr<KeyFrame> kf = new KeyFrame;
196 tkf.time = current_time;
198 obj.prepare_keyframe(tkf);
199 obj.keyframes.push_back(tkf);
202 void Animation::Loader::interval(float t)
204 current_time += t*Time::sec;