| 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 | |
| 12 | namespace 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 | |