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