SkeletalAnimation.cpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Skeleton/SkeletalAnimation.h>
  5. #include <Jolt/Skeleton/SkeletonPose.h>
  6. #include <Jolt/ObjectStream/TypeDeclarations.h>
  7. JPH_NAMESPACE_BEGIN
  8. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::JointState)
  9. {
  10. JPH_ADD_ATTRIBUTE(JointState, mRotation)
  11. JPH_ADD_ATTRIBUTE(JointState, mTranslation)
  12. }
  13. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::Keyframe)
  14. {
  15. JPH_ADD_BASE_CLASS(Keyframe, JointState)
  16. JPH_ADD_ATTRIBUTE(Keyframe, mTime)
  17. }
  18. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::AnimatedJoint)
  19. {
  20. JPH_ADD_ATTRIBUTE(AnimatedJoint, mJointName)
  21. JPH_ADD_ATTRIBUTE(AnimatedJoint, mKeyframes)
  22. }
  23. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation)
  24. {
  25. JPH_ADD_ATTRIBUTE(SkeletalAnimation, mAnimatedJoints)
  26. JPH_ADD_ATTRIBUTE(SkeletalAnimation, mIsLooping)
  27. }
  28. void SkeletalAnimation::JointState::FromMatrix(Mat44Arg inMatrix)
  29. {
  30. mRotation = inMatrix.GetQuaternion();
  31. mTranslation = inMatrix.GetTranslation();
  32. }
  33. float SkeletalAnimation::GetDuration() const
  34. {
  35. if (!mAnimatedJoints.empty() && !mAnimatedJoints[0].mKeyframes.empty())
  36. return mAnimatedJoints[0].mKeyframes.back().mTime;
  37. else
  38. return 0.0f;
  39. }
  40. void SkeletalAnimation::ScaleJoints(float inScale)
  41. {
  42. for (SkeletalAnimation::AnimatedJoint &j : mAnimatedJoints)
  43. for (SkeletalAnimation::Keyframe &k : j.mKeyframes)
  44. k.mTranslation *= inScale;
  45. }
  46. void SkeletalAnimation::Sample(float inTime, SkeletonPose &ioPose) const
  47. {
  48. // Correct time when animation is looping
  49. JPH_ASSERT(inTime >= 0.0f);
  50. float duration = GetDuration();
  51. float time = duration > 0.0f && mIsLooping? fmod(inTime, duration) : inTime;
  52. for (const AnimatedJoint &aj : mAnimatedJoints)
  53. {
  54. // Do binary search for keyframe
  55. int high = (int)aj.mKeyframes.size(), low = -1;
  56. while (high - low > 1)
  57. {
  58. int probe = (high + low) / 2;
  59. if (aj.mKeyframes[probe].mTime < time)
  60. low = probe;
  61. else
  62. high = probe;
  63. }
  64. JointState &state = ioPose.GetJoint(ioPose.GetSkeleton()->GetJointIndex(aj.mJointName));
  65. if (low == -1)
  66. {
  67. // Before first key, return first key
  68. state = static_cast<const JointState &>(aj.mKeyframes.front());
  69. }
  70. else if (high == (int)aj.mKeyframes.size())
  71. {
  72. // Beyond last key, return last key
  73. state = static_cast<const JointState &>(aj.mKeyframes.back());
  74. }
  75. else
  76. {
  77. // Interpolate
  78. const Keyframe &s1 = aj.mKeyframes[low];
  79. const Keyframe &s2 = aj.mKeyframes[low + 1];
  80. float fraction = (time - s1.mTime) / (s2.mTime - s1.mTime);
  81. JPH_ASSERT(fraction >= 0.0f && fraction <= 1.0f);
  82. state.mTranslation = (1.0f - fraction) * s1.mTranslation + fraction * s2.mTranslation;
  83. JPH_ASSERT(s1.mRotation.IsNormalized());
  84. JPH_ASSERT(s2.mRotation.IsNormalized());
  85. state.mRotation = s1.mRotation.SLERP(s2.mRotation, fraction);
  86. JPH_ASSERT(state.mRotation.IsNormalized());
  87. }
  88. }
  89. }
  90. JPH_NAMESPACE_END