feat: support using Humanoid Rig on RenameBonesByHeuristic

This commit is contained in:
Sayamame-beans 2024-09-17 00:50:02 +09:00
parent 9bbec4c86b
commit 97d8261557
3 changed files with 92 additions and 16 deletions

View File

@ -306,7 +306,9 @@ namespace nadena.dev.modular_avatar.core.editor
GameObject src,
GameObject newParent,
List<Transform> skipped = null,
HashSet<Transform> unassigned = null
HashSet<Transform> unassigned = null,
Animator avatarAnimator = null,
Dictionary<Transform, HumanBodyBones> humanoidBones = null
)
{
Dictionary<Transform, Transform> mappings = new Dictionary<Transform, Transform>();
@ -355,21 +357,65 @@ namespace nadena.dev.modular_avatar.core.editor
var childName = child.gameObject.name;
var targetObjectName = childName.Substring(config.prefix.Length,
childName.Length - config.prefix.Length - config.suffix.Length);
if (!NameToBoneMap.TryGetValue(
NormalizeName(targetObjectName), out var bodyBones))
List<HumanBodyBones> bodyBones = null;
var isMapped = false;
if (humanoidBones != null && humanoidBones.TryGetValue(child, out var humanoidBone))
{
if (avatarAnimator != null)
{
var avatarBone = avatarAnimator.GetBoneTransform(humanoidBone);
if (avatarBone != null)
{
mappings[child] = avatarBone;
unassigned.Remove(avatarBone);
lcNameToXform.Remove(NormalizeName(avatarBone.gameObject.name));
isMapped = true;
} else {
bodyBones = new List<HumanBodyBones> { humanoidBone };
}
} else {
bodyBones = new List<HumanBodyBones>() { humanoidBone };
}
}
if (!isMapped && bodyBones == null && !NameToBoneMap.TryGetValue(
NormalizeName(targetObjectName), out bodyBones))
{
continue;
}
foreach (var otherName in bodyBones.SelectMany(bone => BoneToNameMap[bone]))
if (!isMapped)
{
if (lcNameToXform.TryGetValue(otherName, out var targetObject))
foreach (var bodyBone in bodyBones)
{
mappings[child] = targetObject;
unassigned.Remove(targetObject);
lcNameToXform.Remove(otherName.ToLowerInvariant());
break;
if (avatarAnimator != null)
{
var avatarBone = avatarAnimator.GetBoneTransform(bodyBone);
if (avatarBone != null && unassigned.Contains(avatarBone))
{
mappings[child] = avatarBone;
unassigned.Remove(avatarBone);
lcNameToXform.Remove(NormalizeName(avatarBone.gameObject.name));
isMapped = true;
break;
}
}
}
}
if (!isMapped)
{
foreach (var otherName in bodyBones.SelectMany(bone => BoneToNameMap[bone]))
{
if (lcNameToXform.TryGetValue(otherName, out var targetObject))
{
mappings[child] = targetObject;
unassigned.Remove(targetObject);
lcNameToXform.Remove(otherName.ToLowerInvariant());
isMapped = true;
break;
}
}
}
@ -388,7 +434,7 @@ namespace nadena.dev.modular_avatar.core.editor
return mappings;
}
internal static void RenameBonesByHeuristic(ModularAvatarMergeArmature config, List<Transform> skipped = null)
internal static void RenameBonesByHeuristic(ModularAvatarMergeArmature config, List<Transform> skipped = null, Dictionary<Transform, HumanBodyBones> humanoidBones = null, Animator avatarAnimator = null)
{
var target = config.mergeTarget.Get(RuntimeUtil.FindAvatarTransformInParents(config.transform));
if (target == null) return;
@ -399,7 +445,7 @@ namespace nadena.dev.modular_avatar.core.editor
void Traverse(Transform src, Transform dst)
{
var mappings = AssignBoneMappings(config, src.gameObject, dst.gameObject, skipped: skipped);
var mappings = AssignBoneMappings(config, src.gameObject, dst.gameObject, skipped: skipped, humanoidBones: humanoidBones, avatarAnimator: avatarAnimator);
foreach (var pair in mappings)
{

View File

@ -114,7 +114,9 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (GUILayout.Button(G("merge_armature.adjust_names")))
{
HeuristicBoneMapper.RenameBonesByHeuristic(target);
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(target.mergeTarget.Get(target).transform);
var avatarAnimator = avatarRoot != null ? avatarRoot.GetComponent<Animator>() : null;
HeuristicBoneMapper.RenameBonesByHeuristic(target, avatarAnimator: avatarAnimator);
}
}

View File

@ -8,6 +8,7 @@ using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
using static nadena.dev.modular_avatar.core.editor.Localization;
using System;
#endregion
@ -156,8 +157,36 @@ namespace nadena.dev.modular_avatar.core.editor
merge.LockMode = ArmatureLockMode.BaseToMerge;
merge.InferPrefixSuffix();
var outfitAnimator = outfitRoot.GetComponent<Animator>();
if (outfitAnimator != null)
{
var hipsCheck = outfitAnimator.isHuman ? outfitAnimator.GetBoneTransform(HumanBodyBones.Hips) : null;
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<Transform, HumanBodyBones> humanoidBones = null;
if (outfitAnimator != null)
{
humanoidBones = new Dictionary<Transform, HumanBodyBones>();
foreach (HumanBodyBones boneIndex in Enum.GetValues(typeof(HumanBodyBones)))
{
var bone = boneIndex != HumanBodyBones.LastBone ? outfitAnimator.GetBoneTransform(boneIndex) : null;
if (bone == null) continue;
humanoidBones[bone] = boneIndex;
}
}
var avatarAnimator = avatarRoot.GetComponent<Animator>();
List<Transform> subRoots = new List<Transform>();
HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots);
HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots, humanoidBones: humanoidBones, avatarAnimator: avatarAnimator);
// If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to
// help with this
@ -187,7 +216,6 @@ namespace nadena.dev.modular_avatar.core.editor
outfitArmature.name += ".1";
// Also make sure to refresh the avatar's animator humanoid bone cache.
var avatarAnimator = avatarRoot.GetComponent<Animator>();
var humanDescription = avatarAnimator.avatar;
avatarAnimator.avatar = null;
// ReSharper disable once Unity.InefficientPropertyAccess
@ -542,4 +570,4 @@ namespace nadena.dev.modular_avatar.core.editor
return avatarHips != null && outfitHips != null;
}
}
}
}