From 26153ea60dd10f62c34fad080e6f83268e597212 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:52:51 +0900 Subject: [PATCH] feat: improve behavior when called setup outfit for setuped outfit (#1200) --- Editor/SetupOutfit.cs | 101 ++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/Editor/SetupOutfit.cs b/Editor/SetupOutfit.cs index ba69ed9f..c09578c8 100644 --- a/Editor/SetupOutfit.cs +++ b/Editor/SetupOutfit.cs @@ -145,25 +145,54 @@ namespace nadena.dev.modular_avatar.core.editor out var avatarRoot, out var avatarHips, out var outfitHips) ) return; + Undo.SetCurrentGroupName("Setup Outfit"); + var avatarArmature = avatarHips.transform.parent; var outfitArmature = outfitHips.transform.parent; - if (outfitArmature.GetComponent() == null) + var merge = outfitArmature.GetComponent(); + if (merge == null) + { + merge = Undo.AddComponent(outfitArmature.gameObject); + } else { + Undo.RecordObject(merge, ""); + } + + if (merge.mergeTarget == null || merge.mergeTargetObject == null) { - var merge = Undo.AddComponent(outfitArmature.gameObject); merge.mergeTarget = new AvatarObjectReference(); merge.mergeTarget.referencePath = RuntimeUtil.RelativePath(avatarRoot, avatarArmature.gameObject); merge.LockMode = ArmatureLockMode.BaseToMerge; + } + + if (string.IsNullOrEmpty(merge.prefix) && string.IsNullOrEmpty(merge.suffix)) + { merge.InferPrefixSuffix(); + } - List subRoots = new List(); - HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots); + PrefabUtility.RecordPrefabInstancePropertyModifications(merge); - // If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to - // help with this - foreach (var subRoot in subRoots) + List subRoots = new List(); + HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots); + + // If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to + // help with this + foreach (var subRoot in subRoots) + { + var subConfig = subRoot.GetComponent(); + var subConfigMangleNames = false; + if (subConfig == null) + { + subConfig = Undo.AddComponent(subRoot.gameObject); + } + else + { + Undo.RecordObject(subConfig, ""); + subConfigMangleNames = subConfig.mangleNames; + } + + if (subConfig.mergeTarget == null || subConfig.mergeTargetObject == null) { - var subConfig = Undo.AddComponent(subRoot.gameObject); var parentTransform = subConfig.transform.parent; var parentConfig = parentTransform.GetComponentInParent(); var parentMapping = parentConfig.MapBone(parentTransform); @@ -174,34 +203,48 @@ namespace nadena.dev.modular_avatar.core.editor subConfig.LockMode = ArmatureLockMode.BaseToMerge; subConfig.prefix = merge.prefix; subConfig.suffix = merge.suffix; - subConfig.mangleNames = false; + subConfig.mangleNames = subConfigMangleNames; + PrefabUtility.RecordPrefabInstancePropertyModifications(subConfig); } + } - var avatarRootMatchingArmature = avatarRoot.transform.Find(outfitArmature.gameObject.name); - if (merge.prefix == "" && merge.suffix == "" && avatarRootMatchingArmature != null) - { - // We have an armature whose names exactly match the root armature - this can cause some serious - // confusion in Unity's humanoid armature matching system. Fortunately, we can avoid this by - // renaming a bone close to the root; this will ensure the number of matching bones is small, and - // Unity's heuristics (apparently) will choose the base avatar's armature as the "true" armature. - outfitArmature.name += ".1"; + var avatarRootMatchingArmature = avatarRoot.transform.Find(outfitArmature.gameObject.name); + if (merge.prefix == "" && merge.suffix == "" && avatarRootMatchingArmature != null) + { + // We have an armature whose names exactly match the root armature - this can cause some serious + // confusion in Unity's humanoid armature matching system. Fortunately, we can avoid this by + // renaming a bone close to the root; this will ensure the number of matching bones is small, and + // Unity's heuristics (apparently) will choose the base avatar's armature as the "true" armature. + outfitArmature.name += ".1"; - // Also make sure to refresh the avatar's animator humanoid bone cache. - var avatarAnimator = avatarRoot.GetComponent(); - var humanDescription = avatarAnimator.avatar; - avatarAnimator.avatar = null; - // ReSharper disable once Unity.InefficientPropertyAccess - avatarAnimator.avatar = humanDescription; - } + // Also make sure to refresh the avatar's animator humanoid bone cache. + var avatarAnimator = avatarRoot.GetComponent(); + var humanDescription = avatarAnimator.avatar; + avatarAnimator.avatar = null; + // ReSharper disable once Unity.InefficientPropertyAccess + avatarAnimator.avatar = humanDescription; } FixAPose(avatarRoot, outfitArmature); + var meshSettings = outfitRoot.GetComponent(); + var mSInheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.SetOrInherit; + var mSInheritBounds = ModularAvatarMeshSettings.InheritMode.SetOrInherit; if (outfitRoot != null - && outfitRoot.GetComponent() == null + && meshSettings == null && outfitRoot.GetComponentInParent() == null) { - var meshSettings = Undo.AddComponent(outfitRoot.gameObject); + meshSettings = Undo.AddComponent(outfitRoot.gameObject); + } else if (outfitRoot != null && meshSettings != null) { + Undo.RecordObject(meshSettings, ""); + mSInheritProbeAnchor = meshSettings.InheritProbeAnchor; + mSInheritBounds = meshSettings.InheritBounds; + } + + if (meshSettings != null + && (meshSettings.ProbeAnchor == null || meshSettings.ProbeAnchor.Get(meshSettings) == null + || meshSettings.RootBone == null || meshSettings.RootBone.Get(meshSettings) == null)) + { Transform rootBone = null, probeAnchor = null; Bounds bounds = ModularAvatarMeshSettings.DEFAULT_BOUNDS; @@ -217,8 +260,8 @@ namespace nadena.dev.modular_avatar.core.editor rootBone = avatarRoot.transform; } - meshSettings.InheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.SetOrInherit; - meshSettings.InheritBounds = ModularAvatarMeshSettings.InheritMode.SetOrInherit; + meshSettings.InheritProbeAnchor = mSInheritProbeAnchor; + meshSettings.InheritBounds = mSInheritBounds; meshSettings.ProbeAnchor = new AvatarObjectReference(); meshSettings.ProbeAnchor.referencePath = RuntimeUtil.RelativePath(avatarRoot, probeAnchor.gameObject); @@ -226,6 +269,8 @@ namespace nadena.dev.modular_avatar.core.editor meshSettings.RootBone = new AvatarObjectReference(); meshSettings.RootBone.referencePath = RuntimeUtil.RelativePath(avatarRoot, rootBone.gameObject); meshSettings.Bounds = bounds; + + PrefabUtility.RecordPrefabInstancePropertyModifications(meshSettings); } }