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