From e471df886019955efc8c1624f9e72518943a0f04 Mon Sep 17 00:00:00 2001 From: bd_ Date: Fri, 7 Mar 2025 18:41:26 -0800 Subject: [PATCH] fix: incorrect first layer weights in Merge Animator (#1473) Unity ignores the weight of the first layer in a controller, and forces it to one; here, we replicate that behavior in Merge Animator. Note that we choose to do this here rather than in NDMF - this is because NDMF-level APIs are trying to accurately represent the data in the animator, and should be able to round-trip even "garbage" data there. Closes: https://github.com/bdunderscore/ndmf/issues/534 Closes: #1472 --- Editor/MergeAnimatorProcessor.cs | 4 ++ .../MergeAnimatorTests/MergeSingleTests.cs | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Editor/MergeAnimatorProcessor.cs b/Editor/MergeAnimatorProcessor.cs index 47429d62..0c3b208a 100644 --- a/Editor/MergeAnimatorProcessor.cs +++ b/Editor/MergeAnimatorProcessor.cs @@ -121,6 +121,10 @@ namespace nadena.dev.modular_avatar.core.editor basePath = ""; } + var firstLayer = clonedController.Layers.FirstOrDefault(); + // the first layer in an animator controller always has weight 1.0f (regardless of what is serialized) + if (firstLayer != null) firstLayer.DefaultWeight = 1.0f; + foreach (var l in clonedController.Layers) { if (initialWriteDefaults != null) diff --git a/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs b/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs index 71c5bb8e..56c10b75 100644 --- a/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs +++ b/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs @@ -7,7 +7,9 @@ using nadena.dev.modular_avatar.core.editor; using nadena.dev.ndmf; using nadena.dev.ndmf.animator; using NUnit.Framework; +using UnityEditor.Animations; using UnityEngine; +using VRC.SDK3.Avatars.Components; using BuildContext = nadena.dev.ndmf.BuildContext; namespace UnitTests.MergeAnimatorTests @@ -59,6 +61,45 @@ namespace UnitTests.MergeAnimatorTests var state = FindStateInLayer(findFxLayer(av, "Target"), "Anim1"); Assert.IsTrue(state.motion.name.StartsWith("Anim2")); } + + [Test] + public void MergeAnimation_ForcesFirstLayerToWeightOne() + { + var av = CreateRoot("root"); + + var controller = new AnimatorController(); + var stateMachine = new AnimatorStateMachine(); + stateMachine.name = "test machine"; + + controller.AddLayer(new AnimatorControllerLayer() + { + name = "Base", + stateMachine = stateMachine, + defaultWeight = 0 + }); + + var merge = av.AddComponent(); + merge.animator = controller; + merge.layerType = VRCAvatarDescriptor.AnimLayerType.FX; + + + var ctx = new BuildContext(av, null); + ctx.ActivateExtensionContext(); + ctx.ActivateExtensionContextRecursive(); + + var errors = ErrorReport.CaptureErrors(() => + { + new MergeAnimatorProcessor().OnPreprocessAvatar(av, ctx); + ctx.DeactivateAllExtensionContexts(); + }); + + ctx.DeactivateAllExtensionContexts(); + + Assert.IsEmpty(errors); + + var layer = ((AnimatorController) FindFxController(av).animatorController).layers[^1]; + Assert.AreEqual(1, layer.defaultWeight); + } } }