SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLAnimTrack.cpp
Go to the documentation of this file.
1 /**
2  * \file SLAnimTrack.cpp
3  * \date Autumn 2014
4  * \remarks Please use clangformat to format the code. See more code style on
5  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
6  * \authors Marc Wacker, Marcus Hudritsch
7  * \copyright http://opensource.org/licenses/GPL-3.0
8 */
9 
10 #include <SLAnimTrack.h>
11 #include <SLAnimation.h>
12 #include <math/SLCurveBezier.h>
13 #include <SLSceneView.h>
14 
15 //-----------------------------------------------------------------------------
16 /*! Constructor
17  */
19  : _animation(animation)
20 {
21 }
22 
23 //-----------------------------------------------------------------------------
24 /*! Destructor
25  */
27 {
28  for (auto kf : _keyframes)
29  delete kf;
30 }
31 
32 //-----------------------------------------------------------------------------
33 /*! Creates a new keyframed with the passed in timestamp.
34  @note It is required that the keyframes are created in chronological order.
35  since we currently don't sort the keyframe list they have to be sorted
36  before being created.
37 */
39 {
41  _keyframes.push_back(kf);
42  return kf;
43 }
44 
45 //-----------------------------------------------------------------------------
46 /*! Getter for keyframes by index.
47  */
49 {
50  if (index < 0 || index >= numKeyframes())
51  return nullptr;
52 
53  return _keyframes[(SLuint)index];
54 }
55 
56 //-----------------------------------------------------------------------------
57 /*! Get the two keyframes to the left or the right of the passed in timestamp.
58  If keyframes will wrap around, if there is no keyframe after the passed in time
59  then the k2 result will be the first keyframe in the list.
60  If only one keyframe exists the two values will be equivalent.
61 */
63  SLAnimKeyframe** k1,
64  SLAnimKeyframe** k2) const
65 {
66  SLfloat t1, t2;
67  SLuint numKf = (SLuint)_keyframes.size();
68  float animationLength = _animation->lengthSec();
69 
70  assert(animationLength > 0.0f && "Animation length is invalid.");
71 
72  *k1 = *k2 = nullptr;
73 
74  // no keyframes or only one keyframe in animation, early out
75  if (numKf == 0)
76  return 0.0f;
77 
78  if (numKf < 2)
79  {
80  *k1 = *k2 = _keyframes[0];
81  return 0.0f;
82  }
83 
84  // wrap time
85  if (time > animationLength)
86  time = fmod(time, animationLength);
87 
88  // @todo is it really required of us to check if time is < 0.0f here? Or should this be done higher up?
89  while (time < 0.0f)
90  time += animationLength;
91 
92  // search lower bound kf for given time
93  // kf list must be sorted by time at this point
94  // @todo we could use std::lower_bounds here
95  // @todo this could be implemented much nicer
96  // use the following algorithm:
97  // 1. find the keyframe that comes after the 'time' parameter
98  // 2. if there is no keyframe after 'time' then set keframe 2 to the first keyframe in the list
99  // set t2 to animationLength + the time of the keyframe
100  // 3. if there is a keyframe after 'time' then set keyframe 2 to that keyframe
101  // set t2 to the time of the keyframe
102  // 4. now find the keyframe before keyframe 2 (if we use iterators here this is trivial!)
103  // set keyframe 1 to the keyframe found before keyframe 2
104  SLuint kfIndex = 0;
105  for (SLuint i = 0; i < numKf; ++i)
106  {
107  SLAnimKeyframe* cur = _keyframes[i];
108 
109  if (cur->time() <= time)
110  {
111  *k1 = cur;
112  kfIndex = i;
113  }
114  }
115 
116  // time is than first kf
117  if (*k1 == nullptr)
118  {
119  *k1 = _keyframes.back();
120  // as long as k1 is in the back
121  }
122 
123  t1 = (*k1)->time();
124 
125  if (*k1 == _keyframes.back())
126  {
127  *k2 = _keyframes.front();
128  t2 = animationLength + (*k2)->time();
129  }
130  else
131  {
132  *k2 = _keyframes[kfIndex + 1];
133  t2 = (*k2)->time();
134  }
135 
136  if (Utils::abs(t1 - t2) < 0.0001f)
137  return 0.0f;
138 
139  /// @todo do we want to consider the edge case below or do we want
140  /// imported animations to have to have a keyframe at 0.0 time?
141  /// Is there a better solution for this problem?
142  /// e.x: the astroboy animation looks wrong when doing this
143  /// (but thats because it is **** and kf0 and kfn dont match up...
144  //
145  // if an animation doesn't have a keyframe at 0.0 and
146  // k1 is the last keyframe and k2 is the first keyframe
147  // and the current timestamp is just above zero in the timeline
148  //
149  // like this:
150  // 0.0 animationLenth
151  // |-.--*----------*----*------*------|~~~~*
152  // ^ ^ ^ ^
153  // | t2 t1 t2'
154  //
155  // t2' is where we put the t2 value if its a wrap around!
156  // time
157  // then the calculation below wont work because time < t1.
158  //
159  //
160  if (time < t1)
161  time += animationLength;
162 
163  return (time - t1) / (t2 - t1);
164 }
165 
166 //-----------------------------------------------------------------------------
167 /*! Constructor for specialized NodeAnimationTrack
168  */
170  : SLAnimTrack(animation),
171  _animatedNode(nullptr),
172  _interpolationCurve(nullptr),
173  _translationInterpolation(AI_linear),
174  _rebuildInterpolationCurve(true)
175 {
176 }
177 
178 //-----------------------------------------------------------------------------
179 /*! Destructor
180  */
182 {
184  delete _interpolationCurve;
185 }
186 
187 //-----------------------------------------------------------------------------
188 /*! Creates a new SLTransformKeyframe at 'time'.
189  */
191 {
192  return static_cast<SLTransformKeyframe*>(createKeyframe(time));
193 }
194 
195 //-----------------------------------------------------------------------------
196 /*! Calculates a new keyframe based on the input time and interpolation functions.
197  */
199  SLAnimKeyframe* keyframe) const
200 {
201  SLAnimKeyframe* k1;
202  SLAnimKeyframe* k2;
203 
204  SLfloat t = getKeyframesAtTime(time, &k1, &k2);
205 
206  if (k1 == nullptr)
207  return;
208 
209  SLTransformKeyframe* kfOut = static_cast<SLTransformKeyframe*>(keyframe);
210  SLTransformKeyframe* kf1 = static_cast<SLTransformKeyframe*>(k1);
211  SLTransformKeyframe* kf2 = static_cast<SLTransformKeyframe*>(k2);
212 
213  SLVec3f base = kf1->translation();
214  SLVec3f translation;
216  translation = base + (kf2->translation() - base) * t;
217  else
218  {
221  translation = _interpolationCurve->evaluate(time);
222  }
223 
224  kfOut->translation(translation);
225 
226  SLQuat4f rotation;
227  rotation = kf1->rotation().slerp(kf2->rotation(), t); // @todo provide a 2 parameter implementation for lerp, slerp etc.
228  kfOut->rotation(rotation);
229 
230  base = kf1->scale();
231  SLVec3f scale;
232  scale = base + (kf2->scale() - base) * t;
233  kfOut->scale(scale);
234 }
235 
236 //-----------------------------------------------------------------------------
237 /*! Applies the animation with the input timestamp to the set animation target if it exists.
238  */
240 {
241  if (_animatedNode)
242  applyToNode(_animatedNode, time, weight, scale);
243 }
244 
245 //-----------------------------------------------------------------------------
246 /*! Applies the animation to the input node with the input timestamp and weight.
247  */
249  SLfloat time,
250  SLfloat weight,
251  SLfloat scale)
252 {
253  if (node == nullptr)
254  return;
255 
256  SLTransformKeyframe kf(nullptr, time);
257  calcInterpolatedKeyframe(time, &kf);
258 
259  SLVec3f translation = kf.translation() * weight * scale;
260  node->translate(translation, TS_parent);
261 
262  // @todo update the slerp and lerp implementation for quaternions
263  // there is currently no early out for 1.0 and 0.0 inputs
264  // also provide a non OO version.
265  SLQuat4f rotation = SLQuat4f().slerp(kf.rotation(), weight);
266  node->rotate(rotation, TS_parent);
267 
268  // @todo find a good way to combine scale animations,
269  // we can't just scale them by a weight factor...
270  SLVec3f scl = kf.scale();
271  node->scale(scl);
272 }
273 //-----------------------------------------------------------------------------
274 //! Draws all visualizations of node animations
276 {
279  {
280  // Move the animation curve to the initial WM position of the node
281  SLMat4f parentWM = _animatedNode->parent()->updateAndGetWM();
282  SLMat4f initialOM = _animatedNode->initialOM();
283  _interpolationCurve->draw(parentWM * initialOM);
284  }
285 }
286 //-----------------------------------------------------------------------------
287 /*! Rebuilds the translation interpolation Bezier curve.
288  */
290 {
291  if (numKeyframes() > 1)
292  {
294 
295  // Build curve data w. cumulated times
296  SLVVec4f points;
297  points.resize((SLuint)numKeyframes());
298  // SLfloat curTime = 0;
299  for (SLuint i = 0; i < (SLuint)numKeyframes(); ++i)
300  {
301  SLVec3f t = ((SLTransformKeyframe*)_keyframes[i])->translation();
302  points[i].set(t.x, t.y, t.z, _keyframes[i]->time());
303  }
304 
305  // create curve and delete temp arrays again
306  _interpolationCurve = new SLCurveBezier(points);
307  }
308 }
309 
310 //-----------------------------------------------------------------------------
311 /*! Implementation for the keyframe creation function.
312  */
314 {
315  return new SLTransformKeyframe(this, time);
316 }
317 
318 //-----------------------------------------------------------------------------
319 /*! setter for the interpolation curve
320  */
322 {
324  delete _interpolationCurve;
325 
326  _interpolationCurve = curve;
328 }
329 //-----------------------------------------------------------------------------
float SLfloat
Definition: SL.h:173
unsigned int SLuint
Definition: SL.h:171
int SLint
Definition: SL.h:170
#define SL_DB_AXIS
Draw the coordinate axis of a node.
Definition: SLDrawBits.h:25
@ AI_linear
Definition: SLEnums.h:161
@ TS_parent
Definition: SLEnums.h:209
SLQuat4< SLfloat > SLQuat4f
Definition: SLQuat4.h:846
vector< SLVec4f > SLVVec4f
Definition: SLVec4.h:239
Base class for all animation keyframes.
void time(SLfloat t)
Abstract base class for SLAnimationTracks providing time and keyframe functions.
Definition: SLAnimTrack.h:32
SLAnimTrack(SLAnimation *parent)
Definition: SLAnimTrack.cpp:18
virtual SLAnimKeyframe * createKeyframeImpl(SLfloat time)=0
Keyframe creator function for derived implementations.
SLAnimKeyframe * keyframe(SLint index)
Definition: SLAnimTrack.cpp:48
SLAnimKeyframe * createKeyframe(SLfloat time)
Definition: SLAnimTrack.cpp:38
SLAnimation * _animation
parent animation that created this track
Definition: SLAnimTrack.h:54
SLfloat getKeyframesAtTime(SLfloat time, SLAnimKeyframe **k1, SLAnimKeyframe **k2) const
Definition: SLAnimTrack.cpp:62
SLint numKeyframes() const
Definition: SLAnimTrack.h:47
virtual ~SLAnimTrack()
Definition: SLAnimTrack.cpp:26
SLVKeyframe _keyframes
keyframe list for this track
Definition: SLAnimTrack.h:55
SLAnimation is the base container for all animation data.
Definition: SLAnimation.h:33
SLfloat lengthSec() const
Definition: SLAnimation.h:72
The SLCurveBezier class implements a Bezier curve interpolation.
Definition: SLCurveBezier.h:25
Base class for curves defined by multiple 3D points.
Definition: SLCurve.h:24
virtual SLVec3f evaluate(const SLfloat t)=0
virtual void draw(const SLMat4f &wm)=0
SLbool _rebuildInterpolationCurve
dirty flag of the Bezier curve
Definition: SLAnimTrack.h:91
virtual void drawVisuals(SLSceneView *sv)
Draws all visualizations of node animations.
void interpolationCurve(SLCurve *curve)
virtual ~SLNodeAnimTrack()
SLCurve * _interpolationCurve
the translation interpolation curve
Definition: SLAnimTrack.h:89
SLAnimInterpolation _translationInterpolation
interpolation mode for translations (Bezier or linear)
Definition: SLAnimTrack.h:90
SLTransformKeyframe * createNodeKeyframe(SLfloat time)
SLNode * _animatedNode
the target node for this track_nodeID
Definition: SLAnimTrack.h:88
void buildInterpolationCurve() const
virtual void applyToNode(SLNode *node, SLfloat time, SLfloat weight=1.0f, SLfloat scale=1.0f)
SLNodeAnimTrack(SLAnimation *parent)
virtual void calcInterpolatedKeyframe(SLfloat time, SLAnimKeyframe *keyframe) const
virtual void apply(SLfloat time, SLfloat weight=1.0f, SLfloat scale=1.0f)
virtual SLAnimKeyframe * createKeyframeImpl(SLfloat time)
SLNode represents a node in a hierarchical scene graph.
Definition: SLNode.h:147
SLbool drawBit(SLuint bit)
Definition: SLNode.h:300
void parent(SLNode *p)
Definition: SLNode.cpp:600
void rotate(const SLQuat4f &rot, SLTransformSpace relativeTo=TS_object)
Definition: SLNode.cpp:945
void scale(SLfloat s)
Definition: SLNode.h:640
const SLMat4f & initialOM()
Definition: SLNode.h:296
void translate(const SLVec3f &vec, SLTransformSpace relativeTo=TS_object)
Definition: SLNode.cpp:906
SLQuat4< T > slerp(const SLQuat4< T > &q2, T t) const
Definition: SLQuat4.h:745
SceneView class represents a dynamic real time 3D view onto the scene.
Definition: SLSceneView.h:69
SLbool drawBit(SLuint bit)
Definition: SLSceneView.h:199
SLTransformKeyframe is a specialized SLKeyframe for node transformations.
void translation(const SLVec3f &t)
void scale(const SLVec3f &s)
void rotation(const SLQuat4f &r)
T y
Definition: SLVec3.h:43
T x
Definition: SLVec3.h:43
void set(const T X, const T Y, const T Z)
Definition: SLVec3.h:59
T z
Definition: SLVec3.h:43
T abs(T a)
Definition: Utils.h:249