]> git.tdb.fi Git - libs/gl.git/blob - source/animation.cpp
c0b508180b0da9f8676cd12e6f5c74a4a77cede1
[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         // XXX This should probably be done on local matrices
227         Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
228         const Vector3 &base = animation.armature->get_link(link).get_base();
229         Vector3 new_base = result*base;
230         result = Matrix::translation(base-new_base)*result;
231         return result;
232 }
233
234
235 Animation::Loader::Loader(Animation &a):
236         DataFile::CollectionObjectLoader<Animation>(a, 0)
237 {
238         init();
239 }
240
241 Animation::Loader::Loader(Animation &a, Collection &c):
242         DataFile::CollectionObjectLoader<Animation>(a, &c)
243 {
244         init();
245 }
246
247 void Animation::Loader::init()
248 {
249         add("armature", &Animation::armature);
250         add("interval", &Loader::interval);
251         add("keyframe", &Loader::keyframe);
252         add("keyframe", &Loader::keyframe_inline);
253         add("looping", &Animation::looping);
254 }
255
256 void Animation::Loader::keyframe(const string &n)
257 {
258         obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
259 }
260
261 void Animation::Loader::keyframe_inline()
262 {
263         RefPtr<KeyFrame> kf = new KeyFrame;
264         if(coll)
265                 load_sub(*kf, get_collection());
266         else
267                 load_sub(*kf);
268
269         TimedKeyFrame tkf(obj);
270         tkf.time = current_time;
271         tkf.keyframe = kf;
272         obj.prepare_keyframe(tkf);
273         obj.keyframes.push_back(tkf);
274 }
275
276 void Animation::Loader::interval(float t)
277 {
278         current_time += t*Time::sec;
279 }
280
281 } // namespace GL
282 } // namespace Msp