]> git.tdb.fi Git - libs/gl.git/blob - source/animation.cpp
Refactor AnimationPlayer ticking
[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(*this));
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();
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(const Animation &a):
189         animation(a),
190         prev(0)
191 { }
192
193 void Animation::TimedKeyFrame::prepare()
194 {
195         const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
196         for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
197         {
198                 unsigned j = animation.get_slot_for_uniform(i->first);
199                 uniforms.reserve(j+1);
200                 for(unsigned k=uniforms.size(); k<=j; ++k)
201                         uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
202
203                 uniforms[j] = i->second;
204         }
205
206         if(!prev)
207                 return;
208
209         delta_t = time-prev->time;
210         matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
211
212         if(animation.armature)
213         {
214                 unsigned max_index = animation.armature->get_max_link_index();
215                 pose_matrices.resize(max_index+1);
216                 const Pose *pose1 = prev->keyframe->get_pose();
217                 const Pose *pose2 = keyframe->get_pose();
218                 static Matrix identity;
219                 for(unsigned i=0; i<=max_index; ++i)
220                 {
221                         const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
222                         const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
223                         pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
224                 }
225         }
226 }
227
228
229 Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
230         name(n),
231         size(s)
232 { }
233
234
235 Animation::Iterator::Iterator(const Animation &a):
236         animation(&a),
237         iter(animation->keyframes.begin()),
238         end(false)
239 { }
240
241 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
242 {
243         time_since_keyframe += t;
244         while(time_since_keyframe>iter->delta_t)
245         {
246                 vector<TimedKeyFrame>::const_iterator next = iter;
247                 ++next;
248                 if(next==animation->keyframes.end())
249                 {
250                         if(animation->looping)
251                                 next = animation->keyframes.begin();
252                         else
253                         {
254                                 end = true;
255                                 time_since_keyframe = iter->delta_t;
256                                 break;
257                         }
258                 }
259
260                 time_since_keyframe -= iter->delta_t;
261                 iter = next;
262         }
263
264         return *this;
265 }
266
267 Matrix Animation::Iterator::get_matrix() const
268 {
269         if(!iter->prev)
270                 return iter->keyframe->get_matrix();
271
272         return iter->matrix.get(time_since_keyframe/iter->delta_t);
273 }
274
275 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
276 {
277         if(!iter->prev)
278         {
279                 if(iter->uniforms.size()>i)
280                         return iter->uniforms[i];
281                 else
282                         return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
283         }
284
285         unsigned size = animation->uniforms[i].size;
286         float t = time_since_keyframe/iter->delta_t;
287         KeyFrame::AnimatedUniform result(size, 0.0f);
288         for(unsigned j=0; j<size; ++j)
289                 result.values[j] = iter->prev->uniforms[i].values[j]*(1-t)+iter->uniforms[i].values[j]*t;
290         return result;
291 }
292
293 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
294 {
295         if(!animation->armature)
296                 throw invalid_operation("Animation::Iterator::get_pose_matrix");
297         if(link>animation->armature->get_max_link_index())
298                 throw out_of_range("Animation::Iterator::get_pose_matrix");
299
300         if(!iter->prev)
301         {
302                 if(const Pose *pose = iter->keyframe->get_pose())
303                         return pose->get_link_matrix(link);
304                 else
305                         return Matrix();
306         }
307
308         // We must redo the base point correction since interpolation throws if off
309         // XXX This should probably be done on local matrices
310         Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
311         const Vector3 &base = animation->armature->get_link(link).get_base();
312         Vector3 new_base = result*base;
313         result = Matrix::translation(base-new_base)*result;
314         return result;
315 }
316
317
318 Animation::Loader::Loader(Animation &a):
319         DataFile::CollectionObjectLoader<Animation>(a, 0)
320 {
321         init();
322 }
323
324 Animation::Loader::Loader(Animation &a, Collection &c):
325         DataFile::CollectionObjectLoader<Animation>(a, &c)
326 {
327         init();
328 }
329
330 void Animation::Loader::init()
331 {
332         add("armature", &Animation::armature);
333         add("interval", &Loader::interval);
334         add("keyframe", &Loader::keyframe);
335         add("keyframe", &Loader::keyframe_inline);
336         add("looping", &Animation::looping);
337 }
338
339 void Animation::Loader::keyframe(const string &n)
340 {
341         obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
342 }
343
344 void Animation::Loader::keyframe_inline()
345 {
346         RefPtr<KeyFrame> kf = new KeyFrame;
347         if(coll)
348                 load_sub(*kf, get_collection());
349         else
350                 load_sub(*kf);
351
352         obj.add_keyframe(current_time, kf);
353 }
354
355 void Animation::Loader::interval(float t)
356 {
357         current_time += t*Time::sec;
358 }
359
360 } // namespace GL
361 } // namespace Msp