]> git.tdb.fi Git - libs/gl.git/blob - source/animation.cpp
Support slopes in keyframe interpolation
[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 "animationeventobserver.h"
7 #include "armature.h"
8 #include "error.h"
9 #include "pose.h"
10
11 using namespace std;
12
13 namespace Msp {
14 namespace GL {
15
16 Animation::Animation():
17         armature(0),
18         looping(false)
19 { }
20
21 // Avoid synthesizing ~RefPtr in files including animation.h
22 Animation::~Animation()
23 { }
24
25 void Animation::set_armature(const Armature &a)
26 {
27         armature = &a;
28 }
29
30 unsigned Animation::get_slot_for_uniform(const string &n) const
31 {
32         for(unsigned i=0; i<uniforms.size(); ++i)
33                 if(uniforms[i].name==n)
34                         return i;
35         throw key_error(n);
36 }
37
38 const string &Animation::get_uniform_name(unsigned i) const
39 {
40         if(i>=uniforms.size())
41                 throw out_of_range("Animation::get_uniform_name");
42         return uniforms[i].name;
43 }
44
45 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
46 {
47         add_keyframe(t, kf, 1.0f, 1.0f);
48 }
49
50 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float slope)
51 {
52         add_keyframe(t, kf, slope, slope);
53 }
54
55 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float ss, float es)
56 {
57         RefPtr<const KeyFrame> kfr(&kf);
58         kfr.keep();
59         add_keyframe(t, kfr, ss, es);
60 }
61
62 void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf, float ss, float es)
63 {
64         if(!keyframes.empty() && t<keyframes.back().time)
65                 throw invalid_argument("Animation::add_keyframe");
66
67         bool realloc = (keyframes.size()>=keyframes.capacity());
68
69         keyframes.push_back(TimedKeyFrame());
70         TimedKeyFrame &tkf = keyframes.back();
71         tkf.time = t;
72         tkf.start_slope = ss;
73         tkf.end_slope = es;
74         tkf.keyframe = kf;
75
76         if(realloc)
77         {
78                 for(unsigned i=1; i<keyframes.size(); ++i)
79                         keyframes[i].prev = &keyframes[i-1];
80         }
81         else if(keyframes.size()>1)
82                 tkf.prev = &tkf-1;
83
84         prepare_keyframe(tkf);
85 }
86
87 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
88 {
89         const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
90         for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
91         {
92                 bool found = false;
93                 for(unsigned j=0; (!found && j<uniforms.size()); ++j)
94                         if(uniforms[j].name==i->first)
95                         {
96                                 if(uniforms[j].size!=i->second.size)
97                                         throw invalid_operation("Animation::prepare_keyframe");
98                                 found = true;
99                         }
100
101                 if(!found)
102                         uniforms.push_back(UniformInfo(i->first, i->second.size));
103         }
104
105         tkf.prepare(*this);
106 }
107
108 void Animation::add_event(const Time::TimeDelta &t, const string &n, const Variant &v)
109 {
110         Event event;
111         event.time = t;
112         event.name = n;
113         event.value = v;
114         events.push_back(event);
115 }
116
117 void Animation::set_looping(bool l)
118 {
119         looping = l;
120 }
121
122
123 Animation::AxisInterpolation::AxisInterpolation():
124         slope(0),
125         scale(0)
126 { }
127
128 Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
129 {
130         // Compute a normalized vector halfway between the two endpoints
131         float a1_len = 0;
132         float h_len = 0;
133         float cos_half = 0;
134         for(unsigned i=0; i<3; ++i)
135         {
136                 float half_i = (axis1[i]+axis2[i])/2;
137                 cos_half += axis1[i]*half_i;
138                 a1_len += axis1[i]*axis1[i];
139                 h_len += half_i*half_i;
140         }
141
142         // Compute correction factors for smooth interpolation
143         cos_half = min(max(cos_half/sqrt(a1_len*h_len), -1.0f), 1.0f);
144         float angle = acos(cos_half);
145         slope = (angle ? angle/tan(angle) : 1);
146         scale = cos_half;
147 }
148
149
150 Animation::MatrixInterpolation::MatrixInterpolation():
151         matrix1(0),
152         matrix2(0)
153 { }
154
155 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
156         matrix1(&m1),
157         matrix2(&m2)
158 {
159         const float *m1_data = matrix1->data();
160         const float *m2_data = matrix2->data();
161         for(unsigned i=0; i<3; ++i)
162                 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
163 }
164
165 Matrix Animation::MatrixInterpolation::get(float t) const
166 {
167         float u = t*2.0f-1.0f;
168
169         float matrix[16];
170         for(unsigned i=0; i<4; ++i)
171         {
172                 const float *m1_col = matrix1->data()+i*4;
173                 const float *m2_col = matrix2->data()+i*4;
174                 float *out_col = matrix+i*4;
175
176                 if(i<3)
177                 {
178                         /* Linear interpolation will produce vectors that fall on the line
179                         between the two endpoints, and has a higher angular velocity near the
180                         middle.  We compensate for the velocity by interpolating the angle
181                         around the halfway point and computing its tangent.  This is
182                         approximated by a third degree polynomial, scaled so that the result
183                         will be in the range [-1, 1]. */
184                         float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
185
186                         /* The interpolated vectors will also be shorter than unit length.  At
187                         the halfway point the length will be equal to the cosine of half the
188                         angle, which was computed earlier.  Use a second degree polynomial to
189                         approximate. */
190                         float n = (axes[i].scale+(1-axes[i].scale)*u*u);
191
192                         for(unsigned j=0; j<3; ++j)
193                                 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
194                 }
195                 else
196                 {
197                         for(unsigned j=0; j<3; ++j)
198                                 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
199                 }
200         }
201
202         matrix[3] = 0;
203         matrix[7] = 0;
204         matrix[11] = 0;
205         matrix[15] = 1;
206
207         return matrix;
208 }
209
210
211 Animation::TimedKeyFrame::TimedKeyFrame():
212         prev(0),
213         start_slope(1),
214         end_slope(1)
215 { }
216
217 void Animation::TimedKeyFrame::prepare(const Animation &animation)
218 {
219         const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
220         for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
221         {
222                 unsigned j = animation.get_slot_for_uniform(i->first);
223                 uniforms.reserve(j+1);
224                 for(unsigned k=uniforms.size(); k<=j; ++k)
225                         uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
226
227                 uniforms[j] = i->second;
228         }
229
230         if(!prev)
231                 return;
232
233         delta_t = time-prev->time;
234         matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
235
236         if(animation.armature)
237         {
238                 unsigned max_index = animation.armature->get_max_link_index();
239                 pose_matrices.resize(max_index+1);
240                 const Pose *pose1 = prev->keyframe->get_pose();
241                 const Pose *pose2 = keyframe->get_pose();
242                 static Matrix identity;
243                 for(unsigned i=0; i<=max_index; ++i)
244                 {
245                         const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
246                         const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
247                         pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
248                 }
249         }
250 }
251
252
253 Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
254         name(n),
255         size(s)
256 { }
257
258
259 Animation::Iterator::Iterator(const Animation &a):
260         animation(&a),
261         iter(animation->keyframes.begin()),
262         event_iter(animation->events.begin()),
263         x(0),
264         end(false)
265 { }
266
267 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
268 {
269         time_since_keyframe += t;
270         while(time_since_keyframe>iter->delta_t)
271         {
272                 vector<TimedKeyFrame>::const_iterator next = iter;
273                 ++next;
274                 if(next==animation->keyframes.end())
275                 {
276                         if(animation->looping)
277                                 next = animation->keyframes.begin();
278                         else
279                         {
280                                 end = true;
281                                 time_since_keyframe = iter->delta_t;
282                                 break;
283                         }
284                 }
285
286                 time_since_keyframe -= iter->delta_t;
287                 iter = next;
288         }
289
290         x = time_since_keyframe/iter->delta_t;
291         x += (iter->start_slope-1)*((x-2)*x+1)*x + (1-iter->end_slope)*(1-x)*x*x;
292
293         return *this;
294 }
295
296 void Animation::Iterator::dispatch_events(AnimationEventObserver &observer)
297 {
298         vector<Event>::const_iterator events_end = animation->events.end();
299         if(end)
300         {
301                 for(; event_iter!=events_end; ++event_iter)
302                         observer.animation_event(0, event_iter->name, event_iter->value);
303         }
304         else if(event_iter!=events_end)
305         {
306                 Time::TimeDelta t = time_since_keyframe;
307                 if(iter->prev)
308                         t += iter->prev->time;
309                 for(; (event_iter!=events_end && event_iter->time<=t); ++event_iter)
310                         observer.animation_event(0, event_iter->name, event_iter->value);
311         }
312 }
313
314 Matrix Animation::Iterator::get_matrix() const
315 {
316         if(!iter->prev)
317                 return iter->keyframe->get_matrix();
318
319         return iter->matrix.get(x);
320 }
321
322 KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
323 {
324         if(!iter->prev)
325         {
326                 if(iter->uniforms.size()>i)
327                         return iter->uniforms[i];
328                 else
329                         return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
330         }
331
332         unsigned size = animation->uniforms[i].size;
333         KeyFrame::AnimatedUniform result(size, 0.0f);
334         for(unsigned j=0; j<size; ++j)
335                 result.values[j] = iter->prev->uniforms[i].values[j]*(1-x)+iter->uniforms[i].values[j]*x;
336         return result;
337 }
338
339 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
340 {
341         if(!animation->armature)
342                 throw invalid_operation("Animation::Iterator::get_pose_matrix");
343         if(link>animation->armature->get_max_link_index())
344                 throw out_of_range("Animation::Iterator::get_pose_matrix");
345
346         if(!iter->prev)
347         {
348                 if(const Pose *pose = iter->keyframe->get_pose())
349                         return pose->get_link_matrix(link);
350                 else
351                         return Matrix();
352         }
353
354         // We must redo the base point correction since interpolation throws it off
355         // XXX This should probably be done on local matrices
356         Matrix result = iter->pose_matrices[link].get(x);
357         const Vector3 &base = animation->armature->get_link(link).get_base();
358         Vector3 new_base = result*base;
359         result = Matrix::translation(base-new_base)*result;
360         return result;
361 }
362
363
364 Animation::Loader::Loader(Animation &a):
365         DataFile::CollectionObjectLoader<Animation>(a, 0)
366 {
367         init();
368 }
369
370 Animation::Loader::Loader(Animation &a, Collection &c):
371         DataFile::CollectionObjectLoader<Animation>(a, &c)
372 {
373         init();
374 }
375
376 void Animation::Loader::init()
377 {
378         start_slope = 1;
379         end_slope = 1;
380         add("armature", &Animation::armature);
381         add("event", &Loader::event);
382         add("event", &Loader::event1i);
383         add("event", &Loader::event1f);
384         add("event", &Loader::event2f);
385         add("event", &Loader::event3f);
386         add("event", &Loader::event4f);
387         add("interval", &Loader::interval);
388         add("keyframe", &Loader::keyframe);
389         add("keyframe", &Loader::keyframe_inline);
390         add("looping", &Animation::looping);
391         add("slopes", &Loader::slopes);
392 }
393
394 void Animation::Loader::event(const string &n)
395 {
396         obj.add_event(current_time, n);
397 }
398
399 void Animation::Loader::event1i(const string &n, int v)
400 {
401         obj.add_event(current_time, n, v);
402 }
403
404 void Animation::Loader::event1f(const string &n, float v)
405 {
406         obj.add_event(current_time, n, v);
407 }
408
409 void Animation::Loader::event2f(const string &n, float v0, float v1)
410 {
411         obj.add_event(current_time, n, LinAl::Vector<float, 2>(v0, v1));
412 }
413
414 void Animation::Loader::event3f(const string &n, float v0, float v1, float v2)
415 {
416         obj.add_event(current_time, n, Vector3(v0, v1, v2));
417 }
418
419 void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3)
420 {
421         obj.add_event(current_time, n, Vector4(v0, v1, v2, v3));
422 }
423
424 void Animation::Loader::interval(float t)
425 {
426         current_time += t*Time::sec;
427 }
428
429 void Animation::Loader::keyframe(const string &n)
430 {
431         obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n), start_slope, end_slope);
432         start_slope = end_slope;
433         end_slope = 1;
434 }
435
436 void Animation::Loader::keyframe_inline()
437 {
438         RefPtr<KeyFrame> kf = new KeyFrame;
439         if(coll)
440                 load_sub(*kf, get_collection());
441         else
442                 load_sub(*kf);
443
444         obj.add_keyframe(current_time, kf, start_slope, end_slope);
445         start_slope = end_slope;
446         end_slope = 1;
447 }
448
449 void Animation::Loader::slopes(float s, float e)
450 {
451         start_slope = s;
452         end_slope = e;
453 }
454
455 } // namespace GL
456 } // namespace Msp