]> git.tdb.fi Git - libs/gl.git/blob - source/animation.cpp
Refactor the Animation reference out of TimedKeyFrame
[libs/gl.git] / source / animation.cpp
1 #include <cmath>
2 #include <msp/core/maputils.h>
3 #include <msp/datafile/collection.h>
4 #include <msp/time/units.h>
5 #include "animation.h"
6 #include "armature.h"
7 #include "error.h"
8 #include "pose.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Animation::Animation():
16         armature(0),
17         looping(false)
18 { }
19
20 // Avoid synthesizing ~RefPtr in files including animation.h
21 Animation::~Animation()
22 { }
23
24 void Animation::set_armature(const Armature &a)
25 {
26         armature = &a;
27 }
28
29 unsigned Animation::get_slot_for_uniform(const string &n) const
30 {
31         for(unsigned i=0; i<uniforms.size(); ++i)
32                 if(uniforms[i].name==n)
33                         return i;
34         throw key_error(n);
35 }
36
37 const string &Animation::get_uniform_name(unsigned i) const
38 {
39         if(i>=uniforms.size())
40                 throw out_of_range("Animation::get_uniform_name");
41         return uniforms[i].name;
42 }
43
44 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
45 {
46         RefPtr<const KeyFrame> kfr(&kf);
47         kfr.keep();
48         add_keyframe(t, kfr);
49 }
50
51 void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf)
52 {
53         if(!keyframes.empty() && t<keyframes.back().time)
54                 throw invalid_argument("Animation::add_keyframe");
55
56         bool realloc = (keyframes.size()>=keyframes.capacity());
57
58         keyframes.push_back(TimedKeyFrame());
59         TimedKeyFrame &tkf = keyframes.back();
60         tkf.time = t;
61         tkf.keyframe = kf;
62
63         if(realloc)
64         {
65                 for(unsigned i=1; i<keyframes.size(); ++i)
66                         keyframes[i].prev = &keyframes[i-1];
67         }
68         else if(keyframes.size()>1)
69                 tkf.prev = &tkf-1;
70
71         prepare_keyframe(tkf);
72 }
73
74 void Animation::set_looping(bool l)
75 {
76         looping = l;
77 }
78
79 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
80 {
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)
83         {
84                 bool found = false;
85                 for(unsigned j=0; (!found && j<uniforms.size()); ++j)
86                         if(uniforms[j].name==i->first)
87                         {
88                                 if(uniforms[j].size!=i->second.size)
89                                         throw invalid_operation("Animation::prepare_keyframe");
90                                 found = true;
91                         }
92
93                 if(!found)
94                         uniforms.push_back(UniformInfo(i->first, i->second.size));
95         }
96
97         tkf.prepare(*this);
98 }
99
100
101 Animation::AxisInterpolation::AxisInterpolation():
102         slope(0),
103         scale(0)
104 { }
105
106 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
107 {
108         // Compute a normalized vector halfway between the two endpoints
109         float half[3];
110         float a1_len = 0;
111         float h_len = 0;
112         for(unsigned i=0; i<3; ++i)
113         {
114                 half[i] = (axis1[i]+axis2[i])/2;
115                 a1_len += axis1[i]*axis1[i];
116                 h_len += half[i]*half[i];
117         }
118
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);
123         scale = cos_half;
124 }
125
126
127 Animation::MatrixInterpolation::MatrixInterpolation():
128         matrix1(0),
129         matrix2(0)
130 { }
131
132 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
133         matrix1(&m1),
134         matrix2(&m2)
135 {
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);
140 }
141
142 Matrix Animation::MatrixInterpolation::get(float t) const
143 {
144         float u = t*2.0f-1.0f;
145
146         float matrix[16];
147         for(unsigned i=0; i<4; ++i)
148         {
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;
152
153                 if(i<3)
154                 {
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;
162
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
166                         approximate. */
167                         float n = (axes[i].scale+(1-axes[i].scale)*u*u);
168
169                         for(unsigned j=0; j<3; ++j)
170                                 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
171                 }
172                 else
173                 {
174                         for(unsigned j=0; j<3; ++j)
175                                 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
176                 }
177         }
178
179         matrix[3] = 0;
180         matrix[7] = 0;
181         matrix[11] = 0;
182         matrix[15] = 1;
183
184         return matrix;
185 }
186
187
188 Animation::TimedKeyFrame::TimedKeyFrame():
189         prev(0)
190 { }
191
192 void Animation::TimedKeyFrame::prepare(const Animation &animation)
193 {
194         const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
195         for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
196         {
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));
201
202                 uniforms[j] = i->second;
203         }
204
205         if(!prev)
206                 return;
207
208         delta_t = time-prev->time;
209         matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
210
211         if(animation.armature)
212         {
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)
219                 {
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);
223                 }
224         }
225 }
226
227
228 Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
229         name(n),
230         size(s)
231 { }
232
233
234 Animation::Iterator::Iterator(const Animation &a):
235         animation(&a),
236         iter(animation->keyframes.begin()),
237         end(false)
238 { }
239
240 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
241 {
242         time_since_keyframe += t;
243         while(time_since_keyframe>iter->delta_t)
244         {
245                 vector<TimedKeyFrame>::const_iterator next = iter;
246                 ++next;
247                 if(next==animation->keyframes.end())
248                 {
249                         if(animation->looping)
250                                 next = animation->keyframes.begin();
251                         else
252                         {
253                                 end = true;
254                                 time_since_keyframe = iter->delta_t;
255                                 break;
256                         }
257                 }
258
259                 time_since_keyframe -= iter->delta_t;
260                 iter = next;
261         }
262
263         return *this;
264 }
265
266 Matrix Animation::Iterator::get_matrix() const
267 {
268         if(!iter->prev)
269                 return iter->keyframe->get_matrix();
270
271         return iter->matrix.get(time_since_keyframe/iter->delta_t);
272 }
273
274 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
275 {
276         if(!iter->prev)
277         {
278                 if(iter->uniforms.size()>i)
279                         return iter->uniforms[i];
280                 else
281                         return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
282         }
283
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;
289         return result;
290 }
291
292 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
293 {
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");
298
299         if(!iter->prev)
300         {
301                 if(const Pose *pose = iter->keyframe->get_pose())
302                         return pose->get_link_matrix(link);
303                 else
304                         return Matrix();
305         }
306
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;
313         return result;
314 }
315
316
317 Animation::Loader::Loader(Animation &a):
318         DataFile::CollectionObjectLoader<Animation>(a, 0)
319 {
320         init();
321 }
322
323 Animation::Loader::Loader(Animation &a, Collection &c):
324         DataFile::CollectionObjectLoader<Animation>(a, &c)
325 {
326         init();
327 }
328
329 void Animation::Loader::init()
330 {
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);
336 }
337
338 void Animation::Loader::keyframe(const string &n)
339 {
340         obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
341 }
342
343 void Animation::Loader::keyframe_inline()
344 {
345         RefPtr<KeyFrame> kf = new KeyFrame;
346         if(coll)
347                 load_sub(*kf, get_collection());
348         else
349                 load_sub(*kf);
350
351         obj.add_keyframe(current_time, kf);
352 }
353
354 void Animation::Loader::interval(float t)
355 {
356         current_time += t*Time::sec;
357 }
358
359 } // namespace GL
360 } // namespace Msp