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