BsAnimationUtility.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsAnimationUtility.h"
  4. #include "BsVector3.h"
  5. #include "BsQuaternion.h"
  6. namespace bs
  7. {
  8. void setStepTangent(const TKeyframe<Vector3>& lhsIn, const TKeyframe<Vector3>& rhsIn,
  9. TKeyframe<Quaternion>& lhsOut, TKeyframe<Quaternion>& rhsOut)
  10. {
  11. for (UINT32 i = 0; i < 3; i++)
  12. {
  13. if (lhsIn.outTangent[i] != std::numeric_limits<float>::infinity() &&
  14. rhsIn.inTangent[i] != std::numeric_limits<float>::infinity())
  15. continue;
  16. lhsOut.outTangent[i] = std::numeric_limits<float>::infinity();
  17. rhsOut.inTangent[i] = std::numeric_limits<float>::infinity();
  18. }
  19. }
  20. void setStepTangent(const TKeyframe<Quaternion>& lhsIn, const TKeyframe<Quaternion>& rhsIn,
  21. TKeyframe<Vector3>& lhsOut, TKeyframe<Vector3>& rhsOut)
  22. {
  23. for (UINT32 i = 0; i < 4; i++)
  24. {
  25. if (lhsIn.outTangent[i] != std::numeric_limits<float>::infinity() &&
  26. rhsIn.inTangent[i] != std::numeric_limits<float>::infinity())
  27. continue;
  28. if (i < 3)
  29. {
  30. lhsOut.outTangent[i] = std::numeric_limits<float>::infinity();
  31. rhsOut.inTangent[i] = std::numeric_limits<float>::infinity();
  32. }
  33. }
  34. }
  35. void AnimationUtility::wrapTime(float& time, float start, float end, bool loop)
  36. {
  37. float length = end - start;
  38. if(Math::approxEquals(length, 0.0f))
  39. {
  40. time = 0.0f;
  41. return;
  42. }
  43. // Clamp to start or loop
  44. if (time < start)
  45. {
  46. if (loop)
  47. time = time + (std::floor(end - time) / length) * length;
  48. else // Clamping
  49. time = start;
  50. }
  51. // Clamp to end or loop
  52. if (time > end)
  53. {
  54. if (loop)
  55. time = time - std::floor((time - start) / length) * length;
  56. else // Clamping
  57. time = end;
  58. }
  59. }
  60. TAnimationCurve<Quaternion> AnimationUtility::eulerToQuaternionCurve(const TAnimationCurve<Vector3>& eulerCurve)
  61. {
  62. // TODO: We calculate tangents by sampling which can introduce error in the tangents. The error can be exacerbated
  63. // by the fact we constantly switch between the two representations, possibly losing precision every time. Instead
  64. // there must be an analytical way to calculate tangents when converting a curve, or a better way of dealing with
  65. // tangents.
  66. // Consider:
  67. // - Sampling multiple points to calculate tangents to improve precision
  68. // - Store the original quaternion curve with the euler curve
  69. // - This way conversion from euler to quaternion can be done while individual keyframes are being modified
  70. // ensuring the conversion results are immediately visible, and that no accumulation error happens are curves
  71. // are converted between two formats back and forth.
  72. // - Don't store rotation tangents directly, instead store tangent parameters (TCB) which can be shared between
  73. // both curves, and used for tangent calculation.
  74. //
  75. // If we decide to keep tangents in the current form, then we should also enforce that all euler curve tangents are
  76. // the same.
  77. const float FIT_TIME = 0.001f;
  78. auto eulerToQuaternion = [&](INT32 keyIdx, Vector3& angles, const Quaternion& lastQuat)
  79. {
  80. Quaternion quat(
  81. Degree(angles.x),
  82. Degree(angles.y),
  83. Degree(angles.z));
  84. // Flip quaternion in case rotation is over 180 degrees (use shortest path)
  85. if (keyIdx > 0)
  86. {
  87. float dot = quat.dot(lastQuat);
  88. if (dot < 0.0f)
  89. quat = -quat;
  90. }
  91. return quat;
  92. };
  93. INT32 numKeys = (INT32)eulerCurve.getNumKeyFrames();
  94. Vector<TKeyframe<Quaternion>> quatKeyframes(numKeys);
  95. // Calculate key values
  96. Quaternion lastQuat(BsZero);
  97. for (INT32 i = 0; i < numKeys; i++)
  98. {
  99. float time = eulerCurve.getKeyFrame(i).time;
  100. Vector3 angles = eulerCurve.getKeyFrame(i).value;
  101. Quaternion quat = eulerToQuaternion(i, angles, lastQuat);
  102. quatKeyframes[i].time = time;
  103. quatKeyframes[i].value = quat;
  104. quatKeyframes[i].inTangent = Quaternion::ZERO;
  105. quatKeyframes[i].outTangent = Quaternion::ZERO;
  106. lastQuat = quat;
  107. }
  108. // Calculate extra values between keys so we can approximate tangents. If we're sampling very close to the key
  109. // the values should pretty much exactly match the tangent (assuming the curves are cubic hermite)
  110. for (INT32 i = 0; i < numKeys - 1; i++)
  111. {
  112. TKeyframe<Quaternion>& currentKey = quatKeyframes[i];
  113. TKeyframe<Quaternion>& nextKey = quatKeyframes[i + 1];
  114. const TKeyframe<Vector3>& currentEulerKey = eulerCurve.getKeyFrame(i);
  115. const TKeyframe<Vector3>& nextEulerKey = eulerCurve.getKeyFrame(i + 1);
  116. float dt = nextKey.time - currentKey.time;
  117. float startFitTime = currentKey.time + dt * FIT_TIME;
  118. float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
  119. Vector3 anglesStart = eulerCurve.evaluate(startFitTime, false);
  120. Vector3 anglesEnd = eulerCurve.evaluate(endFitTime, false);
  121. Quaternion startFitValue = eulerToQuaternion(i, anglesStart, currentKey.value);
  122. Quaternion endFitValue = eulerToQuaternion(i, anglesEnd, startFitValue);
  123. float invFitTime = 1.0f / (dt * FIT_TIME);
  124. currentKey.outTangent = (startFitValue - currentKey.value) * invFitTime;
  125. nextKey.inTangent = (nextKey.value - endFitValue) * invFitTime;
  126. setStepTangent(currentEulerKey, nextEulerKey, currentKey, nextKey);
  127. }
  128. return TAnimationCurve<Quaternion>(quatKeyframes);
  129. }
  130. TAnimationCurve<Vector3> AnimationUtility::quaternionToEulerCurve(const TAnimationCurve<Quaternion>& quatCurve)
  131. {
  132. // TODO: We calculate tangents by sampling. There must be an analytical way to calculate tangents when converting
  133. // a curve.
  134. const float FIT_TIME = 0.001f;
  135. auto quaternionToEuler = [&](const Quaternion& quat)
  136. {
  137. Radian x, y, z;
  138. quat.toEulerAngles(x, y, z);
  139. Vector3 euler(
  140. x.valueDegrees(),
  141. y.valueDegrees(),
  142. z.valueDegrees()
  143. );
  144. return euler;
  145. };
  146. INT32 numKeys = (INT32)quatCurve.getNumKeyFrames();
  147. Vector<TKeyframe<Vector3>> eulerKeyframes(numKeys);
  148. // Calculate key values
  149. for (INT32 i = 0; i < numKeys; i++)
  150. {
  151. float time = quatCurve.getKeyFrame(i).time;
  152. Quaternion quat = quatCurve.getKeyFrame(i).value;
  153. Vector3 euler = quaternionToEuler(quat);
  154. eulerKeyframes[i].time = time;
  155. eulerKeyframes[i].value = euler;
  156. eulerKeyframes[i].inTangent = Vector3::ZERO;
  157. eulerKeyframes[i].outTangent = Vector3::ZERO;
  158. }
  159. // Calculate extra values between keys so we can approximate tangents. If we're sampling very close to the key
  160. // the values should pretty much exactly match the tangent (assuming the curves are cubic hermite)
  161. for (INT32 i = 0; i < numKeys - 1; i++)
  162. {
  163. TKeyframe<Vector3>& currentKey = eulerKeyframes[i];
  164. TKeyframe<Vector3>& nextKey = eulerKeyframes[i + 1];
  165. const TKeyframe<Quaternion>& currentQuatKey = quatCurve.getKeyFrame(i);
  166. const TKeyframe<Quaternion>& nextQuatKey = quatCurve.getKeyFrame(i + 1);
  167. float dt = nextKey.time - currentKey.time;
  168. float startFitTime = currentKey.time + dt * FIT_TIME;
  169. float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
  170. Quaternion startQuat = Quaternion::normalize(quatCurve.evaluate(startFitTime, false));
  171. Quaternion endQuat = Quaternion::normalize(quatCurve.evaluate(endFitTime, false));
  172. Vector3 startFitValue = quaternionToEuler(startQuat);
  173. Vector3 endFitValue = quaternionToEuler(endQuat);
  174. // If fit values rotate for more than 180 degrees, wrap them so they use the shortest path
  175. for(int j = 0; j < 3; j++)
  176. {
  177. startFitValue[j] = fmod(startFitValue[j] - currentKey.value[j] + 180.0f, 360.0f) + currentKey.value[j] - 180.0f;
  178. endFitValue[j] = nextKey.value[j] + fmod(nextKey.value[j] - endFitValue[j] + 180.0f, 360.0f) - 180.0f;
  179. }
  180. float invFitTime = 1.0f / (dt * FIT_TIME);
  181. currentKey.outTangent = (startFitValue - currentKey.value) * invFitTime;
  182. nextKey.inTangent = (nextKey.value - endFitValue) * invFitTime;
  183. setStepTangent(currentQuatKey, nextQuatKey, currentKey, nextKey);
  184. }
  185. return TAnimationCurve<Vector3>(eulerKeyframes);
  186. }
  187. template<class T>
  188. TAnimationCurve<T> AnimationUtility::scaleCurve(const TAnimationCurve<T>& curve, float factor)
  189. {
  190. INT32 numKeys = (INT32)curve.getNumKeyFrames();
  191. Vector<TKeyframe<T>> newKeyframes(numKeys);
  192. for (INT32 i = 0; i < numKeys; i++)
  193. {
  194. const TKeyframe<T>& key = curve.getKeyFrame(i);
  195. newKeyframes[i].time = key.time;
  196. newKeyframes[i].value = key.value * factor;
  197. newKeyframes[i].inTangent = key.inTangent * factor;
  198. newKeyframes[i].outTangent = key.outTangent * factor;
  199. }
  200. return TAnimationCurve<T>(newKeyframes);
  201. }
  202. template<class T>
  203. TAnimationCurve<T> AnimationUtility::offsetCurve(const TAnimationCurve<T>& curve, float offset)
  204. {
  205. INT32 numKeys = (INT32)curve.getNumKeyFrames();
  206. Vector<TKeyframe<T>> newKeyframes(numKeys);
  207. for (INT32 i = 0; i < numKeys; i++)
  208. {
  209. const TKeyframe<T>& key = curve.getKeyFrame(i);
  210. newKeyframes[i].time = key.time + offset;
  211. newKeyframes[i].value = key.value;
  212. newKeyframes[i].inTangent = key.inTangent;
  213. newKeyframes[i].outTangent = key.outTangent;
  214. }
  215. return TAnimationCurve<T>(newKeyframes);
  216. }
  217. template BS_CORE_EXPORT TAnimationCurve<Vector3> AnimationUtility::scaleCurve(const TAnimationCurve<Vector3>& curve, float factor);
  218. template BS_CORE_EXPORT TAnimationCurve<Quaternion> AnimationUtility::scaleCurve(const TAnimationCurve<Quaternion>& curve, float factor);
  219. template BS_CORE_EXPORT TAnimationCurve<float> AnimationUtility::scaleCurve(const TAnimationCurve<float>& curve, float factor);
  220. template BS_CORE_EXPORT TAnimationCurve<Vector3> AnimationUtility::offsetCurve(const TAnimationCurve<Vector3>& curve, float offset);
  221. template BS_CORE_EXPORT TAnimationCurve<Quaternion> AnimationUtility::offsetCurve(const TAnimationCurve<Quaternion>& curve, float offset);
  222. template BS_CORE_EXPORT TAnimationCurve<float> AnimationUtility::offsetCurve(const TAnimationCurve<float>& curve, float offset);
  223. }