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