BsAnimationUtility.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 BansheeEngine
  7. {
  8. void AnimationUtility::wrapTime(float& time, float start, float end, bool loop)
  9. {
  10. float length = end - start;
  11. // Clamp to start or loop
  12. if (time < start)
  13. {
  14. if (loop)
  15. time = time - std::floor(time / length) * length;
  16. else // Clamping
  17. time = start;
  18. }
  19. // Clamp to end or loop
  20. if (time > end)
  21. {
  22. if (loop)
  23. time = time - std::floor(time / length) * length;
  24. else // Clamping
  25. time = end;
  26. }
  27. }
  28. TAnimationCurve<Quaternion> AnimationUtility::eulerToQuaternionCurve(const TAnimationCurve<Vector3>& eulerCurve)
  29. {
  30. // TODO: We calculate tangents by sampling. There must be an analytical way to calculate tangents when converting
  31. // a curve.
  32. const float FIT_TIME = 0.001f;
  33. auto eulerToQuaternion = [&](INT32 keyIdx, float time, const Quaternion& lastQuat)
  34. {
  35. Vector3 angles = eulerCurve.evaluate(time, false);
  36. Quaternion quat(
  37. Degree(angles.x),
  38. Degree(angles.y),
  39. Degree(angles.z));
  40. // Flip quaternion in case rotation is over 180 degrees (use shortest path)
  41. if (keyIdx > 0)
  42. {
  43. float dot = quat.dot(lastQuat);
  44. if (dot < 0.0f)
  45. quat = -quat;
  46. }
  47. return quat;
  48. };
  49. INT32 numKeys = (INT32)eulerCurve.getNumKeyFrames();
  50. Vector<TKeyframe<Quaternion>> quatKeyframes(numKeys);
  51. // Calculate key values
  52. Quaternion lastQuat;
  53. for (INT32 i = 0; i < numKeys; i++)
  54. {
  55. float time = eulerCurve.getKeyFrame(i).time;
  56. Quaternion quat = eulerToQuaternion(i, time, lastQuat);
  57. quatKeyframes[i].time = time;
  58. quatKeyframes[i].value = quat;
  59. quatKeyframes[i].inTangent = Quaternion::ZERO;
  60. quatKeyframes[i].outTangent = Quaternion::ZERO;
  61. lastQuat = quat;
  62. }
  63. // Calculate extra values between keys so we can approximate tangents. If we're sampling very close to the key
  64. // the values should pretty much exactly match the tangent (assuming the curves are cubic hermite)
  65. for (INT32 i = 0; i < numKeys - 1; i++)
  66. {
  67. TKeyframe<Quaternion>& currentKey = quatKeyframes[i];
  68. TKeyframe<Quaternion>& nextKey = quatKeyframes[i + 1];
  69. float dt = nextKey.time - currentKey.time;
  70. float startFitTime = currentKey.time + dt * FIT_TIME;
  71. float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
  72. Quaternion startFitValue = eulerToQuaternion(i, startFitTime, currentKey.value);
  73. Quaternion endFitValue = eulerToQuaternion(i, endFitTime, startFitValue);
  74. float invFitTime = 1.0f / (dt * FIT_TIME);
  75. currentKey.outTangent = (startFitValue - currentKey.value) * invFitTime;
  76. nextKey.inTangent = (nextKey.value - endFitValue) * invFitTime;
  77. }
  78. return TAnimationCurve<Quaternion>(quatKeyframes);
  79. }
  80. TAnimationCurve<Vector3> AnimationUtility::quaternionToEulerCurve(const TAnimationCurve<Quaternion>& quatCurve)
  81. {
  82. // TODO: We calculate tangents by sampling. There must be an analytical way to calculate tangents when converting
  83. // a curve.
  84. const float FIT_TIME = 0.001f;
  85. auto quaternionToEuler = [&](float time)
  86. {
  87. Quaternion quat = quatCurve.evaluate(time, false);
  88. Radian x, y, z;
  89. quat.toEulerAngles(x, y, z);
  90. Vector3 euler(
  91. x.valueDegrees(),
  92. y.valueDegrees(),
  93. z.valueDegrees()
  94. );
  95. return euler;
  96. };
  97. INT32 numKeys = (INT32)quatCurve.getNumKeyFrames();
  98. Vector<TKeyframe<Vector3>> eulerKeyframes(numKeys);
  99. // Calculate key values
  100. for (INT32 i = 0; i < numKeys; i++)
  101. {
  102. float time = quatCurve.getKeyFrame(i).time;
  103. Vector3 euler = quaternionToEuler(time);
  104. eulerKeyframes[i].time = time;
  105. eulerKeyframes[i].value = euler;
  106. eulerKeyframes[i].inTangent = Vector3::ZERO;
  107. eulerKeyframes[i].outTangent = Vector3::ZERO;
  108. }
  109. // Calculate extra values between keys so we can approximate tangents. If we're sampling very close to the key
  110. // the values should pretty much exactly match the tangent (assuming the curves are cubic hermite)
  111. for (INT32 i = 0; i < numKeys - 1; i++)
  112. {
  113. TKeyframe<Vector3>& currentKey = eulerKeyframes[i];
  114. TKeyframe<Vector3>& nextKey = eulerKeyframes[i + 1];
  115. float dt = nextKey.time - currentKey.time;
  116. float startFitTime = currentKey.time + dt * FIT_TIME;
  117. float endFitTime = currentKey.time + dt * (1.0f - FIT_TIME);
  118. Vector3 startFitValue = quaternionToEuler(startFitTime);
  119. Vector3 endFitValue = quaternionToEuler(endFitTime);
  120. // If fit values rotate for more than 180 degrees, wrap them so they use the shortest path
  121. for(int j = 0; j < 3; j++)
  122. {
  123. startFitValue[j] = fmod(startFitValue[j] - currentKey.value[j] + 180.0f, 360.0f) + currentKey.value[j] - 180.0f;
  124. endFitValue[j] = nextKey.value[j] + fmod(nextKey.value[j] - endFitValue[j] + 180.0f, 360.0f) - 180.0f;
  125. }
  126. float invFitTime = 1.0f / (dt * FIT_TIME);
  127. currentKey.outTangent = (startFitValue - currentKey.value) * invFitTime;
  128. nextKey.inTangent = (nextKey.value - endFitValue) * invFitTime;
  129. }
  130. return TAnimationCurve<Vector3>(eulerKeyframes);
  131. }
  132. }