]> git.tdb.fi Git - libs/gl.git/blob - source/animation.cpp
Exception fixes
[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 "error.h"
7 #include "keyframe.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 void Animation::set_armature(const Armature &a)
21 {
22         armature = &a;
23 }
24
25 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
26 {
27         if(!keyframes.empty() && t<keyframes.back().time)
28                 throw invalid_argument("Animation::add_keyframe");
29
30         TimedKeyFrame tkf(*this);
31         tkf.time = t;
32         tkf.keyframe = &kf;
33         tkf.keyframe.keep();
34         prepare_keyframe(tkf);
35         keyframes.push_back(tkf);
36 }
37
38 void Animation::set_looping(bool l)
39 {
40         looping = l;
41 }
42
43 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
44 {
45         tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
46         if(!tkf.prev)
47                 return;
48
49         tkf.prepare();
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                 static 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 invalid_operation("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         if(!iter->prev)
215         {
216                 if(const Pose *pose = iter->keyframe->get_pose())
217                         return pose->get_link_matrix(link);
218                 else
219                         return Matrix();
220         }
221
222         // We must redo the base point correction since interpolation throws if off
223         Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
224         const Vector3 &base = animation.armature->get_link(link).get_base();
225         Vector3 new_base = result*base;
226         result = Matrix::translation(base.x-new_base.x, base.y-new_base.y, base.z-new_base.z)*result;
227         return result;
228 }
229
230
231 Animation::Loader::Loader(Animation &a):
232         DataFile::CollectionObjectLoader<Animation>(a, 0)
233 {
234         init();
235 }
236
237 Animation::Loader::Loader(Animation &a, Collection &c):
238         DataFile::CollectionObjectLoader<Animation>(a, &c)
239 {
240         init();
241 }
242
243 void Animation::Loader::init()
244 {
245         add("armature", &Animation::armature);
246         add("interval", &Loader::interval);
247         add("keyframe", &Loader::keyframe);
248         add("keyframe", &Loader::keyframe_inline);
249         add("looping", &Animation::looping);
250 }
251
252 void Animation::Loader::keyframe(const string &n)
253 {
254         obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
255 }
256
257 void Animation::Loader::keyframe_inline()
258 {
259         RefPtr<KeyFrame> kf = new KeyFrame;
260         if(coll)
261                 load_sub(*kf, get_collection());
262         else
263                 load_sub(*kf);
264
265         TimedKeyFrame tkf(obj);
266         tkf.time = current_time;
267         tkf.keyframe = kf;
268         obj.prepare_keyframe(tkf);
269         obj.keyframes.push_back(tkf);
270 }
271
272 void Animation::Loader::interval(float t)
273 {
274         current_time += t*Time::sec;
275 }
276
277 } // namespace GL
278 } // namespace Msp