diff --git a/Editor/Inspector/MergeArmatureEditor.cs b/Editor/Inspector/MergeArmatureEditor.cs index 3aad6177..31fa9416 100644 --- a/Editor/Inspector/MergeArmatureEditor.cs +++ b/Editor/Inspector/MergeArmatureEditor.cs @@ -116,7 +116,50 @@ namespace nadena.dev.modular_avatar.core.editor { var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(target.mergeTarget.Get(target).transform); var avatarAnimator = avatarRoot != null ? avatarRoot.GetComponent() : null; - HeuristicBoneMapper.RenameBonesByHeuristic(target, avatarAnimator: avatarAnimator); + + // Search Outfit Root Animator + var outfitRoot = ((ModularAvatarMergeArmature)serializedObject.targetObject).transform; + Animator outfitAnimator = null; + while (outfitRoot != null) + { + if (outfitRoot == avatarRoot) + { + outfitAnimator = null; + break; + } + outfitAnimator = outfitRoot.GetComponent(); + if (outfitAnimator != null && outfitAnimator.isHuman) break; + outfitAnimator = null; + outfitRoot = outfitRoot.parent; + } + + if (outfitAnimator != null) + { + var hipsCheck = outfitAnimator.GetBoneTransform(HumanBodyBones.Hips); + if (hipsCheck != null && hipsCheck.parent == outfitRoot.transform) + { + // Sometimes broken rigs can have the hips as a direct child of the root, instead of having + // an intermediate Armature object. We do not currently support this kind of rig, and so we'll + // assume the outfit's humanoid rig is broken and move on to heuristic matching. + outfitAnimator = null; + } else if (hipsCheck == null) { + outfitAnimator = null; + } + } + + Dictionary humanoidBones = null; + if (outfitAnimator != null) + { + humanoidBones = new Dictionary(); + foreach (HumanBodyBones boneIndex in Enum.GetValues(typeof(HumanBodyBones))) + { + var bone = boneIndex != HumanBodyBones.LastBone ? outfitAnimator.GetBoneTransform(boneIndex) : null; + if (bone == null) continue; + humanoidBones[bone] = boneIndex; + } + } + + HeuristicBoneMapper.RenameBonesByHeuristic(target, humanoidBones: humanoidBones, avatarAnimator: avatarAnimator); } }