BlueRoseNote/03-UnrealEngine/Animation/UE5商城动画重定向插件笔记.md

203 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: UE5商城动画重定向插件笔记
date: 2023-09-05 12:02:29
excerpt:
tags:
- AnimationRetargeting
rating: ⭐
---
# MixamoAnimationRetargeting
主要逻辑位于FMixamoSkeletonRetargeter
- UE4MannequinToMixamo_BoneNamesMapping
- UE4MannequinToMixamo_ChainNamesMapping
- UE5MannequinToMixamo_BoneNamesMapping
- UE5MannequinToMixamo_ChainNamesMapping
重定向逻辑位于FMixamoSkeletonRetargeter::Retarget()
## 生成TPose
```c++
static const TArray<FName> Mixamo_PreserveComponentSpacePose_BoneNames = {
"Head",
"LeftToeBase",
"RightToeBase"
#ifdef MAR_UPPERARMS_PRESERVECS_EXPERIMENTAL_ENABLE_
,"RightShoulder"
,"RightArm" ,"LeftShoulder" ,"LeftArm"#endif
};
static const TArray<TPair<FName, FName>> Mixamo_ParentChildBoneNamesToBypassOneChildConstraint = {
{"LeftUpLeg", "LeftLeg"},
{"LeftLeg", "LeftFoot"},
{"LeftFoot", "LeftToeBase"},
{"LeftToeBase", "LeftToe_End"},
{"RightUpLeg", "RightLeg"},
{"RightLeg", "RightFoot"},
{"RightFoot", "RightToeBase"},
{"RightToeBase", "RightToe_End"},
{"Hips", "Spine"}, // Heuristic to try to align better the part.
{"Spine", "Spine1"},
{"Spine1", "Spine2"},
{"Spine2", "Neck"}, // Heuristic to try to align better the part.
{"Neck", "Head"},
{"Head", "HeadTop_End"},
{"LeftShoulder", "LeftArm"},
{"LeftArm", "LeftForeArm"},
{"LeftForeArm", "LeftHand"},
{"LeftHand", "LeftHandMiddle1"}, // Heuristic to try to align better the part.
{"LeftHandIndex1", "LeftHandIndex2"},
{"LeftHandIndex2", "LeftHandIndex3"},
{"LeftHandIndex3", "LeftHandIndex4"},
{"LeftHandMiddle1", "LeftHandMiddle2"},
{"LeftHandMiddle2", "LeftHandMiddle3"},
{"LeftHandMiddle3", "LeftHandMiddle4"},
{"LeftHandPinky1", "LeftHandPinky2"},
{"LeftHandPinky2", "LeftHandPinky3"},
{"LeftHandPinky3", "LeftHandPinky4"},
{"LeftHandRing1", "LeftHandRing2"},
{"LeftHandRing2", "LeftHandRing3"},
{"LeftHandRing3", "LeftHandRing4"},
{"LeftHandThumb1", "LeftHandThumb2"},
{"LeftHandThumb2", "LeftHandThumb3"},
{"LeftHandThumb3", "LeftHandThumb4"},
{"RightShoulder", "RightArm"},
{"RightArm", "RightForeArm"},
{"RightForeArm", "RightHand"},
{"RightHand", "RightHandMiddle1"}, // Heuristic to try to align better the part.
{"RightHandIndex1", "RightHandIndex2"},
{"RightHandIndex2", "RightHandIndex3"},
{"RightHandIndex3", "RightHandIndex4"},
{"RightHandMiddle1", "RightHandMiddle2"},
{"RightHandMiddle2", "RightHandMiddle3"},
{"RightHandMiddle3", "RightHandMiddle4"},
{"RightHandPinky1", "RightHandPinky2"},
{"RightHandPinky2", "RightHandPinky3"},
{"RightHandPinky3", "RightHandPinky4"},
{"RightHandRing1", "RightHandRing2"},
{"RightHandRing2", "RightHandRing3"},
{"RightHandRing3", "RightHandRing4"},
{"RightHandThumb1", "RightHandThumb2"},
{"RightHandThumb2", "RightHandThumb3"},
{"RightHandThumb3", "RightHandThumb4"}
};
RetargetBasePose(
SkeletalMeshes,
ReferenceSkeleton,
Mixamo_PreserveComponentSpacePose_BoneNames,
UEMannequinToMixamo_BoneNamesMapping.GetInverseMapper(),
Mixamo_ParentChildBoneNamesToBypassOneChildConstraint,
/*bApplyPoseToRetargetBasePose=*/true,
UIKRetargeterController::GetController(IKRetargeter_UEMannequinToMixamo)
);
```
## 判断骨骼结构是否符合要求
```c++
bool FMixamoSkeletonRetargeter::IsMixamoSkeleton(const USkeleton * Skeleton) const
{
// We consider a Skeleton "coming from Mixamo" if it has at least X% of the expected bones.
const float MINIMUM_MATCHING_PERCENTAGE = .75f;
// Convert the array of expected bone names (TODO: cache it...).
TArray<FName> BoneNames;
UE4MannequinToMixamo_BoneNamesMapping.GetDestination(BoneNames);
// Look for and count the known Mixamo bones (see comments on IndexLastCheckedMixamoBone and UEMannequinToMixamo_BonesMapping).
constexpr int32 NumBones = (IndexLastCheckedMixamoBone + 1) / 2;
BoneNames.SetNum(NumBones);
FSkeletonMatcher SkeletonMatcher(BoneNames, MINIMUM_MATCHING_PERCENTAGE);
return SkeletonMatcher.IsMatching(Skeleton);
}
bool FSkeletonMatcher::IsMatching(const USkeleton* Skeleton) const
{
// No Skeleton, No matching...
if (Skeleton == nullptr)
{ return false;
}
const int32 NumExpectedBones = BoneNames.Num();
int32 nMatchingBones = 0;
const FReferenceSkeleton & SkeletonRefSkeleton = Skeleton->GetReferenceSkeleton();
for (int32 i = 0; i < NumExpectedBones; ++i)
{ const int32 BoneIndex = SkeletonRefSkeleton.FindBoneIndex(BoneNames[i]);
if (BoneIndex != INDEX_NONE)
{ ++nMatchingBones;
} } const float MatchedPercentage = float(nMatchingBones) / float(NumExpectedBones);
return MatchedPercentage >= MinimumMatchingPerc;
}
```
enum class ETargetSkeletonType
{
ST_UNKNOWN = 0,
ST_UE4_MANNEQUIN,
ST_UE5_MANNEQUIN,
ST_SIZE
};
static const char* const kUE4MannequinToMixamo_BoneNamesMapping[] = {
// UE Mannequin bone name MIXAMO bone name
"root", "root",
"pelvis", "Hips",
"spine_01", "Spine",
"spine_02", "Spine1",
"spine_03", "Spine2",
"neck_01", "Neck",
"head", "head",
"clavicle_l", "LeftShoulder",
"upperarm_l", "LeftArm",
"lowerarm_l", "LeftForeArm",
"hand_l", "LeftHand",
"clavicle_r", "RightShoulder",
"upperarm_r", "RightArm",
"lowerarm_r", "RightForeArm",
"hand_r", "RightHand",
"thigh_l", "LeftUpLeg",
"calf_l", "LeftLeg",
"foot_l", "LeftFoot",
"ball_l", "LeftToeBase",
"thigh_r", "RightUpLeg",
"calf_r", "RightLeg",
"foot_r", "RightFoot",
"ball_r", "RightToeBase",
// From here, ignored to determine if a skeleton is from Mixamo.
// From here, ignored to determine if a skeleton is from UE Mannequin. "index_01_l", "LeftHandIndex1",
"index_02_l", "LeftHandIndex2",
"index_03_l", "LeftHandIndex3",
"middle_01_l", "LeftHandMiddle1",
"middle_02_l", "LeftHandMiddle2",
"middle_03_l", "LeftHandMiddle3",
"pinky_01_l", "LeftHandPinky1",
"pinky_02_l", "LeftHandPinky2",
"pinky_03_l", "LeftHandPinky3",
"ring_01_l", "LeftHandRing1",
"ring_02_l", "LeftHandRing2",
"ring_03_l", "LeftHandRing3",
"thumb_01_l", "LeftHandThumb1",
"thumb_02_l", "LeftHandThumb2",
"thumb_03_l", "LeftHandThumb3",
"index_01_r", "RightHandIndex1",
"index_02_r", "RightHandIndex2",
"index_03_r", "RightHandIndex3",
"middle_01_r", "RightHandMiddle1",
"middle_02_r", "RightHandMiddle2",
"middle_03_r", "RightHandMiddle3",
"pinky_01_r", "RightHandPinky1",
"pinky_02_r", "RightHandPinky2",
"pinky_03_r", "RightHandPinky3",
"ring_01_r", "RightHandRing1",
"ring_02_r", "RightHandRing2",
"ring_03_r", "RightHandRing3",
"thumb_01_r", "RightHandThumb1",
"thumb_02_r", "RightHandThumb2",
"thumb_03_r", "RightHandThumb3",
// Un-mapped bones (at the moment). Here for reference.
//"lowerarm_twist_01_l", nullptr, //"upperarm_twist_01_l", nullptr, //"lowerarm_twist_01_r", nullptr, //"upperarm_twist_01_r", nullptr, //"calf_twist_01_l", nullptr, //"thigh_twist_01_l", nullptr, //"calf_twist_01_r", nullptr, //"thigh_twist_01_r", nullptr, //"ik_foot_root", nullptr, //"ik_foot_l", nullptr, //"ik_foot_r", nullptr, //"ik_hand_root", nullptr, //"ik_hand_gun", nullptr, //"ik_hand_l", nullptr, //"ik_hand_r", nullptr,};
# EasyPose