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