BsAnimationCurve.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsAnimationCurve.h"
  4. #include "BsAnimationCurveRTTI.h"
  5. #include "BsVector3.h"
  6. #include "BsQuaternion.h"
  7. #include "BsMath.h"
  8. namespace BansheeEngine
  9. {
  10. template <class T>
  11. const UINT32 TAnimationCurve<T>::CACHE_LOOKAHEAD = 3;
  12. template <class T>
  13. TAnimationCurve<T>::TAnimationCurve()
  14. :mStart(0.0f), mEnd(0.0f), mLength(0.0f)
  15. {
  16. }
  17. template <class T>
  18. TAnimationCurve<T>::TAnimationCurve(const Vector<KeyFrame>& keyframes)
  19. :mKeyframes(keyframes)
  20. {
  21. #if BS_DEBUG_MODE
  22. // Ensure keyframes are sorted
  23. if(keyframes.size() > 0)
  24. {
  25. float time = keyframes[0].time;
  26. for (UINT32 i = 1; i < (UINT32)keyframes.size(); i++)
  27. {
  28. assert(keyframes[i].time > time);
  29. time = keyframes[i].time;
  30. }
  31. }
  32. #endif
  33. if (keyframes.size() > 0)
  34. {
  35. mStart = keyframes[0].time;
  36. mEnd = keyframes.back().time;
  37. }
  38. else
  39. {
  40. mStart = 0.0f;
  41. mEnd = 0.0f;
  42. }
  43. mLength = mEnd - mStart;
  44. }
  45. template <class T>
  46. T TAnimationCurve<T>::evaluate(const TCurveEvaluatorData<T>& animInstance, bool loop) const
  47. {
  48. if (mKeyframes.size() == 0)
  49. return T();
  50. float time = animInstance.time;
  51. // Wrap time if looping
  52. if(loop)
  53. {
  54. if (time < mStart)
  55. time = time - std::floor(time / mLength) * mLength;
  56. else if (time > mEnd)
  57. time = time - std::floor(time / mLength) * mLength;
  58. }
  59. // If time is within cache, evaluate it directly
  60. if (time >= animInstance.cachedCurveStart && time < animInstance.cachedCurveEnd)
  61. return evaluateCache(animInstance);
  62. // Clamp to start, cache constant of the first key and return
  63. if(time < mStart)
  64. {
  65. animInstance.cachedCurveStart = -std::numeric_limits<float>::infinity();
  66. animInstance.cachedCurveEnd = mStart;
  67. animInstance.cachedKey = 0;
  68. animInstance.cachedCubicCoefficients[0] = 0.0f;
  69. animInstance.cachedCubicCoefficients[1] = 0.0f;
  70. animInstance.cachedCubicCoefficients[2] = 0.0f;
  71. animInstance.cachedCubicCoefficients[3] = mKeyframes[0].value;
  72. return mKeyframes[0].value;
  73. }
  74. if(time > mEnd) // Clamp to end, cache constant of the final key and return
  75. {
  76. UINT32 lastKey = (UINT32)mKeyframes.size() - 1;
  77. animInstance.cachedCurveStart = mEnd;
  78. animInstance.cachedCurveEnd = std::numeric_limits<float>::infinity();
  79. animInstance.cachedKey = lastKey;
  80. animInstance.cachedCubicCoefficients[0] = 0.0f;
  81. animInstance.cachedCubicCoefficients[1] = 0.0f;
  82. animInstance.cachedCubicCoefficients[2] = 0.0f;
  83. animInstance.cachedCubicCoefficients[3] = mKeyframes[lastKey].value;
  84. return mKeyframes[lastKey].value;
  85. }
  86. // Since our value is not in cache, search for the valid pair of keys of interpolate
  87. UINT32 leftKeyIdx;
  88. UINT32 rightKeyIdx;
  89. findKeys(time, animInstance, leftKeyIdx, rightKeyIdx);
  90. // Calculate cubic hermite curve coefficients so we can store them in cache
  91. const KeyFrame& leftKey = mKeyframes[leftKeyIdx];
  92. const KeyFrame& rightKey = mKeyframes[rightKeyIdx];
  93. float length = rightKey.time - leftKey.time;
  94. animInstance.cachedCurveStart = leftKey.time;
  95. animInstance.cachedCurveEnd = rightKey.time;
  96. Math::cubicHermiteCoefficients(leftKey.value, rightKey.value, leftKey.outTangent, rightKey.inTangent, length,
  97. animInstance.cachedCubicCoefficients);
  98. // TODO - Handle stepped curve - If tangents are infinite assume constant value from left key is used
  99. T output = evaluateCache(animInstance);
  100. return output;
  101. }
  102. template <class T>
  103. T TAnimationCurve<T>::evaluate(float time, bool loop) const
  104. {
  105. if (mKeyframes.size() == 0)
  106. return T();
  107. // Clamp to start or loop
  108. if (time < mStart)
  109. {
  110. if (loop)
  111. time = time - std::floor(time / mLength) * mLength;
  112. else // Clamping
  113. time = mStart;
  114. }
  115. // Clamp to end or loop
  116. if (time > mEnd)
  117. {
  118. if (loop)
  119. time = time - std::floor(time / mLength) * mLength;
  120. else // Clamping
  121. time = mEnd;
  122. }
  123. UINT32 leftKeyIdx;
  124. UINT32 rightKeyIdx;
  125. findKeys(time, leftKeyIdx, rightKeyIdx);
  126. // Evaluate curve as hermite cubic spline
  127. const KeyFrame& leftKey = mKeyframes[leftKeyIdx];
  128. const KeyFrame& rightKey = mKeyframes[rightKeyIdx];
  129. float length = rightKey.time - leftKey.time;
  130. float t = (time - leftKey.time) / length;
  131. T leftTangent;
  132. T rightTangent; // TODO - Remove zero init for vectors/quaternions by default
  133. if (Math::approxEquals(t, 0.0f))
  134. {
  135. t = 0.0f;
  136. leftTangent = T();
  137. rightTangent = T();
  138. }
  139. else
  140. {
  141. // Resize tangents since we're not evaluating the curve over unit range
  142. leftTangent = leftKey.outTangent * length;
  143. rightTangent = rightKey.inTangent * length;
  144. }
  145. return Math::cubicHermite(t, leftKey.value, rightKey.value, leftTangent, rightTangent);
  146. }
  147. template <class T>
  148. T TAnimationCurve<T>::evaluateCache(const TCurveEvaluatorData<T>& animInstance) const
  149. {
  150. float t = animInstance.time - animInstance.cachedCurveStart;
  151. const T* coeffs = animInstance.cachedCubicCoefficients;
  152. return t * (t * (t * coeffs[0] + coeffs[1]) + coeffs[2]) + coeffs[3];
  153. }
  154. template <class T>
  155. void TAnimationCurve<T>::findKeys(float time, const TCurveEvaluatorData<T>& animInstance, UINT32& leftKey, UINT32& rightKey) const
  156. {
  157. // Check nearby keys first if there is cached data
  158. if (animInstance.cachedKey != (UINT32)-1)
  159. {
  160. const KeyFrame& curKey = mKeyframes[animInstance.cachedKey];
  161. if (time >= curKey.time)
  162. {
  163. UINT32 end = std::min((UINT32)mKeyframes.size(), animInstance.cachedKey + CACHE_LOOKAHEAD + 1);
  164. for (UINT32 i = animInstance.cachedKey + 1; i < end; i++)
  165. {
  166. const KeyFrame& nextKey = mKeyframes[i];
  167. if (time < nextKey.time)
  168. {
  169. leftKey = i - 1;
  170. rightKey = i;
  171. animInstance.cachedKey = leftKey;
  172. return;
  173. }
  174. }
  175. }
  176. else
  177. {
  178. UINT32 start = (UINT32)std::max(0, (INT32)animInstance.cachedKey - (INT32)CACHE_LOOKAHEAD);
  179. for(UINT32 i = start; i < animInstance.cachedKey; i++)
  180. {
  181. const KeyFrame& prevKey = mKeyframes[i];
  182. if (time >= prevKey.time)
  183. {
  184. leftKey = i;
  185. rightKey = i + 1;
  186. animInstance.cachedKey = leftKey;
  187. return;
  188. }
  189. }
  190. }
  191. }
  192. // Cannot find nearby ones, search all keys
  193. findKeys(time, leftKey, rightKey);
  194. animInstance.cachedKey = leftKey;
  195. }
  196. template <class T>
  197. void TAnimationCurve<T>::findKeys(float time, UINT32& leftKey, UINT32& rightKey) const
  198. {
  199. INT32 start = 0;
  200. INT32 searchLength = (INT32)mKeyframes.size();
  201. while(searchLength > 0)
  202. {
  203. INT32 half = searchLength >> 1;
  204. INT32 mid = start + half;
  205. if(time < mKeyframes[mid].time)
  206. {
  207. searchLength = half;
  208. }
  209. else
  210. {
  211. start = mid + 1;
  212. searchLength -= half - 1;
  213. }
  214. }
  215. leftKey = start - 1;
  216. rightKey = std::min(start, (INT32)mKeyframes.size() - 1);
  217. }
  218. template <class T>
  219. UINT32 TAnimationCurve<T>::findKey(float time)
  220. {
  221. UINT32 leftKeyIdx;
  222. UINT32 rightKeyIdx;
  223. findKeys(time, leftKeyIdx, rightKeyIdx);
  224. const KeyFrame& leftKey = mKeyframes[leftKeyIdx];
  225. const KeyFrame& rightKey = mKeyframes[rightKeyIdx];
  226. if (Math::abs(leftKey.time - time) <= Math::abs(rightKey.time - time))
  227. return leftKeyIdx;
  228. return rightKeyIdx;
  229. }
  230. template <class T>
  231. TKeyframe<T> TAnimationCurve<T>::evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time)
  232. {
  233. float length = rhs.time - lhs.time;
  234. float t = (time - lhs.time) / length;
  235. TKeyframe<T> output;
  236. // TODO
  237. return output;
  238. }
  239. template <class T>
  240. TAnimationCurve<T> TAnimationCurve<T>::split(float start, float end)
  241. {
  242. Vector<TKeyframe<T>> keyFrames;
  243. start = Math::clamp(start, mStart, mEnd);
  244. end = Math::clamp(end, mStart, mEnd);
  245. if (Math::approxEquals(end - start, 0.0f))
  246. return TAnimationCurve<T>();
  247. UINT32 startKeyIdx = findKey(start);
  248. UINT32 endKeyIdx = findKey(end);
  249. keyFrames.reserve(endKeyIdx - startKeyIdx + 2);
  250. const KeyFrame& startKey = mKeyframes[startKeyIdx];
  251. const KeyFrame& endKey = mKeyframes[endKeyIdx];
  252. if(!Math::approxEquals(startKey.time, start))
  253. {
  254. keyFrames.push_back(evaluateKey(startKey, mKeyframes[startKeyIdx + 1], start));
  255. if(start > startKey.time)
  256. startKeyIdx++;
  257. }
  258. if(!Math::approxEquals(endKey.time, end))
  259. {
  260. keyFrames.push_back(evaluateKey(endKey, mKeyframes[endKeyIdx + 1], end));
  261. if (end < endKey.time)
  262. endKeyIdx--;
  263. }
  264. keyFrames.insert(keyFrames.begin(), mKeyframes.begin() + startKeyIdx, mKeyframes.begin() + endKeyIdx + 1);
  265. for (auto& entry : keyFrames)
  266. entry.time -= start;
  267. return TAnimationCurve<T>(keyFrames);
  268. }
  269. template class TAnimationCurve<Vector3>;
  270. template class TAnimationCurve<Quaternion>;
  271. template class TAnimationCurve<float>;
  272. }