mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-01-30 18:22:52 +08:00
fix: avoid name collisions in setup outfit (#435)
The human avatar mapping system seems to use bone _names_ rather than full _paths_ to identify bones. When the avatar armature and an outfit armature are both present under the avatar root, this can result in misidentification of outfit bones as avatar bones on the avatar animator. This in turn results in issues with Bone Proxy's editor-side tracking logic. This change adjusts setup outfit to ensure that there is always a prefix and/or suffix set, renaming bones if necessary. Note that this does not fully use outfit human avatar data to map bones yet; this is mostly intended as a patch to resolve the issues that have been reported recently, particularly around the stricter validations in SDK 3.3.0.
This commit is contained in:
parent
ebda9cf7d5
commit
6cbcde05f4
@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@ -122,14 +124,15 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var avatarArmature = avatarHips.transform.parent;
|
||||
var outfitArmature = outfitHips.transform.parent;
|
||||
|
||||
if (outfitArmature.GetComponent<ModularAvatarMergeArmature>() == null)
|
||||
var merge = outfitArmature.GetComponent<ModularAvatarMergeArmature>();
|
||||
if (merge == null)
|
||||
{
|
||||
var merge = Undo.AddComponent<ModularAvatarMergeArmature>(outfitArmature.gameObject);
|
||||
merge = Undo.AddComponent<ModularAvatarMergeArmature>(outfitArmature.gameObject);
|
||||
merge.mergeTarget = new AvatarObjectReference();
|
||||
merge.mergeTarget.referencePath = RuntimeUtil.RelativePath(avatarRoot, avatarArmature.gameObject);
|
||||
merge.InferPrefixSuffix();
|
||||
HeuristicBoneMapper.RenameBonesByHeuristic(merge);
|
||||
}
|
||||
HeuristicBoneMapper.RenameBonesByHeuristic(merge);
|
||||
|
||||
if (outfitRoot != null
|
||||
&& outfitRoot.GetComponent<ModularAvatarMeshSettings>() == null
|
||||
@ -277,8 +280,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool FindBones(Object obj, out GameObject avatarRoot, out GameObject avatarHips,
|
||||
out GameObject outfitHips)
|
||||
private static bool FindBones(Object obj, out GameObject avatarRoot, out GameObject avatarHips, out GameObject outfitHips)
|
||||
{
|
||||
avatarHips = outfitHips = null;
|
||||
var outfitRoot = obj as GameObject;
|
||||
@ -286,7 +288,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
? RuntimeUtil.FindAvatarInParents(outfitRoot.transform)?.gameObject
|
||||
: null;
|
||||
if (outfitRoot == null || avatarRoot == null) return false;
|
||||
|
||||
|
||||
var avatarAnimator = avatarRoot.GetComponent<Animator>();
|
||||
if (avatarAnimator == null)
|
||||
{
|
||||
@ -297,7 +299,27 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
return false;
|
||||
}
|
||||
|
||||
avatarHips = avatarAnimator.GetBoneTransform(HumanBodyBones.Hips)?.gameObject;
|
||||
var avatarBoneMappings = GetAvatarBoneMappings(avatarAnimator);
|
||||
if (!avatarBoneMappings.ContainsKey(HumanBodyBones.Hips))
|
||||
{
|
||||
errorMessageGroups = new string[]
|
||||
{
|
||||
S("setup_outfit.err.no_hips")
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
// We do an explicit search for the hips bone rather than invoking the animator, as we want to control
|
||||
// traversal order.
|
||||
foreach (var maybeHips in avatarRoot.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
if (maybeHips.name == avatarBoneMappings[HumanBodyBones.Hips] && !maybeHips.IsChildOf(outfitRoot.transform))
|
||||
{
|
||||
avatarHips = maybeHips.gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avatarHips == null)
|
||||
{
|
||||
errorMessageGroups = new string[]
|
||||
@ -323,13 +345,13 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
foreach (Transform tempHip in child)
|
||||
{
|
||||
if (tempHip.name.Contains(avatarHips.name))
|
||||
if (tempHip.name.Contains(avatarBoneMappings[HumanBodyBones.Hips]))
|
||||
{
|
||||
outfitHips = tempHip.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
hipsCandidates.Add(avatarHips.name);
|
||||
hipsCandidates.Add(avatarBoneMappings[HumanBodyBones.Hips]);
|
||||
|
||||
// If that doesn't work out, we'll check for heuristic bone mapper mappings.
|
||||
foreach (var hbm in HeuristicBoneMapper.BoneToNameMap[HumanBodyBones.Hips])
|
||||
@ -366,5 +388,17 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
return avatarHips != null && outfitHips != null;
|
||||
}
|
||||
|
||||
private static ImmutableDictionary<HumanBodyBones, string> GetAvatarBoneMappings(Animator avatarAnimator)
|
||||
{
|
||||
var avatarHuman = avatarAnimator.avatar?.humanDescription.human ?? new HumanBone[0];
|
||||
return avatarHuman
|
||||
.Where(hb => !string.IsNullOrEmpty(hb.boneName))
|
||||
.Select(hb => new KeyValuePair<HumanBodyBones, string>(
|
||||
(HumanBodyBones) Enum.Parse(typeof(HumanBodyBones), hb.humanName.Replace(" ", "")),
|
||||
hb.boneName
|
||||
))
|
||||
.ToImmutableDictionary();
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,146 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
private static readonly Regex PAT_END_NUMBER = new Regex(@"[_\.][0-9]+");
|
||||
|
||||
private static readonly ImmutableDictionary<HumanBodyBones, ImmutableList<HumanBodyBones>> BoneChildren =
|
||||
ImmutableDictionary<HumanBodyBones, ImmutableList<HumanBodyBones>>.Empty
|
||||
.Add(HumanBodyBones.Hips, ImmutableList.Create(
|
||||
HumanBodyBones.LeftUpperLeg,
|
||||
HumanBodyBones.RightUpperLeg,
|
||||
HumanBodyBones.Spine
|
||||
))
|
||||
.Add(HumanBodyBones.LeftUpperLeg, ImmutableList.Create(
|
||||
HumanBodyBones.LeftLowerLeg
|
||||
))
|
||||
.Add(HumanBodyBones.RightUpperLeg, ImmutableList.Create(
|
||||
HumanBodyBones.RightLowerLeg
|
||||
))
|
||||
.Add(HumanBodyBones.LeftLowerLeg, ImmutableList.Create(
|
||||
HumanBodyBones.LeftFoot
|
||||
))
|
||||
.Add(HumanBodyBones.RightLowerLeg, ImmutableList.Create(
|
||||
HumanBodyBones.RightFoot
|
||||
))
|
||||
.Add(HumanBodyBones.LeftFoot, ImmutableList.Create(
|
||||
HumanBodyBones.LeftToes
|
||||
))
|
||||
.Add(HumanBodyBones.RightFoot, ImmutableList.Create(
|
||||
HumanBodyBones.RightToes
|
||||
))
|
||||
.Add(HumanBodyBones.Spine, ImmutableList.Create(
|
||||
HumanBodyBones.Chest,
|
||||
HumanBodyBones.UpperChest
|
||||
))
|
||||
.Add(HumanBodyBones.Chest, ImmutableList.Create(
|
||||
HumanBodyBones.Neck,
|
||||
HumanBodyBones.LeftShoulder,
|
||||
HumanBodyBones.RightShoulder
|
||||
))
|
||||
.Add(HumanBodyBones.UpperChest, ImmutableList.Create(
|
||||
HumanBodyBones.Neck,
|
||||
HumanBodyBones.LeftShoulder,
|
||||
HumanBodyBones.RightShoulder
|
||||
))
|
||||
.Add(HumanBodyBones.Neck, ImmutableList.Create(
|
||||
HumanBodyBones.Head
|
||||
))
|
||||
.Add(HumanBodyBones.Head, ImmutableList.Create(
|
||||
HumanBodyBones.LeftEye,
|
||||
HumanBodyBones.RightEye,
|
||||
HumanBodyBones.Jaw
|
||||
))
|
||||
.Add(HumanBodyBones.LeftShoulder, ImmutableList.Create(
|
||||
HumanBodyBones.LeftUpperArm
|
||||
))
|
||||
.Add(HumanBodyBones.RightShoulder, ImmutableList.Create(
|
||||
HumanBodyBones.RightUpperArm
|
||||
))
|
||||
.Add(HumanBodyBones.LeftUpperArm, ImmutableList.Create(
|
||||
HumanBodyBones.LeftLowerArm
|
||||
))
|
||||
.Add(HumanBodyBones.RightUpperArm, ImmutableList.Create(
|
||||
HumanBodyBones.RightLowerArm
|
||||
))
|
||||
.Add(HumanBodyBones.LeftLowerArm, ImmutableList.Create(
|
||||
HumanBodyBones.LeftHand
|
||||
))
|
||||
.Add(HumanBodyBones.RightLowerArm, ImmutableList.Create(
|
||||
HumanBodyBones.RightHand
|
||||
))
|
||||
.Add(HumanBodyBones.LeftHand, ImmutableList.Create(
|
||||
HumanBodyBones.LeftThumbProximal,
|
||||
HumanBodyBones.LeftIndexProximal,
|
||||
HumanBodyBones.LeftMiddleProximal,
|
||||
HumanBodyBones.LeftRingProximal,
|
||||
HumanBodyBones.LeftLittleProximal
|
||||
))
|
||||
.Add(HumanBodyBones.RightHand, ImmutableList.Create(
|
||||
HumanBodyBones.RightThumbProximal,
|
||||
HumanBodyBones.RightIndexProximal,
|
||||
HumanBodyBones.RightMiddleProximal,
|
||||
HumanBodyBones.RightRingProximal,
|
||||
HumanBodyBones.RightLittleProximal
|
||||
))
|
||||
.Add(HumanBodyBones.LeftThumbProximal, ImmutableList.Create(
|
||||
HumanBodyBones.LeftThumbIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.RightThumbProximal, ImmutableList.Create(
|
||||
HumanBodyBones.RightThumbIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.LeftThumbIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.LeftThumbDistal
|
||||
))
|
||||
.Add(HumanBodyBones.RightThumbIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.RightThumbDistal
|
||||
))
|
||||
.Add(HumanBodyBones.LeftIndexProximal, ImmutableList.Create(
|
||||
HumanBodyBones.LeftIndexIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.RightIndexProximal, ImmutableList.Create(
|
||||
HumanBodyBones.RightIndexIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.LeftIndexIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.LeftIndexDistal
|
||||
))
|
||||
.Add(HumanBodyBones.RightIndexIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.RightIndexDistal
|
||||
))
|
||||
.Add(HumanBodyBones.LeftMiddleProximal, ImmutableList.Create(
|
||||
HumanBodyBones.LeftMiddleIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.RightMiddleProximal, ImmutableList.Create(
|
||||
HumanBodyBones.RightMiddleIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.LeftMiddleIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.LeftMiddleDistal
|
||||
))
|
||||
.Add(HumanBodyBones.RightMiddleIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.RightMiddleDistal
|
||||
))
|
||||
.Add(HumanBodyBones.LeftRingProximal, ImmutableList.Create(
|
||||
HumanBodyBones.LeftRingIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.RightRingProximal, ImmutableList.Create(
|
||||
HumanBodyBones.RightRingIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.LeftRingIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.LeftRingDistal
|
||||
))
|
||||
.Add(HumanBodyBones.RightRingIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.RightRingDistal
|
||||
))
|
||||
.Add(HumanBodyBones.LeftLittleProximal, ImmutableList.Create(
|
||||
HumanBodyBones.LeftLittleIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.RightLittleProximal, ImmutableList.Create(
|
||||
HumanBodyBones.RightLittleIntermediate
|
||||
))
|
||||
.Add(HumanBodyBones.LeftLittleIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.LeftLittleDistal
|
||||
))
|
||||
.Add(HumanBodyBones.RightLittleIntermediate, ImmutableList.Create(
|
||||
HumanBodyBones.RightLittleDistal
|
||||
));
|
||||
|
||||
// This list is originally from https://github.com/HhotateA/AvatarModifyTools/blob/d8ae75fed8577707253d6b63a64d6053eebbe78b/Assets/HhotateA/AvatarModifyTool/Editor/EnvironmentVariable.cs#L81-L139
|
||||
// Copyright (c) 2021 @HhotateA_xR
|
||||
// Licensed under the MIT License
|
||||
@ -359,15 +499,31 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var target = config.mergeTarget.Get(RuntimeUtil.FindAvatarInParents(config.transform));
|
||||
if (target == null) return;
|
||||
|
||||
bool changedSuffix = false;
|
||||
var newSuffix = config.suffix;
|
||||
if (config.prefix == "" && config.suffix == "")
|
||||
{
|
||||
newSuffix = ".1";
|
||||
changedSuffix = true;
|
||||
}
|
||||
|
||||
Traverse(config.transform, target.transform);
|
||||
|
||||
config.suffix = newSuffix;
|
||||
|
||||
if (changedSuffix)
|
||||
{
|
||||
Undo.RecordObject(config, "Applying heuristic mapping");
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(config);
|
||||
}
|
||||
|
||||
void Traverse(Transform src, Transform dst)
|
||||
{
|
||||
var mappings = AssignBoneMappings(config, src.gameObject, dst.gameObject);
|
||||
|
||||
foreach (var pair in mappings)
|
||||
{
|
||||
var newName = config.prefix + pair.Value.gameObject.name + config.suffix;
|
||||
var newName = config.prefix + pair.Value.gameObject.name + newSuffix;
|
||||
var srcGameObj = pair.Key.gameObject;
|
||||
var oldName = srcGameObj.name;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user