BsAnimationUtility.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Animation/BsAnimationUtility.h"
  4. #include "Math/BsVector3.h"
  5. #include "Math/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. SPtr<TAnimationCurve<Quaternion>> AnimationUtility::eulerToQuaternionCurve(const SPtr<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 bs_shared_ptr_new<TAnimationCurve<Quaternion>>(quatKeyframes);
  129. }
  130. SPtr<TAnimationCurve<Vector3>> AnimationUtility::quaternionToEulerCurve(const SPtr<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 bs_shared_ptr_new<TAnimationCurve<Vector3>>(eulerKeyframes);
  186. }
  187. Vector<SPtr<TAnimationCurve<float>>> AnimationUtility::splitCurve(const SPtr<TAnimationCurve<Vector3>>& compoundCurve)
  188. {
  189. UINT32 numKeyFrames = compoundCurve->getNumKeyFrames();
  190. Vector<TKeyframe<float>> keyFrames[3];
  191. for (UINT32 i = 0; i < numKeyFrames; i++)
  192. {
  193. const TKeyframe<Vector3>& key = compoundCurve->getKeyFrame(i);
  194. TKeyframe<float> newKey;
  195. newKey.time = key.time;
  196. for (UINT32 j = 0; j < 3; j++)
  197. {
  198. bool addNew = true;
  199. if (i > 0)
  200. {
  201. const TKeyframe<float>& prevKey = keyFrames[j].back();
  202. bool isEqual = Math::approxEquals(prevKey.value, key.value[j]) &&
  203. Math::approxEquals(prevKey.outTangent, key.inTangent[j]);
  204. addNew = !isEqual;
  205. }
  206. if (addNew)
  207. {
  208. newKey.value = key.value[j];
  209. newKey.inTangent = key.inTangent[j];
  210. newKey.outTangent = key.outTangent[j];
  211. keyFrames[j].push_back(newKey);
  212. }
  213. }
  214. }
  215. Vector<SPtr<TAnimationCurve<float>>> output(3);
  216. for (UINT32 i = 0; i < 3; i++)
  217. output[i] = bs_shared_ptr_new<TAnimationCurve<float>>(keyFrames[i]);
  218. return output;
  219. }
  220. SPtr<TAnimationCurve<Vector3>> AnimationUtility::combineCurve(const Vector<SPtr<TAnimationCurve<float>>>& curveComponents)
  221. {
  222. // Find unique keyframe times
  223. Map<float, TKeyframe<Vector3>> keyFrames;
  224. for(UINT32 i = 0; i < 3; i++)
  225. {
  226. if (i >= (UINT32)curveComponents.size())
  227. break;
  228. UINT32 numKeyFrames = curveComponents[i]->getNumKeyFrames();
  229. for (UINT32 j = 0; j < numKeyFrames; j++)
  230. {
  231. const TKeyframe<float>& keyFrame = curveComponents[i]->getKeyFrame(j);
  232. auto iterFind = keyFrames.find(keyFrame.time);
  233. if (iterFind == keyFrames.end())
  234. {
  235. TKeyframe<Vector3> newKeyFrame;
  236. newKeyFrame.time = keyFrame.time;
  237. keyFrames.insert(std::make_pair(keyFrame.time, newKeyFrame));
  238. }
  239. }
  240. }
  241. // Populate keyframe values
  242. Vector<TKeyframe<Vector3>> keyframeList(keyFrames.size());
  243. UINT32 idx = 0;
  244. for(auto& entry : keyFrames)
  245. {
  246. TKeyframe<Vector3>& keyFrame = entry.second;
  247. for(UINT32 j = 0; j < 3; j++)
  248. {
  249. TKeyframe<float> currentKey = curveComponents[j]->evaluateKey(keyFrame.time, false);
  250. keyFrame.value[j] = currentKey.value;
  251. keyFrame.inTangent[j] = currentKey.inTangent;
  252. keyFrame.outTangent[j] = currentKey.outTangent;
  253. }
  254. keyframeList[idx] = keyFrame;
  255. idx++;
  256. }
  257. return bs_shared_ptr_new<TAnimationCurve<Vector3>>(keyframeList);
  258. }
  259. template<class T>
  260. TAnimationCurve<T> AnimationUtility::scaleCurve(const TAnimationCurve<T>& curve, float factor)
  261. {
  262. INT32 numKeys = (INT32)curve.getNumKeyFrames();
  263. Vector<TKeyframe<T>> newKeyframes(numKeys);
  264. for (INT32 i = 0; i < numKeys; i++)
  265. {
  266. const TKeyframe<T>& key = curve.getKeyFrame(i);
  267. newKeyframes[i].time = key.time;
  268. newKeyframes[i].value = key.value * factor;
  269. newKeyframes[i].inTangent = key.inTangent * factor;
  270. newKeyframes[i].outTangent = key.outTangent * factor;
  271. }
  272. return TAnimationCurve<T>(newKeyframes);
  273. }
  274. template<class T>
  275. TAnimationCurve<T> AnimationUtility::offsetCurve(const TAnimationCurve<T>& curve, float offset)
  276. {
  277. INT32 numKeys = (INT32)curve.getNumKeyFrames();
  278. Vector<TKeyframe<T>> newKeyframes(numKeys);
  279. for (INT32 i = 0; i < numKeys; i++)
  280. {
  281. const TKeyframe<T>& key = curve.getKeyFrame(i);
  282. newKeyframes[i].time = key.time + offset;
  283. newKeyframes[i].value = key.value;
  284. newKeyframes[i].inTangent = key.inTangent;
  285. newKeyframes[i].outTangent = key.outTangent;
  286. }
  287. return TAnimationCurve<T>(newKeyframes);
  288. }
  289. template BS_CORE_EXPORT TAnimationCurve<Vector3> AnimationUtility::scaleCurve(const TAnimationCurve<Vector3>& curve, float factor);
  290. template BS_CORE_EXPORT TAnimationCurve<Quaternion> AnimationUtility::scaleCurve(const TAnimationCurve<Quaternion>& curve, float factor);
  291. template BS_CORE_EXPORT TAnimationCurve<float> AnimationUtility::scaleCurve(const TAnimationCurve<float>& curve, float factor);
  292. template BS_CORE_EXPORT TAnimationCurve<Vector3> AnimationUtility::offsetCurve(const TAnimationCurve<Vector3>& curve, float offset);
  293. template BS_CORE_EXPORT TAnimationCurve<Quaternion> AnimationUtility::offsetCurve(const TAnimationCurve<Quaternion>& curve, float offset);
  294. template BS_CORE_EXPORT TAnimationCurve<float> AnimationUtility::offsetCurve(const TAnimationCurve<float>& curve, float offset);
  295. }