SkeletonMapper.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // SPDX-FileCopyrightText: 2022 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Core/Reference.h>
  5. #include <Jolt/Skeleton/Skeleton.h>
  6. JPH_NAMESPACE_BEGIN
  7. /// Class that is able to map a low detail (ragdoll) skeleton to a high detail (animation) skeleton and vice versa
  8. class SkeletonMapper : public RefTarget<SkeletonMapper>
  9. {
  10. public:
  11. /// A joint that maps 1-on-1 to a joint in the other skeleton
  12. class Mapping
  13. {
  14. public:
  15. Mapping() = default;
  16. Mapping(int inJointIdx1, int inJointIdx2, Mat44Arg inJoint1To2) : mJointIdx1(inJointIdx1), mJointIdx2(inJointIdx2), mJoint1To2(inJoint1To2), mJoint2To1(inJoint1To2.Inversed()) { }
  17. int mJointIdx1; ///< Index of joint from skeleton 1
  18. int mJointIdx2; ///< Corresponding index of joint from skeleton 2
  19. Mat44 mJoint1To2; ///< Transforms this joint from skeleton 1 to 2
  20. Mat44 mJoint2To1; ///< Inverse of the transform above
  21. };
  22. /// A joint chain that starts with a 1-on-1 mapped joint and ends with a 1-on-1 mapped joint with intermediate joints that cannot be mapped
  23. class Chain
  24. {
  25. public:
  26. Chain() = default;
  27. Chain(Array<int> &&inJointIndices1, Array<int> &&inJointIndices2) : mJointIndices1(move(inJointIndices1)), mJointIndices2(move(inJointIndices2)) { }
  28. Array<int> mJointIndices1; ///< Joint chain from skeleton 1
  29. Array<int> mJointIndices2; ///< Corresponding joint chain from skeleton 2
  30. };
  31. /// Joints that could not be mapped from skeleton 1 to 2
  32. class Unmapped
  33. {
  34. public:
  35. Unmapped() = default;
  36. Unmapped(int inJointIdx, int inParentJointIdx) : mJointIdx(inJointIdx), mParentJointIdx(inParentJointIdx) { }
  37. int mJointIdx; ///< Joint index of unmappable joint
  38. int mParentJointIdx; ///< Parent joint index of unmappable joint
  39. };
  40. /// A function that is called to determine if a joint can be mapped from source to target skeleton
  41. using CanMapJoint = function<bool (const Skeleton *, int, const Skeleton *, int)>;
  42. /// Default function that checks if the names of the joints are equal
  43. static bool sDefaultCanMapJoint(const Skeleton *inSkeleton1, int inIndex1, const Skeleton *inSkeleton2, int inIndex2)
  44. {
  45. return inSkeleton1->GetJoint(inIndex1).mName == inSkeleton2->GetJoint(inIndex2).mName;
  46. }
  47. /// Initialize the skeleton mapper. Skeleton 1 should be the (low detail) ragdoll skeleton and skeleton 2 the (high detail) animation skeleton.
  48. /// We assume that each joint in skeleton 1 can be mapped to a joint in skeleton 2 (if not mapping from animation skeleton to ragdoll skeleton will be undefined).
  49. /// Skeleton 2 should have the same hierarchy as skeleton 1 but can contain extra joints between those in skeleton 1 and it can have extra joints at the root and leaves of the skeleton.
  50. /// @param inSkeleton1 Source skeleton to map from.
  51. /// @param inNeutralPose1 Neutral pose of the source skeleton (model space)
  52. /// @param inSkeleton2 Target skeleton to map to.
  53. /// @param inNeutralPose2 Neutral pose of the target skeleton (model space), inNeutralPose1 and inNeutralPose2 must match as closely as possible, preferably the position of the mappable joints should be identical.
  54. void Initialize(const Skeleton *inSkeleton1, const Mat44 *inNeutralPose1, const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2, const CanMapJoint &inCanMapJoint = sDefaultCanMapJoint);
  55. /// Map a pose. Joints that were directly mappable will be copied in model space from pose 1 to pose 2. Any joints that are only present in skeleton 2
  56. /// will get their model space transform calculated through the local space transforms of pose 2. Joints that are part of a joint chain between two
  57. /// mapped joints will be reoriented towards the next joint in skeleton 1. This means that it is possible for unmapped joints to have some animation,
  58. /// but very extreme animation poses will show artifacts.
  59. /// @param inPose1ModelSpace Pose on skeleton 1 in model space
  60. /// @param inPose2LocalSpace Pose on skeleton 2 in local space (used for the joints that cannot be mapped)
  61. /// @param outPose2ModelSpace Model space pose on skeleton 2 (the output of the mapping)
  62. void Map(const Mat44 *inPose1ModelSpace, const Mat44 *inPose2LocalSpace, Mat44 *outPose2ModelSpace) const;
  63. /// Reverse map a pose, this will only use the mappings and not the chains (it assumes that all joints in skeleton 1 are mapped)
  64. /// @param inPose2ModelSpace
  65. /// @param outPose1ModelSpace
  66. void MapReverse(const Mat44 *inPose2ModelSpace, Mat44 *outPose1ModelSpace) const;
  67. using MappingVector = Array<Mapping>;
  68. using ChainVector = Array<Chain>;
  69. using UnmappedVector = Array<Unmapped>;
  70. ///@name Access to the mapped joints
  71. ///@{
  72. const MappingVector & GetMappings() const { return mMappings; }
  73. MappingVector & GetMappings() { return mMappings; }
  74. const ChainVector & GetChains() const { return mChains; }
  75. ChainVector & GetChains() { return mChains; }
  76. const UnmappedVector & GetUnmapped() const { return mUnmapped; }
  77. UnmappedVector & GetUnmapped() { return mUnmapped; }
  78. ///@}
  79. private:
  80. /// Joint mappings
  81. MappingVector mMappings;
  82. ChainVector mChains;
  83. UnmappedVector mUnmapped; ///< Joint indices that could not be mapped from 1 to 2 (these are indices in 2)
  84. };
  85. JPH_NAMESPACE_END