1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "BsCorePrerequisites.h"
6#include "Animation/BsCurveCache.h"
7#include "Math/BsVector3.h"
8#include "Math/BsVector2.h"
9#include "Math/BsQuaternion.h"
10#include "Allocators/BsPoolAlloc.h"
11
12namespace bs
13{
14 /** @addtogroup Animation-Internal
15 * @{
16 */
17
18 /** Animation keyframe, represented as an endpoint of a cubic hermite spline. */
19 template <class T>
20 struct TKeyframe
21 {
22 T value; /**< Value of the key. */
23 T inTangent; /**< Input tangent (going from the previous key to this one) of the key. */
24 T outTangent; /**< Output tangent (going from this key to next one) of the key. */
25 float time; /**< Position of the key along the animation spline. */
26
27 bool operator== (const TKeyframe<T>& rhs) const
28 {
29 return (value == rhs.value && inTangent == rhs.inTangent && outTangent == rhs.outTangent && time == rhs.time);
30 }
31
32 bool operator!= (const TKeyframe<T>& rhs) const
33 {
34 return !operator==(rhs);
35 }
36 };
37
38 /** Keyframe specialization for integers (no tangents). */
39 template <>
40 struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrameInt,pl:true) TKeyframe<INT32>
41 {
42 INT32 value; /**< Value of the key. */
43 float time; /**< Position of the key along the animation spline. */
44
45 bool operator== (const TKeyframe<INT32>& rhs) const
46 {
47 return (value == rhs.value && time == rhs.time);
48 }
49
50 bool operator!= (const TKeyframe<INT32>& rhs) const
51 {
52 return !operator==(rhs);
53 }
54 };
55
56 template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrame,pl:true) TKeyframe<float>;
57 template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrameVec3,pl:true) TKeyframe<Vector3>;
58 template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrameVec2,pl:true) TKeyframe<Vector2>;
59 template struct BS_SCRIPT_EXPORT(m:Animation,n:KeyFrameQuat,pl:true) TKeyframe<Quaternion>;
60
61 /**
62 * Animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve. The
63 * spline can be evaluated at any time, and uses caching to speed up multiple sequential evaluations.
64 */
65 template <class T>
66 class BS_CORE_EXPORT TAnimationCurve // Note: Curves are expected to be immutable for threading purposes
67 {
68 public:
69 typedef TKeyframe<T> KeyFrame;
70
71 TAnimationCurve() = default;
72
73 /**
74 * Creates a new animation curve.
75 *
76 * @param[in] keyframes Keyframes to initialize the curve with. They must be sorted by time.
77 */
78 BS_SCRIPT_EXPORT()
79 TAnimationCurve(const Vector<KeyFrame>& keyframes);
80
81 /**
82 * Evaluate the animation curve using caching. Caching can significantly speed of evaluation if the evaluation
83 * happens sequential order (which should be true for most curves). If evaluation is not happening in sequential
84 * order using the non-caching version of evaluate() might yield better performance.
85 *
86 * @param[in] time %Time to evaluate the curve at.
87 * @param[in] cache Cached data from previous requests that can be used for speeding up sequential calls
88 * to this method. Caller should ensure to maintain a persistent instance of this data
89 * for every animation using this curve in order to ensure cache is maintained.
90 * @param[in] loop If true the curve will loop when it goes past the end or beggining. Otherwise the
91 * curve value will be clamped.
92 * @return Interpolated value from the curve at provided time.
93 */
94 T evaluate(float time, const TCurveCache<T>& cache, bool loop = true) const;
95
96 /**
97 * Evaluate the animation curve at the specified time. If evaluating multiple values in a sequential order consider
98 * using the cached version of evaluate() for better performance.
99 *
100 * @param[in] time %Time to evaluate the curve at.
101 * @param[in] loop If true the curve will loop when it goes past the end or beggining. Otherwise the curve
102 * value will be clamped.
103 * @return Interpolated value from the curve at provided time.
104 */
105 BS_SCRIPT_EXPORT()
106 T evaluate(float time, bool loop = true) const;
107
108 /**
109 * Evaluates the integrated animation curve. (e.g. evaluating a curve containing velocity values will return
110 * a position).
111 *
112 * @param[in] time %Time to evaluate the curve at.
113 * @param[in] integrationCache Cache storing the values required for integration. Generated the first time
114 * this method is called and re-used on subsequent calls. Caller must ensure to
115 * use the cache only with the curve it was originally used on. Separate caches
116 * need to be used for single and double integration evaluation.
117 * @return Interpolated value from the curve at provided time.
118 */
119 T evaluateIntegrated(float time, const TCurveIntegrationCache<T>& integrationCache) const;
120
121 /**
122 * Evaluates the double integrated animation curve. (e.g. evaluating a curve containing acceleration values will
123 * return a position).
124 *
125 * @param[in] time %Time to evaluate the curve at.
126 * @param[in] integrationCache Cache storing the values required for integration. Generated the first time
127 * this method is called and re-used on subsequent calls. Caller must ensure to
128 * use the cache only with the curve it was originally used on. Separate caches
129 * need to be used for single and double integration evaluation.
130 * @return Interpolated value from the curve at provided time.
131 */
132 T evaluateIntegratedDouble(float time, const TCurveIntegrationCache<T>& integrationCache) const;
133
134 /**
135 * Evaluate the animation curve at the specified time and returns a new keyframe containing the evaluated value
136 * and tangents.
137 *
138 * @param[in] time %Time to evaluate the curve at.
139 * @param[in] loop If true the curve will loop when it goes past the end or beginning. Otherwise the curve
140 * value will be clamped.
141 * @return Keyframe containing the interpolated value and tangents at provided time.
142 */
143 KeyFrame evaluateKey(float time, bool loop = true) const;
144
145 /**
146 * Splits a piece of the animation curve into a separate animation curve.
147 *
148 * @param[in] start Beginning time of the split curve.
149 * @param[in] end End time of the split curve.
150 * @return New curve with data corresponding to the provided split times.
151 */
152 TAnimationCurve<T> split(float start, float end);
153
154 /**
155 * Converts a normal curve into an additive curve. It is assumed the first keyframe in the curve is the reference
156 * key from which to generate the additive curve. Such curves can then be added on top of a curve containing
157 * reference keys.
158 */
159 void makeAdditive();
160
161 /** Returns the time of the first and last keyframe in the curve. */
162 std::pair<float, float> getTimeRange() const;
163
164 /** Calculates the minimal and maximal value of the curve. */
165 std::pair<T, T> calculateRange() const;
166
167 /** Calculates the minimal and maximal value of the integrated curve. */
168 std::pair<T, T> calculateRangeIntegrated(const TCurveIntegrationCache<T>& cache) const;
169
170 /** Calculates the minimal and maximal value of the doubly integrated curve. */
171 std::pair<T, T> calculateRangeIntegratedDouble(const TCurveIntegrationCache<T>& cache) const;
172
173 /** Returns the length of the animation curve, from time zero to last keyframe. */
174 float getLength() const { return mEnd; }
175
176 /** Returns the total number of key-frames in the curve. */
177 UINT32 getNumKeyFrames() const { return (UINT32)mKeyframes.size(); }
178
179 /** Returns a keyframe at the specified index. */
180 const TKeyframe<T>& getKeyFrame(UINT32 idx) const { return mKeyframes[idx]; }
181
182 /** Returns a list of all keyframes in the curve. */
183 BS_SCRIPT_EXPORT(n:KeyFrames,pr:getter)
184 const Vector<TKeyframe<T>>& getKeyFrames() const { return mKeyframes; }
185
186 bool operator== (const TAnimationCurve<T>& rhs) const;
187 bool operator!= (const TAnimationCurve<T>& rhs) const { return !operator==(rhs); }
188 private:
189 friend struct RTTIPlainType<TAnimationCurve<T>>;
190
191 /**
192 * Returns a pair of keys that can be used for interpolating to field the value at the provided time. This attempts
193 * to find keys using the cache first, and if not possible falls back to a full search.
194 *
195 * @param[in] time Time for which to find the relevant keys from. It is expected to be clamped to a
196 * valid range within the curve.
197 * @param[in] cache Animation instance data holding the time to evaluate the curve at, and any cached
198 * data from previous requests. Time is expected to be clamped to a valid range
199 * within the curve.
200 * @param[out] leftKey Index of the key to interpolate from.
201 * @param[out] rightKey Index of the key to interpolate to.
202 */
203 void findKeys(float time, const TCurveCache<T>& cache, UINT32& leftKey, UINT32& rightKey) const;
204
205 /**
206 * Returns a pair of keys that can be used for interpolating to field the value at the provided time.
207 *
208 * @param[in] time Time for which to find the relevant keys from. It is expected to be clamped to a
209 * valid range within the curve.
210 * @param[out] leftKey Index of the key to interpolate from.
211 * @param[out] rightKey Index of the key to interpolate to.
212 */
213 void findKeys(float time, UINT32& leftKey, UINT32& rightKey) const;
214
215 /** Returns a keyframe index nearest to the provided time. */
216 UINT32 findKey(float time);
217
218 /**
219 * Calculates a key in-between the provided two keys.
220 *
221 * @param[in] lhs Key to interpolate from.
222 * @param[in] rhs Key to interpolate to.
223 * @param[in] time Curve time to interpolate the keys at.
224 * @return Interpolated key value.
225 */
226 KeyFrame evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time) const;
227
228 /** Creates a cache used for quick evaluation of single integrated curves. */
229 void buildIntegrationCache(const TCurveIntegrationCache<T>& cache) const;
230
231 /** Creates a cache used for quick evaluation of double integrated curves. */
232 void buildDoubleIntegrationCache(const TCurveIntegrationCache<T>& cache) const;
233
234 static const UINT32 CACHE_LOOKAHEAD;
235
236 Vector<KeyFrame> mKeyframes;
237 float mStart = 0.0f;
238 float mEnd = 0.0f;
239 float mLength = 0.0f;
240 };
241
242#ifdef BS_SBGEN
243 template class BS_SCRIPT_EXPORT(m:Animation,n:AnimationCurve) TAnimationCurve<float>;
244 template class BS_SCRIPT_EXPORT(m:Animation,n:Vector3Curve) TAnimationCurve<Vector3>;
245 template class BS_SCRIPT_EXPORT(m:Animation,n:Vector2Curve) TAnimationCurve<Vector2>;
246 template class BS_SCRIPT_EXPORT(m:Animation,n:QuaternionCurve) TAnimationCurve<Quaternion>;
247 template class BS_SCRIPT_EXPORT(m:Animation,n:IntegerCurve) TAnimationCurve<INT32>;
248#endif
249
250 /** Flags that describe an animation curve. */
251 enum BS_SCRIPT_EXPORT(n:AnimationCurveFlags) class AnimationCurveFlag
252 {
253 /**
254 * If enabled, the curve was imported from an external file and not created within the engine. This will affect
255 * how are animation results applied to scene objects (with imported animations it is assumed the curve is
256 * animating bones and with in-engine curves it is assumed the curve is animating scene objects).
257 */
258 ImportedCurve = 1 << 0,
259 /** Signifies the curve is used to animate between different frames within a morph channel. In range [0, 1]. */
260 MorphFrame = 1 << 1,
261 /** Signifies the curve is used to adjust the weight of a morph channel. In range [0, 1]. */
262 MorphWeight = 1 << 2
263 };
264
265 typedef Flags<AnimationCurveFlag> AnimationCurveFlags;
266 BS_FLAGS_OPERATORS(AnimationCurveFlag);
267
268 /** An animation curve and its name. */
269 template <class T>
270 struct TNamedAnimationCurve
271 {
272 TNamedAnimationCurve() = default;
273
274 /**
275 * Constructs a new named animation curve.
276 *
277 * @param[in] name Name of the curve.
278 * @param[in] curve Curve containing the animation data.
279 */
280 TNamedAnimationCurve(const String& name, const TAnimationCurve<T> curve)
281 :name(name), curve(curve)
282 { }
283
284 /**
285 * Constructs a new named animation curve.
286 *
287 * @param[in] name Name of the curve.
288 * @param[in] flags Flags that describe the animation curve.
289 * @param[in] curve Curve containing the animation data.
290 */
291 TNamedAnimationCurve(const String& name, AnimationCurveFlags flags, const TAnimationCurve<T> curve)
292 :name(name), curve(curve)
293 { }
294
295 /** Name of the curve. */
296 String name;
297
298 /** Flags that describe the animation curve. */
299 AnimationCurveFlags flags;
300
301 /** Actual curve containing animation data. */
302 TAnimationCurve<T> curve;
303 };
304
305#ifdef BS_SBGEN
306 template class BS_SCRIPT_EXPORT(m:Animation,n:NamedFloatCurve,pl:true) TNamedAnimationCurve<float>;
307 template class BS_SCRIPT_EXPORT(m:Animation,n:NamedVector3Curve,pl:true) TNamedAnimationCurve<Vector3>;
308 template class BS_SCRIPT_EXPORT(m:Animation,n:NamedVector2Curve,pl:true) TNamedAnimationCurve<Vector2>;
309 template class BS_SCRIPT_EXPORT(m:Animation,n:NamedQuaternionCurve,pl:true) TNamedAnimationCurve<Quaternion>;
310 template class BS_SCRIPT_EXPORT(m:Animation,n:NamedIntegerCurve,pl:true) TNamedAnimationCurve<INT32>;
311#endif
312
313 /** @} */
314
315 IMPLEMENT_GLOBAL_POOL(TAnimationCurve<float>, 32)
316}
317