SkeletalAnimation.cpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt.h>
  4. #include <Skeleton/SkeletalAnimation.h>
  5. #include <Skeleton/SkeletonPose.h>
  6. #include <ObjectStream/TypeDeclarations.h>
  7. namespace JPH {
  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. void SkeletalAnimation::JointState::ToMatrix(Mat44 &outMatrix)
  34. {
  35. outMatrix = Mat44::sRotationTranslation(mRotation, mTranslation);
  36. }
  37. float SkeletalAnimation::GetDuration() const
  38. {
  39. if (!mAnimatedJoints.empty() && !mAnimatedJoints[0].mKeyframes.empty())
  40. return mAnimatedJoints[0].mKeyframes.back().mTime;
  41. else
  42. return 0.0f;
  43. }
  44. void SkeletalAnimation::ScaleJoints(float inScale)
  45. {
  46. for (SkeletalAnimation::AnimatedJoint &j : mAnimatedJoints)
  47. for (SkeletalAnimation::Keyframe &k : j.mKeyframes)
  48. k.mTranslation *= inScale;
  49. }
  50. void SkeletalAnimation::Sample(float inTime, SkeletonPose &ioPose) const
  51. {
  52. // Correct time when animation is looping
  53. JPH_ASSERT(inTime >= 0.0f);
  54. float duration = GetDuration();
  55. float time = duration > 0.0f && mIsLooping? fmod(inTime, duration) : inTime;
  56. for (const AnimatedJoint &aj : mAnimatedJoints)
  57. {
  58. // Do binary search for keyframe
  59. int high = (int)aj.mKeyframes.size(), low = -1;
  60. while (high - low > 1)
  61. {
  62. int probe = (high + low) / 2;
  63. if (aj.mKeyframes[probe].mTime < time)
  64. low = probe;
  65. else
  66. high = probe;
  67. }
  68. JointState &state = ioPose.GetJoint(ioPose.GetSkeleton()->GetJointIndex(aj.mJointName));
  69. if (low == -1)
  70. {
  71. // Before first key, return first key
  72. state = static_cast<const JointState &>(aj.mKeyframes.front());
  73. }
  74. else if (high == (int)aj.mKeyframes.size())
  75. {
  76. // Beyond last key, return last key
  77. state = static_cast<const JointState &>(aj.mKeyframes.back());
  78. }
  79. else
  80. {
  81. // Interpolate
  82. const Keyframe &s1 = aj.mKeyframes[low];
  83. const Keyframe &s2 = aj.mKeyframes[low + 1];
  84. float fraction = (time - s1.mTime) / (s2.mTime - s1.mTime);
  85. JPH_ASSERT(fraction >= 0.0f && fraction <= 1.0f);
  86. state.mTranslation = (1.0f - fraction) * s1.mTranslation + fraction * s2.mTranslation;
  87. JPH_ASSERT(s1.mRotation.IsNormalized());
  88. JPH_ASSERT(s2.mRotation.IsNormalized());
  89. state.mRotation = s1.mRotation.SLERP(s2.mRotation, fraction);
  90. JPH_ASSERT(state.mRotation.IsNormalized());
  91. }
  92. }
  93. }
  94. } // JPH