]> git.tdb.fi Git - libs/gl.git/blob - source/animation.cpp
Support for armature-based animation
[libs/gl.git] / source / animation.cpp
1 #include <cmath>
2 #include <msp/datafile/collection.h>
3 #include <msp/time/units.h>
4 #include "animation.h"
5 #include "armature.h"
6 #include "keyframe.h"
7 #include "pose.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace GL {
13
14 Animation::Animation():
15         armature(0),
16         looping(false)
17 { }
18
19 void Animation::set_armature(const Armature &a)
20 {
21         armature = &a;
22 }
23
24 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
25 {
26         if(!keyframes.empty() && t<keyframes.back().time)
27                 throw invalid_argument("Animation::add_keyframe");
28
29         TimedKeyFrame tkf(*this);
30         tkf.time = t;
31         tkf.keyframe = &kf;
32         tkf.keyframe.keep();
33         prepare_keyframe(tkf);
34         keyframes.push_back(tkf);
35 }
36
37 void Animation::set_looping(bool l)
38 {
39         looping = l;
40 }
41
42 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
43 {
44         tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
45         if(!tkf.prev)
46                 return;
47
48         tkf.prepare();
49
50 }
51
52
53 Animation::AxisInterpolation::AxisInterpolation():
54         slope(0),
55         scale(0)
56 { }
57
58 Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
59 {
60         // Compute a normalized vector halfway between the two endpoints
61         double half[3];
62         double len = 0;
63         for(unsigned i=0; i<3; ++i)
64         {
65                 half[i] = (axis1[i]+axis2[i])/2;
66                 len += half[i]*half[i];
67         }
68         len = sqrt(len);
69         for(unsigned i=0; i<3; ++i)
70                 half[i] /= len;
71
72         // Compute correction factors for smooth interpolation
73         double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
74         double angle = acos(cos_half);
75         slope = (angle ? angle/tan(angle) : 1);
76         scale = cos_half;
77 }
78
79
80 Animation::MatrixInterpolation::MatrixInterpolation():
81         matrix1(0),
82         matrix2(0)
83 { }
84
85 Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
86         matrix1(&m1),
87         matrix2(&m2)
88 {
89         const double *m1_data = matrix1->data();
90         const double *m2_data = matrix2->data();
91         for(unsigned i=0; i<3; ++i)
92                 axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
93 }
94
95 Matrix Animation::MatrixInterpolation::get(float t) const
96 {
97         float u = t*2.0f-1.0f;
98
99         double matrix[16];
100         for(unsigned i=0; i<4; ++i)
101         {
102                 const double *m1_col = matrix1->data()+i*4;
103                 const double *m2_col = matrix2->data()+i*4;
104                 double *out_col = matrix+i*4;
105
106                 if(i<3)
107                 {
108                         /* Linear interpolation will produce vectors that fall on the line
109                         between the two endpoints, and has a higher angular velocity near the
110                         middle.  We compensate for the velocity by interpolating the angle
111                         around the halfway point and computing its tangent.  This is
112                         approximated by a third degree polynomial, scaled so that the result
113                         will be in the range [-1, 1]. */
114                         float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
115
116                         /* The interpolate vectors will also be shorter than unit length.  At
117                         the halfway point the length will be equal to the cosine of half the
118                         angle, which was computed earlier.  Use a second degree polynomial to
119                         approximate. */
120                         float n = (axes[i].scale+(1-axes[i].scale)*u*u);
121
122                         for(unsigned j=0; j<3; ++j)
123                                 out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
124                 }
125                 else
126                 {
127                         for(unsigned j=0; j<3; ++j)
128                                 out_col[j] = (1-t)*m1_col[j]+t*m2_col[j];
129                 }
130         }
131
132         matrix[3] = 0;
133         matrix[7] = 0;
134         matrix[11] = 0;
135         matrix[15] = 1;
136
137         return matrix;
138 }
139
140
141 Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
142         animation(a),
143         prev(0)
144 { }
145
146 void Animation::TimedKeyFrame::prepare()
147 {
148         delta_t = time-prev->time;
149         matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
150         if(animation.armature)
151         {
152                 unsigned max_index = animation.armature->get_max_link_index();
153                 pose_matrices.resize(max_index+1);
154                 const Pose *pose1 = prev->keyframe->get_pose();
155                 const Pose *pose2 = keyframe->get_pose();
156                 Matrix identity;
157                 for(unsigned i=0; i<=max_index; ++i)
158                 {
159                         const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
160                         const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
161                         pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
162                 }
163         }
164 }
165
166
167 Animation::Iterator::Iterator(const Animation &a):
168         animation(a),
169         iter(animation.keyframes.begin()),
170         end(false)
171 { }
172
173 Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
174 {
175         time_since_keyframe += t;
176         while(time_since_keyframe>iter->delta_t)
177         {
178                 KeyFrameList::const_iterator next = iter;
179                 ++next;
180                 if(next==animation.keyframes.end())
181                 {
182                         if(animation.looping)
183                                 next = animation.keyframes.begin();
184                         else
185                         {
186                                 end = true;
187                                 time_since_keyframe = iter->delta_t;
188                                 break;
189                         }
190                 }
191
192                 time_since_keyframe -= iter->delta_t;
193                 iter = next;
194         }
195
196         return *this;
197 }
198
199 Matrix Animation::Iterator::get_matrix() const
200 {
201         if(!iter->prev)
202                 return iter->keyframe->get_matrix();
203
204         return iter->matrix.get(time_since_keyframe/iter->delta_t);
205 }
206
207 Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
208 {
209         if(!animation.armature)
210                 throw logic_error("Animation::Iterator::get_pose_matrix");
211         if(link>animation.armature->get_max_link_index())
212                 throw out_of_range("Animation::Iterator::get_pose_matrix");
213
214         return iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
215 }
216
217
218 Animation::Loader::Loader(Animation &a):
219         DataFile::CollectionObjectLoader<Animation>(a, 0)
220 {
221         init();
222 }
223
224 Animation::Loader::Loader(Animation &a, Collection &c):
225         DataFile::CollectionObjectLoader<Animation>(a, &c)
226 {
227         init();
228 }
229
230 void Animation::Loader::init()
231 {
232         add("armature", &Animation::armature);
233         add("interval", &Loader::interval);
234         add("keyframe", &Loader::keyframe);
235         add("keyframe", &Loader::keyframe_inline);
236         add("looping", &Animation::looping);
237 }
238
239 void Animation::Loader::keyframe(const string &n)
240 {
241         obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
242 }
243
244 void Animation::Loader::keyframe_inline()
245 {
246         RefPtr<KeyFrame> kf = new KeyFrame;
247         load_sub(*kf);
248
249         TimedKeyFrame tkf(obj);
250         tkf.time = current_time;
251         tkf.keyframe = kf;
252         obj.prepare_keyframe(tkf);
253         obj.keyframes.push_back(tkf);
254 }
255
256 void Animation::Loader::interval(float t)
257 {
258         current_time += t*Time::sec;
259 }
260
261 } // namespace GL
262 } // namespace Msp