From 838f1dac7ea3c500c2d9037e461cacc9989fb46e Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 1 Oct 2024 20:01:03 -0700
Subject: [PATCH 01/39] fix: ignore blendtree-only layers when determing
 animator WD state

---
 Editor/MergeAnimatorProcessor.cs | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Editor/MergeAnimatorProcessor.cs b/Editor/MergeAnimatorProcessor.cs
index 50efd939..b0af81ac 100644
--- a/Editor/MergeAnimatorProcessor.cs
+++ b/Editor/MergeAnimatorProcessor.cs
@@ -246,6 +246,18 @@ namespace nadena.dev.modular_avatar.core.editor
             var stateMachineQueue = new Queue<AnimatorStateMachine>();
             foreach (var layer in controller.layers)
             {
+                // Special case: A layer with a single state, which contains a blend tree, is ignored for WD analysis.
+                // This is because WD ON blend trees have different behavior from most WD ON states, and can be safely
+                // used in a WD OFF animator.
+
+                if (layer.stateMachine.states.Length == 1
+                    && layer.stateMachine.states[0].state.motion is BlendTree
+                    && layer.stateMachine.stateMachines.Length == 0
+                   )
+                {
+                    continue;
+                }
+                
                 stateMachineQueue.Enqueue(layer.stateMachine);
             }
 

From cb2afcc3d51114781f1fd99a5ac034405221d4bc Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 1 Oct 2024 19:52:25 -0700
Subject: [PATCH 02/39] fix: Menu Installers on the same object as Merge
 Armature are not processed

Changes to pass ordering caused Merge Armature to destroy menus before being
processed by Menu Installer; fix this by hoisting menu generation to occur within
the animation services context, before Merge Armature runs. This is safe because
the menu installer pass does not interact with the avatar's animator controllers
directly.
---
 Editor/PluginDefinition/PluginDefinition.cs   |  8 +++++-
 .../VirtualMenuTests/VirtualMenuTests.cs      | 26 +++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/Editor/PluginDefinition/PluginDefinition.cs b/Editor/PluginDefinition/PluginDefinition.cs
index 69f179d7..2d258b23 100644
--- a/Editor/PluginDefinition/PluginDefinition.cs
+++ b/Editor/PluginDefinition/PluginDefinition.cs
@@ -59,6 +59,13 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
                 {
                     seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
                         .PreviewingWith(new ShapeChangerPreview(), new ObjectSwitcherPreview(), new MaterialSetterPreview());
+#if MA_VRCSDK3_AVATARS
+                    // TODO: We currently run this above MergeArmaturePlugin, because Merge Armature might destroy
+                    // game objects which contain Menu Installers. It'd probably be better however to teach Merge Armature
+                    // to retain those objects? maybe?
+                    seq.Run(MenuInstallPluginPass.Instance);
+#endif
+                    
                     seq.Run(MergeArmaturePluginPass.Instance);
                     seq.Run(BoneProxyPluginPass.Instance);
                     seq.Run(VisibleHeadAccessoryPluginPass.Instance);
@@ -73,7 +80,6 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
                     seq.Run(ConstraintConverterPass.Instance);
                 });
 #if MA_VRCSDK3_AVATARS
-                seq.Run(MenuInstallPluginPass.Instance);
                 seq.Run(PhysbonesBlockerPluginPass.Instance);
                 seq.Run("Fixup Expressions Menu", ctx =>
                 {
diff --git a/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs b/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs
index 81a9ffa3..5450dba0 100644
--- a/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs
+++ b/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs
@@ -698,6 +698,32 @@ namespace modular_avatar_tests.VirtualMenuTests
             Assert.AreEqual(4, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[5].subParameters.Length);
         }
 
+        [Test]
+        public void MergeArmatureAndMenuInstallerOnSameObjectWorks()
+        {
+            var root = CreateRoot("root");
+            var armature = CreateChild(root, "Armature");
+            var installer = armature.AddComponent<ModularAvatarMenuInstaller>();
+            
+            var merge = installer.gameObject.AddComponent<ModularAvatarMergeArmature>();
+            merge.mergeTarget.Set(root);
+            
+            var menu = Create<VRCExpressionsMenu>();
+            menu.controls.Add(new VRCExpressionsMenu.Control()
+            {
+                name = "control",
+                type = VRCExpressionsMenu.Control.ControlType.Toggle
+            });
+            
+            installer.menuToAppend = menu;
+            
+            AvatarProcessor.ProcessAvatar(root);
+
+            var realizedMenu = root.GetComponent<VRCAvatarDescriptor>().expressionsMenu;
+            Assert.AreEqual(1, realizedMenu.controls.Count);
+            Assert.AreEqual("control", realizedMenu.controls[0].name);
+        }
+
         ModularAvatarMenuInstaller CreateInstaller(string name)
         {
             GameObject obj = new GameObject();

From f85d455c8f5f3d785f10959c2935ab9668ae7390 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 1 Oct 2024 20:04:12 -0700
Subject: [PATCH 03/39] fix: NRE issued when viewing Menu Item editor outside
 of an avatar

---
 Editor/Inspector/Menu/MenuItemGUI.cs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Editor/Inspector/Menu/MenuItemGUI.cs b/Editor/Inspector/Menu/MenuItemGUI.cs
index c89c3817..30439a32 100644
--- a/Editor/Inspector/Menu/MenuItemGUI.cs
+++ b/Editor/Inspector/Menu/MenuItemGUI.cs
@@ -646,6 +646,9 @@ namespace nadena.dev.modular_avatar.core.editor
             var myMenuItem = serializedObject.targetObject as ModularAvatarMenuItem;
             if (myMenuItem == null) return null;
 
+            var avatarRoot = RuntimeUtil.FindAvatarInParents(myMenuItem.gameObject.transform);
+            if (avatarRoot == null) return null;
+
             var myParameterName = myMenuItem.Control.parameter.name;
             if (string.IsNullOrEmpty(myParameterName)) return new List<ModularAvatarMenuItem>();
 
@@ -653,7 +656,6 @@ namespace nadena.dev.modular_avatar.core.editor
             if (myMappings.TryGetValue((ParameterNamespace.Animator, myParameterName), out var myReplacement))
                 myParameterName = myReplacement.ParameterName;
 
-            var avatarRoot = RuntimeUtil.FindAvatarInParents(myMenuItem.gameObject.transform);
             var siblings = new List<ModularAvatarMenuItem>();
 
             foreach (var otherMenuItem in avatarRoot.GetComponentsInChildren<ModularAvatarMenuItem>(true))

From 8150e05dd04573be68bcf3e346eb1f8e1d2a0b09 Mon Sep 17 00:00:00 2001
From: nadena-dev-ci <bd_+ci-bot@nadena.dev>
Date: Wed, 2 Oct 2024 10:44:23 +0900
Subject: [PATCH 04/39] New translations en-us.json (Chinese Traditional)

---
 Editor/Localization/zh-Hant.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Editor/Localization/zh-Hant.json b/Editor/Localization/zh-Hant.json
index 21cd5a61..bdc8b921 100644
--- a/Editor/Localization/zh-Hant.json
+++ b/Editor/Localization/zh-Hant.json
@@ -251,7 +251,7 @@
   "reactive_object.inverse": "反轉條件",
   "reactive_object.material-setter.set-to": "將材質設定為:",
   "menuitem.misc.add_toggle": "新增開關",
-  "ro_sim.open_debugger_button": "開啟響應除錯工具",
+  "ro_sim.open_debugger_button": "開啟 Reaction 除錯工具",
   "ro_sim.window.title": "MA 響應除錯工具",
   "ro_sim.header.inspecting": "檢視物件",
   "ro_sim.header.clear_overrides": "清除所有覆寫",

From 2c9939dea8b4292273beec3aa31ebf820348645d Mon Sep 17 00:00:00 2001
From: nekobako <nekobako.tofu@gmail.com>
Date: Mon, 30 Sep 2024 19:35:32 +0900
Subject: [PATCH 05/39] chore: remove ShapeChanger migration for beta only

---
 .../ModularAvatarShapeChanger.cs              | 39 -------------------
 .../InitialStates/SCDefaultAnimation.prefab   | 28 +++++++++----
 2 files changed, 20 insertions(+), 47 deletions(-)

diff --git a/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs b/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs
index d8e1dea5..fd54e96f 100644
--- a/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs
+++ b/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs
@@ -60,11 +60,6 @@ namespace nadena.dev.modular_avatar.core
     [HelpURL("https://modular-avatar.nadena.dev/docs/reference/shape-changer?lang=auto")]
     public class ModularAvatarShapeChanger : ReactiveComponent, IHaveObjReferences
     {
-        // Migration field to help with 1.10-beta series avatar data. Since this was never in a released version of MA,
-        // this migration support will be removed in 1.10.0.
-        [SerializeField] [FormerlySerializedAs("targetRenderer")] [HideInInspector]
-        private AvatarObjectReference m_targetRenderer = new();
-
         [SerializeField] [FormerlySerializedAs("Shapes")]
         private List<ChangedShape> m_shapes = new();
 
@@ -82,40 +77,6 @@ namespace nadena.dev.modular_avatar.core
             }
         }
 
-        private void OnEnable()
-        {
-            MigrateTargetRenderer();
-        }
-
-        protected override void OnValidate()
-        {
-            base.OnValidate();
-            MigrateTargetRenderer();
-        }
-
-        // Migrate early versions of MASC (from Modular Avatar 1.10.0-beta.4 or earlier) to the new format, where the
-        // target renderer is stored separately for each shape.
-        // This logic will be removed in 1.10.0.
-        private void MigrateTargetRenderer()
-        {
-            // Note: This method runs in the context of OnValidate, and therefore cannot touch any other unity objects.
-            if (!string.IsNullOrEmpty(m_targetRenderer.referencePath) || m_targetRenderer.targetObject != null)
-            {
-                foreach (var shape in m_shapes)
-                {
-                    if (shape.Object == null) shape.Object = new AvatarObjectReference();
-                    
-                    if (string.IsNullOrEmpty(shape.Object.referencePath) && shape.Object.targetObject == null)
-                    {
-                        shape.Object.referencePath = m_targetRenderer.referencePath;
-                        shape.Object.targetObject = m_targetRenderer.targetObject;
-                    }
-                }
-                m_targetRenderer.referencePath = null;
-                m_targetRenderer.targetObject = null;
-            }
-        }
-
         public IEnumerable<AvatarObjectReference> GetObjectReferences()
         {
             foreach (var shape in m_shapes)
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab
index f6431564..71de2608 100644
--- a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab
@@ -46,10 +46,13 @@ MonoBehaviour:
   m_EditorClassIdentifier: 
   m_inverted: 0
   m_targetRenderer:
-    referencePath: test mesh
+    referencePath: 
     targetObject: {fileID: 0}
   m_shapes:
-  - ShapeName: key2
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key2
     ChangeType: 0
     Value: 100
 --- !u!1 &2598725701317979415
@@ -98,10 +101,13 @@ MonoBehaviour:
   m_EditorClassIdentifier: 
   m_inverted: 0
   m_targetRenderer:
-    referencePath: test mesh
+    referencePath: 
     targetObject: {fileID: 0}
   m_shapes:
-  - ShapeName: key1
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key1
     ChangeType: 1
     Value: 10
 --- !u!1 &2845086157653980983
@@ -150,10 +156,13 @@ MonoBehaviour:
   m_EditorClassIdentifier: 
   m_inverted: 0
   m_targetRenderer:
-    referencePath: test mesh
+    referencePath: 
     targetObject: {fileID: 0}
   m_shapes:
-  - ShapeName: key3
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key3
     ChangeType: 1
     Value: 100
 --- !u!1 &6385483934583485188
@@ -204,10 +213,13 @@ MonoBehaviour:
   m_EditorClassIdentifier: 
   m_inverted: 0
   m_targetRenderer:
-    referencePath: test mesh
+    referencePath: 
     targetObject: {fileID: 0}
   m_shapes:
-  - ShapeName: key1
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key1
     ChangeType: 1
     Value: 20
 --- !u!114 &2918390808850211981

From 4b9d1128c6f8b8fb233669ad7eb86e99a2078bde Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 1 Oct 2024 20:09:47 -0700
Subject: [PATCH 06/39] chore: set harmony ID on UnpatchAll call

---
 Editor/HarmonyPatches/PatchLoader.cs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Editor/HarmonyPatches/PatchLoader.cs b/Editor/HarmonyPatches/PatchLoader.cs
index 0ce7ffa4..b368fca7 100644
--- a/Editor/HarmonyPatches/PatchLoader.cs
+++ b/Editor/HarmonyPatches/PatchLoader.cs
@@ -11,6 +11,8 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
 {
     internal class PatchLoader
     {
+        private const string HarmonyId = "nadena.dev.modular_avatar";
+
         private static readonly Action<Harmony>[] patches = new Action<Harmony>[]
         {
             //HierarchyViewPatches.Patch,
@@ -19,7 +21,7 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
         [InitializeOnLoadMethod]
         static void ApplyPatches()
         {
-            var harmony = new Harmony("nadena.dev.modular_avatar");
+            var harmony = new Harmony(HarmonyId);
 
             foreach (var patch in patches)
             {
@@ -33,7 +35,7 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
                 }
             }
 
-            AssemblyReloadEvents.beforeAssemblyReload += () => { harmony.UnpatchAll(); };
+            AssemblyReloadEvents.beforeAssemblyReload += () => { harmony.UnpatchAll(HarmonyId); };
         }
     }
 }
\ No newline at end of file

From 1c29af20fbceda1643d0c2872e97044e98789e3d Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 1 Oct 2024 20:14:26 -0700
Subject: [PATCH 07/39] docs: fix incorrect docusaurus directive

---
 docs~/docs/reference/reaction/debugger/index.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/docs~/docs/reference/reaction/debugger/index.md b/docs~/docs/reference/reaction/debugger/index.md
index 6cbfbf47..2eae1191 100644
--- a/docs~/docs/reference/reaction/debugger/index.md
+++ b/docs~/docs/reference/reaction/debugger/index.md
@@ -1,5 +1,4 @@
-
----
+---
 sidebar_position: 900
 ---
 

From 6c551858959a2ea88d9c3ecdf3745b8a0532a916 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 1 Oct 2024 20:11:57 -0700
Subject: [PATCH 08/39] docs: fix reactive component help URL link

---
 Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs | 2 +-
 Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs   | 2 +-
 Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs b/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs
index 0a919dd0..dfe2cf7d 100644
--- a/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs
+++ b/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs
@@ -38,7 +38,7 @@ namespace nadena.dev.modular_avatar.core
     }
     
     [AddComponentMenu("Modular Avatar/MA Material Setter")]
-    [HelpURL("https://modular-avatar.nadena.dev/docs/reference/material-setter?lang=auto")]
+    [HelpURL("https://modular-avatar.nadena.dev/docs/reference/reaction/material-setter?lang=auto")]
     public class ModularAvatarMaterialSetter : ReactiveComponent, IHaveObjReferences
     {
         [SerializeField] private List<MaterialSwitchObject> m_objects = new();
diff --git a/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs b/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs
index 93b1421a..74cc69a8 100644
--- a/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs
+++ b/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs
@@ -21,7 +21,7 @@ namespace nadena.dev.modular_avatar.core
     }
 
     [AddComponentMenu("Modular Avatar/MA Object Toggle")]
-    [HelpURL("https://modular-avatar.nadena.dev/docs/reference/object-toggle?lang=auto")]
+    [HelpURL("https://modular-avatar.nadena.dev/docs/reference/reaction/object-toggle?lang=auto")]
     public class ModularAvatarObjectToggle : ReactiveComponent, IHaveObjReferences
     {
         [SerializeField] private List<ToggledObject> m_objects = new();
diff --git a/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs b/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs
index fd54e96f..4fff480b 100644
--- a/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs
+++ b/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs
@@ -57,7 +57,7 @@ namespace nadena.dev.modular_avatar.core
     }
 
     [AddComponentMenu("Modular Avatar/MA Shape Changer")]
-    [HelpURL("https://modular-avatar.nadena.dev/docs/reference/shape-changer?lang=auto")]
+    [HelpURL("https://modular-avatar.nadena.dev/docs/reference/reaction/shape-changer?lang=auto")]
     public class ModularAvatarShapeChanger : ReactiveComponent, IHaveObjReferences
     {
         [SerializeField] [FormerlySerializedAs("Shapes")]

From 36e035c8c70be094e41550e1f8fa2b253204430b Mon Sep 17 00:00:00 2001
From: nekobako <nekobako.tofu@gmail.com>
Date: Thu, 3 Oct 2024 11:03:03 +0900
Subject: [PATCH 09/39] =?UTF-8?q?fix:=20inverted=20reactive=20components?=
 =?UTF-8?q?=20on=20inactive=20objects=20didn't=20set=20defa=E2=80=A6=20(#1?=
 =?UTF-8?q?246)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: inverted reactive components on inactive objects didn't set default states

* chore: use `InitiallyActive` helper

---------

Co-authored-by: bd_ <bd_@nadena.dev>
---
 .../ReactiveObjectAnalyzer.cs                 |   2 +-
 .../ReactiveObjectPrepass.cs                  |   6 +-
 .../InitialStates/SCDefaultAnimation.cs       |  19 +-
 .../SCDefaultAnimationInactive.prefab         | 668 ++++++++++++++++++
 .../SCDefaultAnimationInactive.prefab.meta    |   7 +
 .../SCDefaultAnimationInverted.prefab         | 668 ++++++++++++++++++
 .../SCDefaultAnimationInverted.prefab.meta    |   7 +
 7 files changed, 1372 insertions(+), 5 deletions(-)
 create mode 100644 UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab
 create mode 100644 UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta
 create mode 100644 UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab
 create mode 100644 UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta

diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
index b7482b27..defcc3c1 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
@@ -287,7 +287,7 @@ namespace nadena.dev.modular_avatar.core.editor
                 }
                 
                 var deletions = info.actionGroups.Where(agk => agk.IsDelete).ToList();
-                if (deletions.Any(d => d.ControllingConditions.All(c => c.IsConstantActive)))
+                if (deletions.Any(d => d.InitiallyActive))
                 {
                     // always deleted
                     shapes.Remove(key);
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs
index 37e0de57..82c5faa8 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs
@@ -14,11 +14,11 @@ namespace nadena.dev.modular_avatar.core.editor
 
         protected override void Execute(ndmf.BuildContext context)
         {
-            var hasShapeChanger = context.AvatarRootObject.GetComponentInChildren<ModularAvatarShapeChanger>() != null;
+            var hasShapeChanger = context.AvatarRootObject.GetComponentInChildren<ModularAvatarShapeChanger>(true) != null;
             var hasObjectSwitcher =
-                context.AvatarRootObject.GetComponentInChildren<ModularAvatarObjectToggle>() != null;
+                context.AvatarRootObject.GetComponentInChildren<ModularAvatarObjectToggle>(true) != null;
             var hasMaterialSetter =
-                context.AvatarRootObject.GetComponentInChildren<ModularAvatarMaterialSetter>() != null;
+                context.AvatarRootObject.GetComponentInChildren<ModularAvatarMaterialSetter>(true) != null;
             if (hasShapeChanger || hasObjectSwitcher || hasMaterialSetter)
             {
                 var clip = new AnimationClip();
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs
index 13be9ca7..8a3f9ad6 100644
--- a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs
@@ -14,7 +14,24 @@ namespace ShapeChangerTests
         [Test]
         public void SetsCorrectInitialStatesAndAnimations()
         {
-            var root = CreatePrefab("SCDefaultAnimation.prefab");
+            SetsCorrectInitialStatesAndAnimations("SCDefaultAnimation.prefab");
+        }
+
+        [Test]
+        public void SetsCorrectInitialStatesAndAnimationsForInactiveSC()
+        {
+            SetsCorrectInitialStatesAndAnimations("SCDefaultAnimationInactive.prefab");
+        }
+
+        [Test]
+        public void SetsCorrectInitialStatesAndAnimationsForInvertedSC()
+        {
+            SetsCorrectInitialStatesAndAnimations("SCDefaultAnimationInverted.prefab");
+        }
+
+        private void SetsCorrectInitialStatesAndAnimations(string prefabPath)
+        {
+            var root = CreatePrefab(prefabPath);
             AvatarProcessor.ProcessAvatar(root);
 
             var fx = (AnimatorController) FindFxController(root).animatorController;
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab
new file mode 100644
index 00000000..e7640290
--- /dev/null
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab
@@ -0,0 +1,668 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1307328145036867423
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7471115643889882934}
+  - component: {fileID: 2015798673852064281}
+  m_Layer: 0
+  m_Name: AlwaysOffDelete
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &7471115643889882934
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1307328145036867423}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2015798673852064281
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1307328145036867423}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key2
+    ChangeType: 0
+    Value: 100
+--- !u!1 &2598725701317979415
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1861262250770563182}
+  - component: {fileID: 8866671501173891171}
+  m_Layer: 0
+  m_Name: Toggled
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &1861262250770563182
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2598725701317979415}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8866671501173891171
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2598725701317979415}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key1
+    ChangeType: 1
+    Value: 10
+--- !u!1 &2845086157653980983
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 110069860838053623}
+  - component: {fileID: 8218581995269956798}
+  m_Layer: 0
+  m_Name: AlwaysOffSet
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &110069860838053623
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2845086157653980983}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8218581995269956798
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2845086157653980983}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key3
+    ChangeType: 1
+    Value: 100
+--- !u!1 &6385483934583485188
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 569625391371299408}
+  - component: {fileID: 3841502665919975468}
+  - component: {fileID: 2918390808850211981}
+  - component: {fileID: 664065153831629983}
+  m_Layer: 0
+  m_Name: InitialOffToggled
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &569625391371299408
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3841502665919975468
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 0
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key1
+    ChangeType: 1
+    Value: 20
+--- !u!114 &2918390808850211981
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Control:
+    name: 
+    icon: {fileID: 0}
+    type: 102
+    parameter:
+      name: 
+    value: 1
+    style: 0
+    subMenu: {fileID: 0}
+    subParameters: []
+    labels: []
+  MenuSource: 1
+  menuSource_otherObjectChildren: {fileID: 0}
+  isSynced: 1
+  isSaved: 1
+  isDefault: 0
+  automaticValue: 0
+--- !u!114 &664065153831629983
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 7ef83cb0c23d4d7c9d41021e544a1978, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  menuToAppend: {fileID: 0}
+  installTargetMenu: {fileID: 0}
+--- !u!1 &6855505756433160176
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8936933457054072598}
+  - component: {fileID: 6580323041906195452}
+  - component: {fileID: 146169679456758165}
+  - component: {fileID: 5146811121193962360}
+  m_Layer: 0
+  m_Name: SCDefaultAnimationInactive
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8936933457054072598
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 1.0024816, y: 0.25853348, z: -0.63345385}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 1861262250770563182}
+  - {fileID: 7471115643889882934}
+  - {fileID: 110069860838053623}
+  - {fileID: 569625391371299408}
+  - {fileID: 1326682634762807916}
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!95 &6580323041906195452
+Animator:
+  serializedVersion: 5
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  m_Enabled: 1
+  m_Avatar: {fileID: 0}
+  m_Controller: {fileID: 0}
+  m_CullingMode: 0
+  m_UpdateMode: 0
+  m_ApplyRootMotion: 0
+  m_LinearVelocityBlending: 0
+  m_StabilizeFeet: 0
+  m_WarningMessage: 
+  m_HasTransformHierarchy: 1
+  m_AllowConstantClipSamplingOptimization: 1
+  m_KeepAnimatorStateOnDisable: 0
+  m_WriteDefaultValuesOnDisable: 0
+--- !u!114 &146169679456758165
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Name: 
+  ViewPosition: {x: 0, y: 1.6, z: 0.2}
+  Animations: 0
+  ScaleIPD: 1
+  lipSync: 0
+  lipSyncJawBone: {fileID: 0}
+  lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
+  lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
+  VisemeSkinnedMesh: {fileID: 0}
+  MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
+  VisemeBlendShapes: []
+  unityVersion: 
+  portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
+  portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
+  networkIDs: []
+  customExpressions: 0
+  expressionsMenu: {fileID: 0}
+  expressionParameters: {fileID: 0}
+  enableEyeLook: 0
+  customEyeLookSettings:
+    eyeMovement:
+      confidence: 0.5
+      excitement: 0.5
+    leftEye: {fileID: 0}
+    rightEye: {fileID: 0}
+    eyesLookingStraight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingUp:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingDown:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingLeft:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingRight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidType: 0
+    upperLeftEyelid: {fileID: 0}
+    upperRightEyelid: {fileID: 0}
+    lowerLeftEyelid: {fileID: 0}
+    lowerRightEyelid: {fileID: 0}
+    eyelidsDefault:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsClosed:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingUp:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingDown:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsSkinnedMesh: {fileID: 0}
+    eyelidsBlendshapes: 
+  customizeAnimationLayers: 0
+  baseAnimationLayers:
+  - isEnabled: 0
+    type: 0
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 4
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 5
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  specialAnimationLayers:
+  - isEnabled: 0
+    type: 6
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 7
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 8
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  AnimationPreset: {fileID: 0}
+  animationHashSet: []
+  autoFootsteps: 1
+  autoLocomotion: 1
+  collider_head:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_torso:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+--- !u!114 &5146811121193962360
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  launchedFromSDKPipeline: 0
+  completedSDKPipeline: 0
+  blueprintId: 
+  contentType: 0
+  assetBundleUnityVersion: 
+  fallbackStatus: 0
+--- !u!1001 &1577363430154308999
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    serializedVersion: 3
+    m_TransformParent: {fileID: 8936933457054072598}
+    m_Modifications:
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071067
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_DirtyAABB
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_AABB.m_Extent.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_AABB.m_Extent.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[0]
+      value: 5
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[1]
+      value: 6
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[2]
+      value: 7
+      objectReference: {fileID: 0}
+    - target: {fileID: 919132149155446097, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_Name
+      value: test mesh
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+    m_RemovedGameObjects: []
+    m_AddedGameObjects: []
+    m_AddedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+--- !u!4 &1326682634762807916 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+  m_PrefabInstance: {fileID: 1577363430154308999}
+  m_PrefabAsset: {fileID: 0}
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta
new file mode 100644
index 00000000..d37cf9c1
--- /dev/null
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 4744d7b0db7db0d459f3aa7e6a0cf7db
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab
new file mode 100644
index 00000000..daa2600d
--- /dev/null
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab
@@ -0,0 +1,668 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1307328145036867423
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7471115643889882934}
+  - component: {fileID: 2015798673852064281}
+  m_Layer: 0
+  m_Name: AlwaysOffDelete
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &7471115643889882934
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1307328145036867423}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2015798673852064281
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1307328145036867423}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key2
+    ChangeType: 0
+    Value: 100
+--- !u!1 &2598725701317979415
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1861262250770563182}
+  - component: {fileID: 8866671501173891171}
+  m_Layer: 0
+  m_Name: Toggled
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &1861262250770563182
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2598725701317979415}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8866671501173891171
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2598725701317979415}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key1
+    ChangeType: 1
+    Value: 10
+--- !u!1 &2845086157653980983
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 110069860838053623}
+  - component: {fileID: 8218581995269956798}
+  m_Layer: 0
+  m_Name: AlwaysOffSet
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &110069860838053623
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2845086157653980983}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8218581995269956798
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2845086157653980983}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key3
+    ChangeType: 1
+    Value: 100
+--- !u!1 &6385483934583485188
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 569625391371299408}
+  - component: {fileID: 3841502665919975468}
+  - component: {fileID: 2918390808850211981}
+  - component: {fileID: 664065153831629983}
+  m_Layer: 0
+  m_Name: InitialOnToggled
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &569625391371299408
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8936933457054072598}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3841502665919975468
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 1
+  m_shapes:
+  - Object:
+      referencePath: test mesh
+      targetObject: {fileID: 0}
+    ShapeName: key1
+    ChangeType: 1
+    Value: 20
+--- !u!114 &2918390808850211981
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Control:
+    name: 
+    icon: {fileID: 0}
+    type: 102
+    parameter:
+      name: 
+    value: 1
+    style: 0
+    subMenu: {fileID: 0}
+    subParameters: []
+    labels: []
+  MenuSource: 1
+  menuSource_otherObjectChildren: {fileID: 0}
+  isSynced: 1
+  isSaved: 1
+  isDefault: 1
+  automaticValue: 0
+--- !u!114 &664065153831629983
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6385483934583485188}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 7ef83cb0c23d4d7c9d41021e544a1978, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  menuToAppend: {fileID: 0}
+  installTargetMenu: {fileID: 0}
+--- !u!1 &6855505756433160176
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8936933457054072598}
+  - component: {fileID: 6580323041906195452}
+  - component: {fileID: 146169679456758165}
+  - component: {fileID: 5146811121193962360}
+  m_Layer: 0
+  m_Name: SCDefaultAnimationInverted
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8936933457054072598
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 1.0024816, y: 0.25853348, z: -0.63345385}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 1861262250770563182}
+  - {fileID: 7471115643889882934}
+  - {fileID: 110069860838053623}
+  - {fileID: 569625391371299408}
+  - {fileID: 1326682634762807916}
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!95 &6580323041906195452
+Animator:
+  serializedVersion: 5
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  m_Enabled: 1
+  m_Avatar: {fileID: 0}
+  m_Controller: {fileID: 0}
+  m_CullingMode: 0
+  m_UpdateMode: 0
+  m_ApplyRootMotion: 0
+  m_LinearVelocityBlending: 0
+  m_StabilizeFeet: 0
+  m_WarningMessage: 
+  m_HasTransformHierarchy: 1
+  m_AllowConstantClipSamplingOptimization: 1
+  m_KeepAnimatorStateOnDisable: 0
+  m_WriteDefaultValuesOnDisable: 0
+--- !u!114 &146169679456758165
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Name: 
+  ViewPosition: {x: 0, y: 1.6, z: 0.2}
+  Animations: 0
+  ScaleIPD: 1
+  lipSync: 0
+  lipSyncJawBone: {fileID: 0}
+  lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
+  lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
+  VisemeSkinnedMesh: {fileID: 0}
+  MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
+  VisemeBlendShapes: []
+  unityVersion: 
+  portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
+  portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
+  networkIDs: []
+  customExpressions: 0
+  expressionsMenu: {fileID: 0}
+  expressionParameters: {fileID: 0}
+  enableEyeLook: 0
+  customEyeLookSettings:
+    eyeMovement:
+      confidence: 0.5
+      excitement: 0.5
+    leftEye: {fileID: 0}
+    rightEye: {fileID: 0}
+    eyesLookingStraight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingUp:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingDown:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingLeft:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingRight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidType: 0
+    upperLeftEyelid: {fileID: 0}
+    upperRightEyelid: {fileID: 0}
+    lowerLeftEyelid: {fileID: 0}
+    lowerRightEyelid: {fileID: 0}
+    eyelidsDefault:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsClosed:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingUp:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingDown:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsSkinnedMesh: {fileID: 0}
+    eyelidsBlendshapes: 
+  customizeAnimationLayers: 0
+  baseAnimationLayers:
+  - isEnabled: 0
+    type: 0
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 4
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 5
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  specialAnimationLayers:
+  - isEnabled: 0
+    type: 6
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 7
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 8
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  AnimationPreset: {fileID: 0}
+  animationHashSet: []
+  autoFootsteps: 1
+  autoLocomotion: 1
+  collider_head:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_torso:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+--- !u!114 &5146811121193962360
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6855505756433160176}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  launchedFromSDKPipeline: 0
+  completedSDKPipeline: 0
+  blueprintId: 
+  contentType: 0
+  assetBundleUnityVersion: 
+  fallbackStatus: 0
+--- !u!1001 &1577363430154308999
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    serializedVersion: 3
+    m_TransformParent: {fileID: 8936933457054072598}
+    m_Modifications:
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071067
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_DirtyAABB
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_AABB.m_Extent.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_AABB.m_Extent.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[0]
+      value: 5
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[1]
+      value: 6
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[2]
+      value: 7
+      objectReference: {fileID: 0}
+    - target: {fileID: 919132149155446097, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+      propertyPath: m_Name
+      value: test mesh
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+    m_RemovedGameObjects: []
+    m_AddedGameObjects: []
+    m_AddedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+--- !u!4 &1326682634762807916 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
+  m_PrefabInstance: {fileID: 1577363430154308999}
+  m_PrefabAsset: {fileID: 0}
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta
new file mode 100644
index 00000000..487346d8
--- /dev/null
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c86e54d4b828d364aa677a5b3ce7be12
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

From 02204c272f4d326e81baea3ba5e65538d5611b0c Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Wed, 2 Oct 2024 19:03:44 -0700
Subject: [PATCH 10/39] Revert "fix: remove unnecessory exit transitions for
 reactive components (#1161)" (#1248)

This reverts commit 9dfa0dae23d9d4aa6e80e9c9d691e363dc297fdc. Those
transitions are needed when controlling the same object from multiple
parameters.

Closes: #1233
---
 .../AnimationGeneration/ReactiveObjectPass.cs | 40 ++++++++++---------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
index 793fba66..69a0ede7 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
@@ -300,8 +300,7 @@ namespace nadena.dev.modular_avatar.core.editor
             var transitionBuffer = new List<(AnimatorState, List<AnimatorStateTransition>)>();
             var entryTransitions = new List<AnimatorTransition>();
 
-            var initialStateTransitionList = new List<AnimatorStateTransition>();
-            transitionBuffer.Add((initialState, initialStateTransitionList));
+            transitionBuffer.Add((initialState, new List<AnimatorStateTransition>()));
 
             foreach (var group in info.actionGroups.Skip(lastConstant))
             {
@@ -321,30 +320,33 @@ namespace nadena.dev.modular_avatar.core.editor
 
                     var conditions = GetTransitionConditions(asc, group);
 
-                    if (!group.Inverted)
+                    foreach (var (st, transitions) in transitionBuffer)
                     {
-                        var transition = new AnimatorStateTransition
+                        if (!group.Inverted)
                         {
-                            isExit = true,
-                            hasExitTime = false,
-                            duration = 0,
-                            hasFixedDuration = true,
-                            conditions = (AnimatorCondition[])conditions.Clone()
-                        };
-                        initialStateTransitionList.Add(transition);
-                    }
-                    else
-                    {
-                        foreach (var cond in conditions)
-                        {
-                            initialStateTransitionList.Add(new AnimatorStateTransition
+                            var transition = new AnimatorStateTransition
                             {
                                 isExit = true,
                                 hasExitTime = false,
                                 duration = 0,
                                 hasFixedDuration = true,
-                                conditions = new[] { InvertCondition(cond) }
-                            });
+                                conditions = (AnimatorCondition[])conditions.Clone()
+                            };
+                            transitions.Add(transition);
+                        }
+                        else
+                        {
+                            foreach (var cond in conditions)
+                            {
+                                transitions.Add(new AnimatorStateTransition
+                                {
+                                    isExit = true,
+                                    hasExitTime = false,
+                                    duration = 0,
+                                    hasFixedDuration = true,
+                                    conditions = new[] { InvertCondition(cond) }
+                                });
+                            }
                         }
                     }
 

From 409592f952b3e93a61b99b800623b6cef4bd6cbd Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Wed, 2 Oct 2024 19:42:19 -0700
Subject: [PATCH 11/39] fix(rc): constant-off objects are not handled correctly
 (#1249)

Closes: #1233
---
 Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs    | 4 +++-
 .../AnimationGeneration/ReactiveObjectAnalyzer.cs             | 2 +-
 .../ShapeChanger/InitialStates/SCDefaultAnimation.prefab      | 3 ++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
index 68ae3154..dcf040ea 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
@@ -35,7 +35,9 @@ namespace nadena.dev.modular_avatar.core.editor
 
         public bool Inverted;
 
-        public bool IsConstant => ControllingConditions.Count == 0 || ControllingConditions.All(c => c.IsConstant);
+        public bool IsConstant => ControllingConditions.Count == 0
+                                  || ControllingConditions.All(c => c.IsConstant)
+                                  || ControllingConditions.Any(c => c.IsConstant && !c.InitiallyActive);
         public bool IsConstantOn => IsConstant && InitiallyActive;
 
         public override string ToString()
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
index defcc3c1..745c5184 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
@@ -144,7 +144,7 @@ namespace nadena.dev.modular_avatar.core.editor
             HashSet<GameObject> toggledObjects = new();
 
             if (asc == null) return;
-
+            
             foreach (var targetProp in shapes.Keys)
                 if (targetProp is { TargetObject: GameObject go, PropertyName: "m_IsActive" })
                     toggledObjects.Add(go);
diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab
index 71de2608..6dbfb2b7 100644
--- a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab
@@ -183,7 +183,7 @@ GameObject:
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
-  m_IsActive: 0
+  m_IsActive: 1
 --- !u!4 &569625391371299408
 Transform:
   m_ObjectHideFlags: 0
@@ -250,6 +250,7 @@ MonoBehaviour:
   isSynced: 1
   isSaved: 1
   isDefault: 0
+  automaticValue: 0
 --- !u!114 &664065153831629983
 MonoBehaviour:
   m_ObjectHideFlags: 0

From 4ec36ca489c2b14f5adf05d06f62342a75bd36e7 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Wed, 2 Oct 2024 19:48:38 -0700
Subject: [PATCH 12/39] fix: shape changer preview overrides default blendshape
 values inappropriately (#1250)

Closes: #1227
---
 Editor/ReactiveObjects/ShapeChangerPreview.cs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Editor/ReactiveObjects/ShapeChangerPreview.cs b/Editor/ReactiveObjects/ShapeChangerPreview.cs
index d17c7a37..2ca011c7 100644
--- a/Editor/ReactiveObjects/ShapeChangerPreview.cs
+++ b/Editor/ReactiveObjects/ShapeChangerPreview.cs
@@ -101,6 +101,7 @@ namespace nadena.dev.modular_avatar.core.editor
 
                 var activeRule = prop.actionGroups.LastOrDefault(rule => rule.InitiallyActive);
                 if (activeRule == null || activeRule.Value is not float value) continue;
+                if (activeRule.ControllingObject == null) continue; // default value is being inherited
 
                 value = Math.Clamp(value, 0, 100);
                 

From 816d2b28cb3733503d5c0da82adba1246a4740a1 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Wed, 2 Oct 2024 19:51:17 -0700
Subject: [PATCH 13/39] fix: NRE from scale adjuster preview (#1251)

---
 Editor/ScaleAdjuster/ScaleAdjusterPreview.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs b/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs
index 542cd5ff..3b34cec1 100644
--- a/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs
+++ b/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs
@@ -344,7 +344,7 @@ namespace nadena.dev.modular_avatar.core.editor
             if (proxy == null) return;
 
             var curParent = proxy.transform.parent ?? original.transform.parent;
-            if (_finalBonesMap.TryGetValue(curParent, out var newRoot))
+            if (curParent != null && _finalBonesMap.TryGetValue(curParent, out var newRoot))
             {
                 // We need to remember this proxy so we can avoid destroying it when we destroy VirtualAvatarRoot
                 // in Dispose

From d9c0a21f0dcdcc8dec86249511dd403aa675d906 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Wed, 2 Oct 2024 19:52:21 -0700
Subject: [PATCH 14/39] 1.10.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index c84e0cd9..1b12db66 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "nadena.dev.modular-avatar",
   "displayName": "Modular Avatar",
-  "version": "1.10.0",
+  "version": "1.10.1",
   "unity": "2022.3",
   "description": "A suite of tools for assembling your avatar out of reusable components",
   "author": {

From c379d730ca5d1a02df26605497984fc594df2832 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Wed, 2 Oct 2024 20:09:14 -0700
Subject: [PATCH 15/39] chore: fix unit test broken by merge

---
 .../InitialStates/SCDefaultAnimation.cs       | 45 ++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)

diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs
index 8a3f9ad6..c1061b3e 100644
--- a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs
+++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs
@@ -20,7 +20,50 @@ namespace ShapeChangerTests
         [Test]
         public void SetsCorrectInitialStatesAndAnimationsForInactiveSC()
         {
-            SetsCorrectInitialStatesAndAnimations("SCDefaultAnimationInactive.prefab");
+            var root = CreatePrefab("SCDefaultAnimationInactive.prefab");
+            AvatarProcessor.ProcessAvatar(root);
+
+            var fx = (AnimatorController) FindFxController(root).animatorController;
+            var baseLayer = fx.layers[0];
+            
+            var bt = baseLayer.stateMachine.states[0].state.motion as BlendTree;
+            Assert.NotNull(bt);
+            var subBt = bt.children[0].motion as BlendTree;
+            Assert.NotNull(subBt);
+            var clip = subBt.children[0].motion as AnimationClip;
+            Assert.NotNull(clip);
+            
+            var smr = root.transform.Find("test mesh").GetComponent<SkinnedMeshRenderer>();
+            var sharedMesh = smr.sharedMesh;
+
+            var bindings = AnimationUtility.GetCurveBindings(clip);
+            var curve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(
+                "test mesh",
+                typeof(SkinnedMeshRenderer),
+                "blendShape.key1"
+            ));
+            Assert.IsNull(curve); // always off MenuItem (due to object disable), no curve should be generated
+            
+            curve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(
+                "test mesh",
+                typeof(SkinnedMeshRenderer),
+                "blendShape.key2"
+            ));
+            // Always-on delete, no curve should be generated
+            Assert.IsNull(curve);
+            
+            curve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(
+                "test mesh",
+                typeof(SkinnedMeshRenderer),
+                "blendShape.key3"
+            ));
+            // Always-on set, no curve should be generated
+            Assert.IsNull(curve);
+            
+            // Check actual blendshape states
+            Assert.AreEqual(10.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key1")), 0.1f);
+            Assert.AreEqual(5.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key2")), 0.1f);
+            Assert.AreEqual(100.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key3")), 0.1f);
         }
 
         [Test]

From 30cafb21e40e08431636e3dfaec6fb542f07c094 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Thu, 3 Oct 2024 20:16:53 -0700
Subject: [PATCH 16/39] fix: incorrect handling of shape key deletion (#1258)

This change reworks delete handling to be more consistent with other properties,
by treating it as a virtual property (`deletedShape.{blendshapeName}`) instead of
a weird additional field of blendshape keys. This then fixes a number of issues
(e.g. broken preview for delete keys).

Fixes: #1253
---
 .../AnimationGeneration/AnimatedProperty.cs   |   1 -
 .../AnimationGeneration/ReactionRule.cs       |  10 +-
 .../ReactiveObjectAnalyzer.LocateReactions.cs |  75 +-
 .../ReactiveObjectAnalyzer.cs                 |  29 +-
 .../AnimationGeneration/ReactiveObjectPass.cs |  77 +-
 Editor/ReactiveObjects/ShapeChangerPreview.cs |  54 +-
 .../ReactiveObjects/Simulator/ROSimulator.cs  |   5 +-
 .../ReactiveComponent/DeletionTest.prefab     | 666 ++++++++++++++++++
 .../DeletionTest.prefab.meta                  |   7 +
 .../ShapeDeletionAnalysis.cs                  |  98 +++
 .../ShapeDeletionAnalysis.cs.meta             |  11 +
 .../shape deletion test mesh.fbx              | Bin 0 -> 17116 bytes
 .../shape deletion test mesh.fbx.meta         | 109 +++
 13 files changed, 1040 insertions(+), 102 deletions(-)
 create mode 100644 UnitTests~/ReactiveComponent/DeletionTest.prefab
 create mode 100644 UnitTests~/ReactiveComponent/DeletionTest.prefab.meta
 create mode 100644 UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs
 create mode 100644 UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta
 create mode 100644 UnitTests~/ReactiveComponent/shape deletion test mesh.fbx
 create mode 100644 UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta

diff --git a/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs b/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs
index c3bded7d..f56a59af 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs
@@ -8,7 +8,6 @@ namespace nadena.dev.modular_avatar.core.editor
         public TargetProp TargetProp { get; }
         public string ControlParam { get; set; }
 
-        public bool alwaysDeleted;
         public object currentState;
 
         // Objects which trigger deletion of this shape key. 
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
index dcf040ea..12bea0cb 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.Linq;
-using nadena.dev.modular_avatar.animation;
 using UnityEngine;
 
 namespace nadena.dev.modular_avatar.core.editor
@@ -9,8 +8,8 @@ namespace nadena.dev.modular_avatar.core.editor
     {
         public ReactionRule(TargetProp key, float value)
             : this(key, (object)value) { }
-            
-        public  ReactionRule(TargetProp key, UnityEngine.Object value)
+
+        public ReactionRule(TargetProp key, Object value)
             : this(key, (object)value) { }
             
         private ReactionRule(TargetProp key, object value)
@@ -31,15 +30,15 @@ namespace nadena.dev.modular_avatar.core.editor
 
         public bool InitiallyActive =>
             ((ControllingConditions.Count == 0) || ControllingConditions.All(c => c.InitiallyActive)) ^ Inverted;
-        public bool IsDelete;
 
         public bool Inverted;
 
         public bool IsConstant => ControllingConditions.Count == 0
                                   || ControllingConditions.All(c => c.IsConstant)
                                   || ControllingConditions.Any(c => c.IsConstant && !c.InitiallyActive);
-        public bool IsConstantOn => IsConstant && InitiallyActive;
 
+        public bool IsConstantActive => IsConstant && InitiallyActive ^ Inverted;
+        
         public override string ToString()
         {
             return $"AGK: {TargetProp}={Value}";
@@ -57,7 +56,6 @@ namespace nadena.dev.modular_avatar.core.editor
             }
             else return false;
             if (!ControllingConditions.SequenceEqual(other.ControllingConditions)) return false;
-            if (IsDelete || other.IsDelete) return false;
 
             return true;
         }
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs
index f84d0b30..f2e500e7 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs
@@ -124,50 +124,55 @@ namespace nadena.dev.modular_avatar.core.editor
                     var key = new TargetProp
                     {
                         TargetObject = renderer,
-                        PropertyName = "blendShape." + shape.ShapeName,
+                        PropertyName = BlendshapePrefix + shape.ShapeName
                     };
 
+                    var currentValue = renderer.GetBlendShapeWeight(shapeId);
                     var value = shape.ChangeType == ShapeChangeType.Delete ? 100 : shape.Value;
-                    if (!shapeKeys.TryGetValue(key, out var info))
+
+                    RegisterAction(key, renderer, currentValue, value, changer, shape);
+
+                    key = new TargetProp
                     {
-                        info = new AnimatedProperty(key, renderer.GetBlendShapeWeight(shapeId));
-                        shapeKeys[key] = info;
+                        TargetObject = renderer,
+                        PropertyName = DeletedShapePrefix + shape.ShapeName
+                    };
 
-                        // Add initial state
-                        var agk = new ReactionRule(key, value);
-                        agk.Value = renderer.GetBlendShapeWeight(shapeId);
-                        info.actionGroups.Add(agk);
-                    }
-
-                    var action = ObjectRule(key, changer, value);
-                    action.Inverted = _computeContext.Observe(changer, c => c.Inverted);
-                    var isCurrentlyActive = changer.gameObject.activeInHierarchy;
-
-                    if (shape.ChangeType == ShapeChangeType.Delete)
-                    {
-                        action.IsDelete = true;
-                        
-                        if (isCurrentlyActive) info.currentState = 100;
-
-                        info.actionGroups.Add(action); // Never merge
-
-                        continue;
-                    }
-
-                    if (changer.gameObject.activeInHierarchy) info.currentState = action.Value;
-
-                    if (info.actionGroups.Count == 0)
-                    {
-                        info.actionGroups.Add(action);
-                    }
-                    else if (!info.actionGroups[^1].TryMerge(action))
-                    {
-                        info.actionGroups.Add(action);
-                    }
+                    value = shape.ChangeType == ShapeChangeType.Delete ? 1 : 0;
+                    RegisterAction(key, renderer, 0, value, changer, shape);
                 }
             }
 
             return shapeKeys;
+
+            void RegisterAction(TargetProp key, SkinnedMeshRenderer renderer, float currentValue, float value,
+                ModularAvatarShapeChanger changer, ChangedShape shape)
+            {
+                if (!shapeKeys.TryGetValue(key, out var info))
+                {
+                    info = new AnimatedProperty(key, currentValue);
+                    shapeKeys[key] = info;
+
+                    // Add initial state
+                    var agk = new ReactionRule(key, value);
+                    agk.Value = currentValue;
+                    info.actionGroups.Add(agk);
+                }
+
+                var action = ObjectRule(key, changer, value);
+                action.Inverted = _computeContext.Observe(changer, c => c.Inverted);
+
+                if (changer.gameObject.activeInHierarchy) info.currentState = action.Value;
+
+                if (info.actionGroups.Count == 0)
+                {
+                    info.actionGroups.Add(action);
+                }
+                else if (!info.actionGroups[^1].TryMerge(action))
+                {
+                    info.actionGroups.Add(action);
+                }
+            }
         }
         
         private void FindMaterialSetters(Dictionary<TargetProp, AnimatedProperty> objectGroups, GameObject root)
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
index 745c5184..1c5b97b2 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
@@ -18,6 +18,9 @@ namespace nadena.dev.modular_avatar.core.editor
         private readonly ndmf.BuildContext _context;
         private readonly AnimationServicesContext _asc;
         private Dictionary<string, float> _simulationInitialStates;
+
+        public const string BlendshapePrefix = "blendShape.";
+        public const string DeletedShapePrefix = "deletedShape.";
         
         public ImmutableDictionary<string, float> ForcePropertyOverrides { get; set; } = ImmutableDictionary<string, float>.Empty;
 
@@ -58,7 +61,6 @@ namespace nadena.dev.modular_avatar.core.editor
         {
             public Dictionary<TargetProp, AnimatedProperty> Shapes;
             public Dictionary<TargetProp, object> InitialStates;
-            public HashSet<TargetProp> DeletedShapes;
         }
 
         private static PropCache<GameObject, AnalysisResult> _analysisCache;
@@ -86,7 +88,6 @@ namespace nadena.dev.modular_avatar.core.editor
         /// </summary>
         /// <param name="root">The avatar root</param>
         /// <param name="initialStates">A dictionary of target property to initial state (float or UnityEngine.Object)</param>
-        /// <param name="deletedShapes">A hashset of blendshape properties which are always deleted</param>
         /// <returns></returns>
         public AnalysisResult Analyze(
             GameObject root
@@ -98,7 +99,6 @@ namespace nadena.dev.modular_avatar.core.editor
             {
                 result.Shapes = new();
                 result.InitialStates = new();
-                result.DeletedShapes = new();
                 return result;
             }
             
@@ -109,7 +109,7 @@ namespace nadena.dev.modular_avatar.core.editor
             ApplyInitialStateOverrides(shapes);
             AnalyzeConstants(shapes); 
             ResolveToggleInitialStates(shapes);
-            PreprocessShapes(shapes, out result.InitialStates, out result.DeletedShapes);
+            PreprocessShapes(shapes, out result.InitialStates);
             result.Shapes = shapes;
 
             return result;
@@ -165,7 +165,7 @@ namespace nadena.dev.modular_avatar.core.editor
                 group.actionGroups.RemoveAll(agk => agk.IsConstant && !agk.InitiallyActive);
                 
                 // Remove all action groups up until the last one where we're always on
-                var lastAlwaysOnGroup = group.actionGroups.FindLastIndex(ag => ag.IsConstantOn);
+                var lastAlwaysOnGroup = group.actionGroups.FindLastIndex(ag => ag.IsConstantActive);
                 if (lastAlwaysOnGroup > 0)
                     group.actionGroups.RemoveRange(0, lastAlwaysOnGroup - 1);
             }
@@ -264,18 +264,17 @@ namespace nadena.dev.modular_avatar.core.editor
         }
 
         /// <summary>
-        /// Determine initial state and deleted shapes for all properties
+        /// Determine initial state for all properties
         /// </summary>
         /// <param name="shapes"></param>
         /// <param name="initialStates"></param>
-        /// <param name="deletedShapes"></param>
-        private void PreprocessShapes(Dictionary<TargetProp, AnimatedProperty> shapes, out Dictionary<TargetProp, object> initialStates, out HashSet<TargetProp> deletedShapes)
+        private void PreprocessShapes(Dictionary<TargetProp, AnimatedProperty> shapes,
+            out Dictionary<TargetProp, object> initialStates)
         {
             // For each shapekey, determine 1) if we can just set an initial state and skip and 2) if we can delete the
             // corresponding mesh. If we can't, delete ops are merged into the main list of operations.
             
             initialStates = new Dictionary<TargetProp, object>();
-            deletedShapes = new HashSet<TargetProp>();
 
             foreach (var (key, info) in shapes.ToList())
             {
@@ -285,18 +284,6 @@ namespace nadena.dev.modular_avatar.core.editor
                     shapes.Remove(key);
                     continue;
                 }
-                
-                var deletions = info.actionGroups.Where(agk => agk.IsDelete).ToList();
-                if (deletions.Any(d => d.InitiallyActive))
-                {
-                    // always deleted
-                    shapes.Remove(key);
-                    deletedShapes.Add(key);
-                    continue;
-                }
-                
-                // Move deleted shapes to the end of the list, so they override all Set actions
-                info.actionGroups = info.actionGroups.Where(agk => !agk.IsDelete).Concat(deletions).ToList();
 
                 var initialState = info.actionGroups.Where(agk => agk.InitiallyActive)
                     .Select(agk => agk.Value)
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
index 69a0ede7..3ff0fe4a 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
@@ -2,7 +2,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Collections.Immutable;
 using System.Linq;
 using nadena.dev.modular_avatar.animation;
 using UnityEditor;
@@ -42,10 +41,11 @@ namespace nadena.dev.modular_avatar.core.editor
 
             var shapes = analysis.Shapes;
             var initialStates = analysis.InitialStates;
-            var deletedShapes = analysis.DeletedShapes;
             
             GenerateActiveSelfProxies(shapes);
 
+            ProcessMeshDeletion(initialStates, shapes);
+
             ProcessInitialStates(initialStates, shapes);
             ProcessInitialAnimatorVariables(shapes);
             
@@ -53,8 +53,6 @@ namespace nadena.dev.modular_avatar.core.editor
             {
                 ProcessShapeKey(groups);
             }
-
-            ProcessMeshDeletion(deletedShapes);
         }
 
         private void GenerateActiveSelfProxies(Dictionary<TargetProp, AnimatedProperty> shapes)
@@ -225,30 +223,65 @@ namespace nadena.dev.modular_avatar.core.editor
 
         #region Mesh processing
 
-        private void ProcessMeshDeletion(HashSet<TargetProp> deletedKeys)
+        private void ProcessMeshDeletion(Dictionary<TargetProp, object> initialStates,
+            Dictionary<TargetProp, AnimatedProperty> shapes)
         {
-            ImmutableDictionary<SkinnedMeshRenderer, List<TargetProp>> renderers = deletedKeys
-                .GroupBy(
-                    v => (SkinnedMeshRenderer) v.TargetObject
-                ).ToImmutableDictionary(
-                    g => (SkinnedMeshRenderer) g.Key,
-                    g => g.ToList()
-                );
+            var renderers = initialStates
+                .Where(kvp => kvp.Key.PropertyName.StartsWith(ReactiveObjectAnalyzer.DeletedShapePrefix))
+                .Where(kvp => kvp.Key.TargetObject is SkinnedMeshRenderer)
+                .Where(kvp => kvp.Value is float f && f > 0.5f)
+                // Filter any non-constant keys
+                .Where(kvp =>
+                {
+                    if (!shapes.ContainsKey(kvp.Key))
+                    {
+                        // Constant value
+                        return true;
+                    }
 
-            foreach (var (renderer, infos) in renderers)
+                    var lastGroup = shapes[kvp.Key].actionGroups.LastOrDefault();
+                    return lastGroup?.IsConstantActive == true && lastGroup.Value is float f && f > 0.5f;
+                })
+                .GroupBy(kvp => kvp.Key.TargetObject as SkinnedMeshRenderer)
+                .Select(grouping => (grouping.Key, grouping.Select(
+                    kvp => kvp.Key.PropertyName.Substring(ReactiveObjectAnalyzer.DeletedShapePrefix.Length)
+                ).ToList()))
+                .ToList();
+            foreach (var (renderer, shapeNamesToDelete) in renderers)
             {
                 if (renderer == null) continue;
 
                 var mesh = renderer.sharedMesh;
                 if (mesh == null) continue;
 
-                renderer.sharedMesh = RemoveBlendShapeFromMesh.RemoveBlendshapes(
-                    mesh,
-                    infos
-                        .Select(i => mesh.GetBlendShapeIndex(i.PropertyName.Substring("blendShape.".Length)))
-                        .Where(k => k >= 0)
-                        .ToList()
-                );
+                var shapesToDelete = shapeNamesToDelete
+                    .Select(shape => mesh.GetBlendShapeIndex(shape))
+                    .Where(k => k >= 0)
+                    .ToList();
+
+                renderer.sharedMesh = RemoveBlendShapeFromMesh.RemoveBlendshapes(mesh, shapesToDelete);
+
+                foreach (var name in shapeNamesToDelete)
+                {
+                    // Don't need to animate this anymore...!
+                    shapes.Remove(new TargetProp
+                    {
+                        TargetObject = renderer,
+                        PropertyName = ReactiveObjectAnalyzer.BlendshapePrefix + name
+                    });
+
+                    shapes.Remove(new TargetProp
+                    {
+                        TargetObject = renderer,
+                        PropertyName = ReactiveObjectAnalyzer.DeletedShapePrefix + name
+                    });
+
+                    initialStates.Remove(new TargetProp
+                    {
+                        TargetObject = renderer,
+                        PropertyName = ReactiveObjectAnalyzer.BlendshapePrefix + name
+                    });
+                }
             }
         }
 
@@ -257,10 +290,6 @@ namespace nadena.dev.modular_avatar.core.editor
         private void ProcessShapeKey(AnimatedProperty info)
         {
             // TODO: prune non-animated keys
-
-            // Check if this is non-animated and skip most processing if so
-            if (info.alwaysDeleted || info.actionGroups[^1].IsConstant) return;
-
             var asm = GenerateStateMachine(info);
             ApplyController(asm, "MA Responsive: " + info.TargetProp.TargetObject.name);
         }
diff --git a/Editor/ReactiveObjects/ShapeChangerPreview.cs b/Editor/ReactiveObjects/ShapeChangerPreview.cs
index 2ca011c7..58c09131 100644
--- a/Editor/ReactiveObjects/ShapeChangerPreview.cs
+++ b/Editor/ReactiveObjects/ShapeChangerPreview.cs
@@ -72,8 +72,8 @@ namespace nadena.dev.modular_avatar.core.editor
             var analysis = ReactiveObjectAnalyzer.CachedAnalyze(context, avatarRoot);
             var shapes = analysis.Shapes;
 
-            ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>.Builder rendererStates =
-                ImmutableDictionary.CreateBuilder<SkinnedMeshRenderer, ImmutableList<(int, float)>>(
+            var rendererStates =
+                ImmutableDictionary.CreateBuilder<SkinnedMeshRenderer, ImmutableDictionary<int, float>>(
                     
                 );
             var avatarRootTransform = avatarRoot.transform;
@@ -83,16 +83,29 @@ namespace nadena.dev.modular_avatar.core.editor
                 var target = prop.TargetProp;
                 if (target.TargetObject == null || target.TargetObject is not SkinnedMeshRenderer r) continue;
                 if (!r.transform.IsChildOf(avatarRootTransform)) continue;
-                if (!target.PropertyName.StartsWith("blendShape.")) continue;
 
+                var isDelete = false;
+                string shapeName = null;
+                if (target.PropertyName.StartsWith(ReactiveObjectAnalyzer.DeletedShapePrefix))
+                {
+                    isDelete = true;
+                    shapeName = target.PropertyName.Substring(ReactiveObjectAnalyzer.DeletedShapePrefix.Length);
+                }
+                else if (target.PropertyName.StartsWith(ReactiveObjectAnalyzer.BlendshapePrefix))
+                {
+                    shapeName = target.PropertyName.Substring(ReactiveObjectAnalyzer.BlendshapePrefix.Length);
+                }
+                else
+                {
+                    continue;
+                }
+                
                 var mesh = r.sharedMesh;
                 if (mesh == null) continue;
                 
-                var shapeName = target.PropertyName.Substring("blendShape.".Length);
-                
                 if (!rendererStates.TryGetValue(r, out var states))
                 {
-                    states = ImmutableList<(int, float)>.Empty;
+                    states = ImmutableDictionary<int, float>.Empty;
                     rendererStates[r] = states;
                 }
                 
@@ -103,15 +116,30 @@ namespace nadena.dev.modular_avatar.core.editor
                 if (activeRule == null || activeRule.Value is not float value) continue;
                 if (activeRule.ControllingObject == null) continue; // default value is being inherited
 
-                value = Math.Clamp(value, 0, 100);
-                
-                if (activeRule.IsDelete) value = -1;
-                
-                states = states.Add((index, value));
+                if (isDelete)
+                {
+                    if (value < 0.5f) continue;
+                    value = -1;
+                }
+                else
+                {
+                    if (states.ContainsKey(index))
+                    {
+                        // Delete takes precedence over set in preview
+                        continue;
+                    }
+
+                    value = Math.Clamp(value, 0, 100);
+                }
+
+                states = states.SetItem(index, value);
                 rendererStates[r] = states;
             }
-            
-            return rendererStates.ToImmutableDictionary();
+
+            return rendererStates.ToImmutableDictionary(
+                kvp => kvp.Key,
+                kvp => kvp.Value.Select(shapePair => (shapePair.Key, shapePair.Value)
+                ).ToImmutableList());
         }
         
         private IEnumerable<RenderGroup> ShapesToGroups(GameObject avatarRoot, ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>> shapes)
diff --git a/Editor/ReactiveObjects/Simulator/ROSimulator.cs b/Editor/ReactiveObjects/Simulator/ROSimulator.cs
index 3aaf3237..84896612 100644
--- a/Editor/ReactiveObjects/Simulator/ROSimulator.cs
+++ b/Editor/ReactiveObjects/Simulator/ROSimulator.cs
@@ -471,7 +471,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
                     var f_set_inactive = effectGroup.Q<VisualElement>("effect__set-inactive");
                     var f_value = effectGroup.Q<FloatField>("effect__value");
                     var f_material = effectGroup.Q<ObjectField>("effect__material");
-                    var f_delete = effectGroup.Q("effect__deleted");
+                    var f_delete = effectGroup.Q<TextField>("effect__deleted");
                     
                     f_target_component.style.display = DisplayStyle.None;
                     f_target_component.SetEnabled(false);
@@ -504,9 +504,10 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
                         f_property.value = targetProp.PropertyName;
                         f_property.style.display = DisplayStyle.Flex;
 
-                        if (reactionRule.IsDelete)
+                        if (reactionRule.TargetProp.PropertyName.StartsWith(ReactiveObjectAnalyzer.DeletedShapePrefix))
                         {
                             f_delete.style.display = DisplayStyle.Flex;
+                            f_delete.value = reactionRule.Value is > 0.5f ? "DELETE" : "RETAIN";
                         } else if (reactionRule.Value is float f)
                         {
                             f_value.SetValueWithoutNotify(f);
diff --git a/UnitTests~/ReactiveComponent/DeletionTest.prefab b/UnitTests~/ReactiveComponent/DeletionTest.prefab
new file mode 100644
index 00000000..6cefc636
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/DeletionTest.prefab
@@ -0,0 +1,666 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &2464504760772767737
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3251791125987375227}
+  - component: {fileID: 6611954401356246169}
+  - component: {fileID: 4257580493320060063}
+  - component: {fileID: 7095484051158404692}
+  m_Layer: 0
+  m_Name: DeletionTest
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3251791125987375227
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2464504760772767737}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0.06867766, y: 0.7869835, z: -0.57959247}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children:
+  - {fileID: 8671858138418525756}
+  - {fileID: 3787319563290092876}
+  - {fileID: 2780879708549973278}
+  - {fileID: 6867583134219554799}
+  - {fileID: 3617623734196600728}
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!95 &6611954401356246169
+Animator:
+  serializedVersion: 5
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2464504760772767737}
+  m_Enabled: 1
+  m_Avatar: {fileID: 0}
+  m_Controller: {fileID: 0}
+  m_CullingMode: 0
+  m_UpdateMode: 0
+  m_ApplyRootMotion: 0
+  m_LinearVelocityBlending: 0
+  m_StabilizeFeet: 0
+  m_WarningMessage: 
+  m_HasTransformHierarchy: 1
+  m_AllowConstantClipSamplingOptimization: 1
+  m_KeepAnimatorStateOnDisable: 0
+  m_WriteDefaultValuesOnDisable: 0
+--- !u!114 &4257580493320060063
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2464504760772767737}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Name: 
+  ViewPosition: {x: 0, y: 1.6, z: 0.2}
+  Animations: 0
+  ScaleIPD: 1
+  lipSync: 0
+  lipSyncJawBone: {fileID: 0}
+  lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
+  lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
+  VisemeSkinnedMesh: {fileID: 0}
+  MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
+  VisemeBlendShapes: []
+  unityVersion: 
+  portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
+  portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
+  networkIDs: []
+  customExpressions: 0
+  expressionsMenu: {fileID: 0}
+  expressionParameters: {fileID: 0}
+  enableEyeLook: 0
+  customEyeLookSettings:
+    eyeMovement:
+      confidence: 0.5
+      excitement: 0.5
+    leftEye: {fileID: 0}
+    rightEye: {fileID: 0}
+    eyesLookingStraight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingUp:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingDown:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingLeft:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingRight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidType: 0
+    upperLeftEyelid: {fileID: 0}
+    upperRightEyelid: {fileID: 0}
+    lowerLeftEyelid: {fileID: 0}
+    lowerRightEyelid: {fileID: 0}
+    eyelidsDefault:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsClosed:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingUp:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingDown:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsSkinnedMesh: {fileID: 0}
+    eyelidsBlendshapes: 
+  customizeAnimationLayers: 0
+  baseAnimationLayers:
+  - isEnabled: 0
+    type: 0
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 4
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 5
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  specialAnimationLayers:
+  - isEnabled: 0
+    type: 6
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 7
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 8
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  AnimationPreset: {fileID: 0}
+  animationHashSet: []
+  autoFootsteps: 1
+  autoLocomotion: 1
+  collider_head:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_torso:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+--- !u!114 &7095484051158404692
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2464504760772767737}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  launchedFromSDKPipeline: 0
+  completedSDKPipeline: 0
+  blueprintId: 
+  contentType: 0
+  assetBundleUnityVersion: 
+  fallbackStatus: 0
+--- !u!1 &3134446681435896768
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2780879708549973278}
+  - component: {fileID: 2470606632396626262}
+  m_Layer: 0
+  m_Name: Delete
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2780879708549973278
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3134446681435896768}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 3251791125987375227}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2470606632396626262
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3134446681435896768}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 0
+  m_shapes:
+  - Object:
+      referencePath: shape deletion test mesh
+      targetObject: {fileID: 0}
+    ShapeName: bottom
+    ChangeType: 0
+    Value: 50
+--- !u!1 &7874409458034691206
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3787319563290092876}
+  - component: {fileID: 8462455628590652122}
+  m_Layer: 0
+  m_Name: PriorSet
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3787319563290092876
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7874409458034691206}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 3251791125987375227}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8462455628590652122
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7874409458034691206}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 0
+  m_shapes:
+  - Object:
+      referencePath: shape deletion test mesh
+      targetObject: {fileID: 0}
+    ShapeName: bottom
+    ChangeType: 1
+    Value: 50
+--- !u!1 &7956182162252432618
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3617623734196600728}
+  - component: {fileID: 4167915178638071617}
+  - component: {fileID: 3280847981733507148}
+  m_Layer: 0
+  m_Name: MenuSet
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &3617623734196600728
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7956182162252432618}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 3251791125987375227}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4167915178638071617
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7956182162252432618}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 0
+  m_shapes:
+  - Object:
+      referencePath: shape deletion test mesh
+      targetObject: {fileID: 0}
+    ShapeName: bottom
+    ChangeType: 1
+    Value: 0
+--- !u!114 &3280847981733507148
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7956182162252432618}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Control:
+    name: 
+    icon: {fileID: 0}
+    type: 102
+    parameter:
+      name: 
+    value: 1
+    style: 0
+    subMenu: {fileID: 0}
+    subParameters: []
+    labels: []
+  MenuSource: 1
+  menuSource_otherObjectChildren: {fileID: 0}
+  isSynced: 1
+  isSaved: 1
+  isDefault: 0
+  automaticValue: 1
+--- !u!1 &8389945206789797712
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6867583134219554799}
+  - component: {fileID: 8099891503683627458}
+  m_Layer: 0
+  m_Name: NullSet
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!4 &6867583134219554799
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8389945206789797712}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 3251791125987375227}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8099891503683627458
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8389945206789797712}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 0
+  m_shapes:
+  - Object:
+      referencePath: shape deletion test mesh
+      targetObject: {fileID: 0}
+    ShapeName: bottom
+    ChangeType: 1
+    Value: 0
+--- !u!1001 &9210451080691405271
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    serializedVersion: 3
+    m_TransformParent: {fileID: 3251791125987375227}
+    m_Modifications:
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071067
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_DirtyAABB
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.z
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[0]
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_Name
+      value: shape deletion test mesh
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+    m_RemovedGameObjects: []
+    m_AddedGameObjects: []
+    m_AddedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
+--- !u!4 &8671858138418525756 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 9210451080691405271}
+  m_PrefabAsset: {fileID: 0}
diff --git a/UnitTests~/ReactiveComponent/DeletionTest.prefab.meta b/UnitTests~/ReactiveComponent/DeletionTest.prefab.meta
new file mode 100644
index 00000000..0197de59
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/DeletionTest.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a82669288fc87d94db320a2494fd76c5
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs
new file mode 100644
index 00000000..7cb78e50
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs
@@ -0,0 +1,98 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using modular_avatar_tests;
+using nadena.dev.modular_avatar.core;
+using nadena.dev.modular_avatar.core.editor;
+using NUnit.Framework;
+using UnityEngine;
+
+public class ShapeDeletionAnalysis : TestBase
+{
+    [Test]
+    public void BasicShapeDeletionAnalysis()
+    {
+        var root = CreatePrefab("DeletionTest.prefab");
+
+        var mesh = AssertPreviewDeletion(root);
+
+        AssertBuildDeletion(mesh, root);
+    }
+
+    [Test]
+    public void WhenShapeDeletionIsConditionedOnSubsequentChanger_DoesNotDelete()
+    {
+        var root = CreatePrefab("DeletionTest.prefab");
+        root.transform.Find("MenuSet").gameObject.SetActive(true);
+
+        AssertPreviewDeletion(root);
+        AssertNoMeshDeletion(root);
+        
+        var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
+        Assert.AreEqual(100, mesh.GetBlendShapeWeight(mesh.sharedMesh.GetBlendShapeIndex("bottom")));
+    }
+
+
+    [Test]
+    public void WhenShapeDeletionIsConditionedOnItself_DoesNotDelete()
+    {
+        var root = CreatePrefab("DeletionTest.prefab");
+        root.transform.Find("Delete").gameObject.AddComponent<ModularAvatarMenuItem>().InitSettings();
+        
+        AssertNoPreviewDeletion(root);
+        AssertNoMeshDeletion(root);
+        
+        var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
+        // deletion action is initially off, so we use the shape changer above it, which is set to 50.
+        Assert.AreEqual(50f, mesh.GetBlendShapeWeight(mesh.sharedMesh.GetBlendShapeIndex("bottom")));
+    }
+    
+    private static void AssertBuildDeletion(SkinnedMeshRenderer mesh, GameObject root)
+    {
+        var originalSharedMesh = mesh.sharedMesh;
+        AvatarProcessor.ProcessAvatar(root);
+        Assert.AreNotEqual(originalSharedMesh, mesh.sharedMesh);
+
+        Assert.IsTrue(mesh.sharedMesh.vertices.All(v => v.z >= 0));
+    }
+
+    private static SkinnedMeshRenderer AssertPreviewDeletion(GameObject root)
+    {
+        var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
+        var analysis = new ReactiveObjectAnalyzer().Analyze(root);
+        var deletedShape = analysis.Shapes.GetValueOrDefault(new TargetProp()
+        {
+            TargetObject = mesh,
+            PropertyName = "deletedShape.bottom"
+        });
+        Assert.IsNotNull(deletedShape);
+        var activeGroup = deletedShape.actionGroups.LastOrDefault(ag => ag.InitiallyActive);
+        Assert.AreEqual(1.0f, activeGroup?.Value);
+        return mesh;
+    }
+    
+    private static void AssertNoPreviewDeletion(GameObject root)
+    {
+        var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
+        var analysis = new ReactiveObjectAnalyzer().Analyze(root);
+        var deletedShape = analysis.Shapes.GetValueOrDefault(new TargetProp()
+        {
+            TargetObject = mesh,
+            PropertyName = "deletedShape.bottom"
+        });
+        if (deletedShape != null)
+        {
+            var activeGroup = deletedShape.actionGroups.LastOrDefault(ag => ag.InitiallyActive);
+            Assert.IsFalse(activeGroup?.Value is float f && f > 0);
+        }
+        
+    }
+
+    private static void AssertNoMeshDeletion(GameObject root)
+    {
+        var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
+        var originalSharedMesh = mesh.sharedMesh;
+        AvatarProcessor.ProcessAvatar(root);
+        Assert.AreEqual(originalSharedMesh, mesh.sharedMesh);
+    }
+}
diff --git a/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta
new file mode 100644
index 00000000..024b7f11
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 18eb55e1b66a00243a91142456dfd5f5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx
new file mode 100644
index 0000000000000000000000000000000000000000..52fa8bb9d39297d235bc9146d397de02a8c87a50
GIT binary patch
literal 17116
zcmc&+3y>Ved7eX$J4uIx1O|Z+Iur>#j7}#ZUJ^+6mJaPbxV;kyQibMr=k5mfW;VOC
z(w(sl7@3MG5>ip95E56}AxSC522u&OgGr!JB?gLA2#`RCNkYJ}2oNc;j1>bW-`D-m
zZtv~R?&{<!cWG{By8Hk7e{}!-&p)%;ZBHfbjNR1Teq&R+>)Dy9rY7qG>*$4+^@sIV
zzAQoeF2_zfnXbve@v^S({VE#kaLmDRR=#<UlVM}ed$TNS(&#ZCJ;FAEKWMZq?D7(s
zsdNCkp{cafgC^@PLQ7fPk#TI*>P|TEz@A9|JzB!C>+`S}@`pyl?7fbi={X81J~nFS
z^!i>f-ZLLnivba$%<8nKdH`UpF)C;8@^hJ<C8*tD)Xwd7y<Fh*tV8_`W_{dA_+GN-
zQq%`V{eoUMm2$JX@mpvpDt{|IH=(v(3F>F!glJDW9`xO`HXGO@DMwkTr+;13hNdme
zo0~T`#fIXzzPx$U=H@LAK*%#dvj)e!c*60V9&gl-e+IWZQ~rpZ;;mIzXlX#paMsCm
z+JSvBD9P2CIA+rTakF)+w9?FZRVQ&XpW2er(CY*?vBsl%-(fV&8FGV^6DRG1Ms<BW
zH*%|!2vo!4M#J2;Trdu;kl+QQzG1gBb->Rgv#RACvt`iP?{XfhmQRe9`VN0$!twMV
z1k9ITH>P{zK*moynZR|jSGL>=K`H&!IO+!C3vlZ66FH&`XHs#J?T!Z-*BgsdC9K2A
zQXMyr`cQwSd30p*F9hZqv;k%zyJQkGLHbW1%?9}@jYMnpXWTKY-jp&OL`?fRFR9r;
ztJR9`6d{HKV_$$%Nb&i$bUNiG)cV?k83#R^5}~K1akvb^IoVBkMe<ca?nf(-m*P|o
zxzEPREsuMifje!k9~1CS4ZbV_TpU0I{yb3SSIl+L0u|A#5xGF}>|*{pL+~)S?Cc#<
zdJHQHdW@6VmQ5S+Z`0LRwluZ0Y*qi7TUuIvN1(;Ni%sZmH|11N0jHEPQ@#%*NN%mh
z0i9BSn4Jyc_I@WBVc?Z>i{zrF@+7Be;X4I-Ky!0`6m$joDwy~cLGRM&*cqZ2SNlpW
zHr|MN1`S$$8uHvC$k3)UH1h3&y<KBpgj0V07z?QnYh%Adt4A;lx?0N%7?SRpb9F8u
zdy<1^;d<?#uYd2`t6qHe>E|9_|EHh*_<uf?epF#&(T+*=bGmM5*@S;hn`F{r>b7*k
zc@X@xcAsY(fgoj}-f!Yg5Wpd3Ck3DO$nPFU;lG|RXo|e;bD&^v?yi{pm>$+}x^2?U
zD(C9Lb3;z6#|xY>M{fx|6krxs3CT9#02#M;A#=yP%B0r{DK}kNZrvFK36%%Mo?_iD
zlr5T)8)$*D>bOsoj@#j5JF7e{#;|@|I&Oio>bRSs9IA>ohrHZ~!Z}<`)1RW`g#2<%
z&TU~Dc~uU+ClFU@h($OJdu|X<*eR#mP6XJ4X!oR_L*Sz>`*IT_Vs35*D0^Gy!EI8G
zRveInvpmnpsiWNjG|sGD8jaJlZDPd5Cg0(wz!0XTrLG2dw#PZFuEvEW9F@CxRCsJd
zH}J?A(&>!axm3`B9Wi4^xQBDl4<Sf0mp}kGfRH+kxCkEQErDCD;m8wrtX@Cq#JOWQ
zUT&hWTlJu~wGxt$-OF$Y2PBE7Z4aSzCQ!2MR#ik$7dWd>foE$(05;<q08~r?z)wfg
zKmOX$XSfUx3%z0Nj$AhIC%Q9G!5}u>>c+mR6+jW?1eGdfUKmbgk6~2$1&ThW1K~=b
z+{BO2nL*le91UUT*lpij+>8oK$)bYf!qrm-e37H_&_In{f<yEY$Kyi6>DN&#XRu!g
zXq8zW1C3#fv(cqZ8Me)HCt!}mZ54J=bS^G$0F5%j8(0m4z8~~q9>j|Kl)GEXB`;<E
zpfl=Z953Ny!zy(ixwLS*08`e*ib6b{?E!75*StedqVGPVK@?8D9+hf}I$vu>Jz|iB
z;;rKAr2rhLsGPvY$4c77MB_r(>G%^)kePb76o(f7k&t3uSZ7IiKQ-h`q*I)MT>0H2
zlf6!M{0(ldQi=@zIru5|Yr_G9(~i|$!$ZHv^6rk#l8YAwu2aJixxL+=#KWcXHG8n)
z{mf;ZYH{crHHFw+mC}ogz&T;2BifTQMZ6h^lyf5vh)iEUkZ~uNCD?=Ks}VQl22;xO
zjQGAh_MnfY*4iN<UayI1vkru@*{1loo%9c6qv-xl=r{v0D0hcJC*i>TRii}DTFURP
z(zA%($(%)$l&&;&DT4QKk{izBUZJZvg}4U4fR14)nnI5Pn4^d<Wk`2@4)ZVLF=v99
zCX@Ene4LGzY0jL0^{@afyc-8p!$mac2M9KOuYYtj>jX13^eqB>od%|@Lf`>+zdvK#
z@08+lmq%!Ldi*YgKcOOeG{3Zn;GPz{*8!IHY1Xi?mNk^Iy)0}36>C>rZrY-s^{}vH
z*v6nejb;6DW#E~w?kf;xP+DW56?1%Jl&tA$lzXEnzfq0yrYOpHDpT5%k=$N{byh)6
zdoq&S8>>-9a{F6CX*j=dLG@>l)>BfJS>dk=-4(D4Ryd!wLpp?P38&l7#M}ut==V}(
z9WVM@%kjuddAH#ZubsyT+|g0b$!2e?9&}mOXbnJnYXN$!2B4d20s26ITC`|%0eoDF
zIbL<@RjnO(Mj(mA_zaH+GEQDMj3Q>R?LH)^hA$fMv%%@Yz5-ZU@v}Il`TEL^XdRMp
z)1(Y}ISns#M+MZGyP$6al|ru5Leh^Lyhg6*4=R#HF6UKPPSK-9uH~C6kX3Lyzg>|m
zlF8>Ql0`E4sgSMJn&jN>@y4Bu8>o0YBA~busbFfva8t66R-R+`fsS)*ryh-wl+c~V
z6niTgw6+;`d`Bl3K`}1QLoSpO8hR;k8acswD^W$7?(Rxdk(T?*XsQac>sTeKNDfyc
zI~bu2doq&4gP@|dug3vbgpD|S+Hw;{#4yv~^WO!&Ps20nl7~4r5qg7}VsFEfK3VV8
z*m$%kV9PwH1=^TU`6+e>psC9%G&EyoO#V*1o*cBjF{h>-_DE%nnl{&ZWT7kYQPaM<
zy9|asd1|uUUm2q&Szf7(QIjketkC+nTHA#t7rWpso#TxK<29Mat_o9=M2D-w)Fjlg
zGBDaerzF%BO{KaihN(%YFI0u8NvLN9=1Q1ILL9_n%ts~Lys=bmOQ`;0Em9OnO#;QL
zfYc<;mjJ?jR<;^GoyUCtwzeQlH>ntPdQR|3p^`0_({(tl!_bO!?+VK}QF$CVKJFhV
z_xMzYwv8*bny%8_v3pp3&KRC+_d7^1<CB2^X&Jox&!y8ovciNblher+qu^mB%xAX+
zcm+Pf?;`Nk!esOt7rsLH6G8bE%?mT7-5JOEoK9j-8y1b$Yo!FS{!V1e%JE5p%K%Nc
zFIx(8NU=n!$=s~U)sZw#ZMBJ)FPGE*ieT?TKPa9tE3xxCRY?UpR7ZVNP`foM<2Ry)
zd(`y%63lWt)bdr@=8|q=lxC#urrnl7ObdM%+8`;Oy$t#$2`_Erjw>}OT>eo>L0;5Q
z{tYA34%}4D6kP-Ek&X5<czN=&^x@toS-M%cEGomuZA9VpGV$ixsS8%?J_Zg+b_pq?
zozjFcXAcs_@myvUPvrySzBjfF1Ys%4`aO&R2)+r2Oc<s>kpxrO#)x-7q)@o83;7yN
zPP6sm1(F-HrzDS1X2q<<YY8rZc1=V*c1^h1ES9@YS`)tT3ajd*9|s;uWGd0Og?Ppr
zJL5H75#sS3-w}S=KqxvWdc9Vb9i20OK1`k)7as~cqEd<{a5Zt$1zl1*1~;8ZjO4PF
zlv9$y&*$_H0*(S0+z4Dv+`J~-bZOmi2T+bGZ6jQf*N<iOgN~|nmRu~u9z+s}O_7B)
zAQMwHumu9akOTzv)sGYuGyZdN9c}BwAY|`u9I!QC1NC9W+NoYUT}!ZC&guSCFvViC
zf!Us$m@(W{NH|bGmuVKcrU+-~rQ*xy<2w!kfHL-K06w#Hx>>wTN;v#5ton-~5U5gW
zyB`Q|+t3d6(RL<u?L1z)Uh^2&K*%Raddq}69Zv7V0Mw!w1xcAQ1Jus%4}@RCXT|a2
zJD0LE6?|5-leiEwm+TonE5tLHik`<e2>k|N$UKgZ<3%X*rxlmLmez-a^m0wg>5(Vx
znoRp1<E=`oXiMw+fZz<{1i|R~HP~p19|HJZ1%05qDy0*v85<P!u>}VZ>1XivR}gE`
zhyp&vKZjZi1oiq2aCSRb;9k8NM??7HHn!qF?rdgX^<JwZH{w`zVVjEc_;$8~#*k99
z$=0EP!eI_kIO=x95Ac~mMK!}i3f&q_N3H^V)lkVc=wg;65b99D(HDgbhaV+>9+i(t
z2Rh>hGz2c*wkNq-NQTvJP1Ys$S$8e^aNoTxf3>1_`PLPq3u704^`7UCj6ZnI))kj5
zeE8R1m}MRN)=GC96hxv497_iL)YO>o5z(1cVYf?AmVN54ejK4FpCIa{XlLNy{Yl(&
za`BqJ^k=vy|HNiLw)2{WG#^Vl`?CBT_xB1f*W!LoS8~kBx{az4pGeA?5F{rgNjV&c
zosVMy4)#A22dCosAYy;Au|&l-T`A>D_1@8gYU}$lB>UvQK5%gJrqM84eM7<S=WNuF
z4B^hj=95Oq#V?_;7jY4(#3<KQ`&5xCcMBOk@6!DD5z`6Dw^I6$-|1$sEbkFA&ZK(H
zbT&y0N`=&>vhp)?x4)h^+u3*W@fE{=@Wy{FU21i`)bRU_B0J66D5T{LiVpcgt(ph5
zVjdjVJZJ&)pk>Shw}tXNf|<}93r%wj?@^{0G|W|GiV10;jkPJr)9@bX|Bs~U6z%aZ
zLUxMw*d`KOkKxZ4-jhU@QAyN^vtDq^KHN9I@0zx^kG!+7^>6I|ZTWfK;ghr0o*7^C
z_K{bQob+FM^~i~*TVKK5i6bXlK55zh;$JTs_@w10E8efW`_S?QPYFJaRu8T4xwmKm
zhO?bBo!XcuXU?2(rBKDRA?-lhP*?2NAlJkQX~UJoI`%6n-^DH$+Up$CUg2YovRZ@D
ztH^2>84ZO6)GM{*HfjNJJ-4M&aeT~mP`WQc)hR5TM}%xK$mWIYW?f3dLba5XP1r#~
zapx#;m3&79it5SBm&DB=BRwgNrh3+)fk2Fo^sgVtz1c&r$1dqd<oWa*No8;~Dyj<Q
zyHO+EF9>I&uO4Y3uFR2!_dHVTD61fUSc>HNND%NkNJimmGe-$Ee4e4giV}t6l%79A
ze8bs`;M~~9pMCP~t?a8J47~{(J2@4oQcYhsMUYgN`oRTh^W{l{%OZ=uCk@D;TlLBo
z*nq;hauzCSRWhgM0?rd)mUUdOg>5)oi2G1mz8ru07ghh?1_${aqB;ZRhd=GwCy+6v
zepcfs1Y!U9=07n-XsK^i5eb#E+|7(+q!a~VVWm0;=oK!}BFp`;G(=jC<e@{Pvs(jB
zXJok@ijk5($4|Nbu))DE-*Rj5Y9qAVY<z3r#NIo6kdGRc`tV(c&a78Y+xS^uk~i~U
zS89>lPvLi7Ve3k1RjVb8Ug<e^7!3>hMu^03+_Ea2w~Q}Yq%?$0o=|Wfo2|Y{{n9so
zcnNzgM0c|A8#v~|4`tlRB$q17o$4?DM42bwVcJFH0*x^9H1hmyrEz~#RE~!-d^ju_
z$k@eSUVJghjVG3{O<6+yUP?K;yF~I5_2x(C#W?&X4U5@kjB@*1=QDP(;@mkxe~KT=
z<%i<NSs1Eg-1a;t_2Z}!&&41#0?l)##*jAvov2aeX6dp4cMH06sniXQJ2oC<lc9fY
z(*5vvZ>zqHI!nhL$YxM7`uu|a2F~FZy^xH5m9wcT)(gT~i6TF!F4kr+LCJq{V%6c{
zhhnY7fnS#5+tFA!*VXU;^itGmUh0mIu=fcUMG_oE<H2k2M@o@f{Rk8cvv%RYvn_Xw
zn%_9QJZH@s>&`vT<ZYY3Xv1*i0jrg$pTP;Ufxk_t^<54qtOJ6=5CABQb*p@LLJJg?
zuK|LUWM+OLwXH&{?-_0LRNIh!oBoOLKS&dIih4<?chsGbT0Ym+cVOz`nNPf;y2f{>
zXepF?S_{6kAq<UT=ic#U)kS6B!dj~?yo$4N2(}N|&hKvoKOhZ^+Z&5A{ghDU^bZ-d
z(?y<YMK3AE(*Q&&+D4;HgX&pp^!=Lp0KMemy6q)!)l>37Y4sYvp+2-JzW55@C?B4R
z$Q^f?+EeoQVXu%Qd?h(`cPbfKJYmDzQ#<V%fa8bRzRKCM2=&|qJj#P73Npl4J;bXa
zSN7752ZWx^2K4l{`4nJudI^b`w$|5$lv_GT>E83C)w72RVtuRD23I>ExEcz%z8UEw
z1w`tD9vq1D*agHIG=l!MmE&!NehWH$>}4wDnSHrcuhjF=T-OojvhVNDyW3bu^nYc$
zg}KFi|0aw=&s~fbZs_@3<j%6)!W}su0C;7)g%3ipu>S*PyM?=89_^{J-NMK`7WRL;
zY`4&L<s}Ujqt0u&JfzH_E6pxLO&w}zTR6-N-#ID&7IrH=|Lmzp=DqaXtD}Fm|D(Gv
l{OKiMG<xv=cKBtLlQ-RW>%acwpTGG2yNz$>?q9dy{{c=!45t79

literal 0
HcmV?d00001

diff --git a/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta
new file mode 100644
index 00000000..0f60ec10
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta	
@@ -0,0 +1,109 @@
+fileFormatVersion: 2
+guid: fe5b76dae94c07345b74d51e9a9a8440
+ModelImporter:
+  serializedVersion: 22200
+  internalIDToNameTable: []
+  externalObjects: {}
+  materials:
+    materialImportMode: 2
+    materialName: 0
+    materialSearch: 1
+    materialLocation: 1
+  animations:
+    legacyGenerateAnimations: 4
+    bakeSimulation: 0
+    resampleCurves: 1
+    optimizeGameObjects: 0
+    removeConstantScaleCurves: 0
+    motionNodeName: 
+    rigImportErrors: 
+    rigImportWarnings: 
+    animationImportErrors: 
+    animationImportWarnings: 
+    animationRetargetingWarnings: 
+    animationDoRetargetingWarnings: 0
+    importAnimatedCustomProperties: 0
+    importConstraints: 0
+    animationCompression: 1
+    animationRotationError: 0.5
+    animationPositionError: 0.5
+    animationScaleError: 0.5
+    animationWrapMode: 0
+    extraExposedTransformPaths: []
+    extraUserProperties: []
+    clipAnimations: []
+    isReadable: 0
+  meshes:
+    lODScreenPercentages: []
+    globalScale: 1
+    meshCompression: 0
+    addColliders: 0
+    useSRGBMaterialColor: 1
+    sortHierarchyByName: 1
+    importPhysicalCameras: 1
+    importVisibility: 1
+    importBlendShapes: 1
+    importCameras: 1
+    importLights: 1
+    nodeNameCollisionStrategy: 1
+    fileIdsGeneration: 2
+    swapUVChannels: 0
+    generateSecondaryUV: 0
+    useFileUnits: 1
+    keepQuads: 0
+    weldVertices: 1
+    bakeAxisConversion: 0
+    preserveHierarchy: 0
+    skinWeightsMode: 0
+    maxBonesPerVertex: 4
+    minBoneWeight: 0.001
+    optimizeBones: 1
+    meshOptimizationFlags: -1
+    indexFormat: 0
+    secondaryUVAngleDistortion: 8
+    secondaryUVAreaDistortion: 15.000001
+    secondaryUVHardAngle: 88
+    secondaryUVMarginMethod: 1
+    secondaryUVMinLightmapResolution: 40
+    secondaryUVMinObjectScale: 1
+    secondaryUVPackMargin: 4
+    useFileScale: 1
+    strictVertexDataChecks: 0
+  tangentSpace:
+    normalSmoothAngle: 60
+    normalImportMode: 0
+    tangentImportMode: 3
+    normalCalculationMode: 4
+    legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+    blendShapeNormalImportMode: 1
+    normalSmoothingSource: 0
+  referencedClips: []
+  importAnimation: 1
+  humanDescription:
+    serializedVersion: 3
+    human: []
+    skeleton: []
+    armTwist: 0.5
+    foreArmTwist: 0.5
+    upperLegTwist: 0.5
+    legTwist: 0.5
+    armStretch: 0.05
+    legStretch: 0.05
+    feetSpacing: 0
+    globalScale: 1
+    rootMotionBoneName: 
+    hasTranslationDoF: 0
+    hasExtraRoot: 0
+    skeletonHasParents: 1
+  lastHumanDescriptionAvatarSource: {instanceID: 0}
+  autoGenerateAvatarMappingIfUnspecified: 1
+  animationType: 2
+  humanoidOversampling: 1
+  avatarSetup: 0
+  addHumanoidExtraRootOnlyWhenUsingAvatar: 1
+  importBlendShapeDeformPercent: 1
+  remapMaterialsIfMaterialImportModeIsNone: 0
+  additionalBone: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

From 4da4ebc9849aba51c16feb6d8069fe23088a7041 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Thu, 3 Oct 2024 20:17:33 -0700
Subject: [PATCH 17/39] 1.10.2

---
 .github/ProjectRoot/vpm-manifest-2022.json | 2 +-
 package.json                               | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/ProjectRoot/vpm-manifest-2022.json b/.github/ProjectRoot/vpm-manifest-2022.json
index eaba4f86..60d7c06f 100644
--- a/.github/ProjectRoot/vpm-manifest-2022.json
+++ b/.github/ProjectRoot/vpm-manifest-2022.json
@@ -19,7 +19,7 @@
       "dependencies": {}
     },
     "nadena.dev.ndmf": {
-      "version": "1.5.0"
+      "version": "1.5.3"
     }
   }
 }
\ No newline at end of file
diff --git a/package.json b/package.json
index 1b12db66..b6141b32 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "nadena.dev.modular-avatar",
   "displayName": "Modular Avatar",
-  "version": "1.10.1",
+  "version": "1.10.2",
   "unity": "2022.3",
   "description": "A suite of tools for assembling your avatar out of reusable components",
   "author": {
@@ -16,6 +16,6 @@
   },
   "vpmDependencies": {
     "com.vrchat.avatars": ">=3.7.0",
-    "nadena.dev.ndmf": ">=1.5.0 <2.0.0-a"
+    "nadena.dev.ndmf": ">=1.5.3 <2.0.0-a"
   }
 }

From 394601d4a782d0fb1973decaebe8131f6894d0b7 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sun, 6 Oct 2024 07:19:32 +0900
Subject: [PATCH 18/39] =?UTF-8?q?docs:=20=E3=83=89=E3=82=AD=E3=83=A5?=
 =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=81=B8=E3=81=AE=E8=AA=98=E5=B0=8E?=
 =?UTF-8?q?=E3=82=92=E5=BE=AE=E8=AA=BF=E6=95=B4=20(#1265)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 033d49d3..c4b74d03 100644
--- a/README.md
+++ b/README.md
@@ -33,4 +33,4 @@ For more information, check out the [documentation](https://m-a.nadena.dev).
 * 部分的なアニメーターを親に統合することで、様々のギミックの実装を簡単にします。
 * 他にもいろいろ!
 
-詳しくは[ドキュメンテーションページにご参照ください](https://modular-avatar.nadena.dev/ja/).
+詳しくは[ドキュメンテーションページをご覧ください](https://modular-avatar.nadena.dev/ja/).

From 656a4016842c3216c2bf807fddd0016b076dc0d5 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 5 Oct 2024 15:19:42 -0700
Subject: [PATCH 19/39] fix: proxy animations are cloned in Merge Animator
 (#1266)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes issues with しゃがみ置き換え+α among other things.
---
 Editor/Animation/AnimationDatabase.cs         |  16 +-
 UnitTests~/MergeAnimatorTests/ProxyAnim.meta  |   8 +
 .../New Animator Controller.controller        |  72 ++++
 .../New Animator Controller.controller.meta   |   8 +
 .../ProxyAnim/ProxyAnimTest.cs                |  28 ++
 .../ProxyAnim/ProxyAnimTest.cs.meta           |   3 +
 .../ProxyAnim/ProxyAnimTest.prefab            | 378 ++++++++++++++++++
 .../ProxyAnim/ProxyAnimTest.prefab.meta       |   7 +
 8 files changed, 519 insertions(+), 1 deletion(-)
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim.meta
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab
 create mode 100644 UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta

diff --git a/Editor/Animation/AnimationDatabase.cs b/Editor/Animation/AnimationDatabase.cs
index bf788923..dd594092 100644
--- a/Editor/Animation/AnimationDatabase.cs
+++ b/Editor/Animation/AnimationDatabase.cs
@@ -53,7 +53,21 @@ namespace nadena.dev.modular_avatar.animation
                 set
                 {
                     _originalClip = value;
-                    IsProxyAnimation = value != null && Util.IsProxyAnimation(value);
+
+                    var baseClip = ObjectRegistry.GetReference(value)?.Object as AnimationClip;
+
+                    IsProxyAnimation = false;
+                    if (value != null && Util.IsProxyAnimation(value))
+                    {
+                        IsProxyAnimation = true;
+                    }
+                    else if (baseClip != null && Util.IsProxyAnimation(baseClip))
+                    {
+                        // RenameParametersPass replaces proxy clips outside of the purview of the animation database,
+                        // so trace this using ObjectRegistry and correct the reference.
+                        IsProxyAnimation = true;
+                        _originalClip = baseClip;
+                    }
                 }
             }
 
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim.meta
new file mode 100644
index 00000000..659236e2
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 130af01284a51c24f99eeb52361a81fb
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller
new file mode 100644
index 00000000..9a6333ca
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller	
@@ -0,0 +1,72 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1102 &-9046052599989551153
+AnimatorState:
+  serializedVersion: 6
+  m_ObjectHideFlags: 1
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: proxy_tpose
+  m_Speed: 1
+  m_CycleOffset: 0
+  m_Transitions: []
+  m_StateMachineBehaviours: []
+  m_Position: {x: 50, y: 50, z: 0}
+  m_IKOnFeet: 0
+  m_WriteDefaultValues: 1
+  m_Mirror: 0
+  m_SpeedParameterActive: 0
+  m_MirrorParameterActive: 0
+  m_CycleOffsetParameterActive: 0
+  m_TimeParameterActive: 0
+  m_Motion: {fileID: 7400000, guid: 645a7092829eff9478fb3a29f959a6fa, type: 2}
+  m_Tag: 
+  m_SpeedParameter: 
+  m_MirrorParameter: 
+  m_CycleOffsetParameter: 
+  m_TimeParameter: 
+--- !u!91 &9100000
+AnimatorController:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: New Animator Controller
+  serializedVersion: 5
+  m_AnimatorParameters: []
+  m_AnimatorLayers:
+  - serializedVersion: 5
+    m_Name: proxy
+    m_StateMachine: {fileID: 1953483892909110087}
+    m_Mask: {fileID: 0}
+    m_Motions: []
+    m_Behaviours: []
+    m_BlendingMode: 0
+    m_SyncedLayerIndex: -1
+    m_DefaultWeight: 0
+    m_IKPass: 0
+    m_SyncedLayerAffectsTiming: 0
+    m_Controller: {fileID: 9100000}
+--- !u!1107 &1953483892909110087
+AnimatorStateMachine:
+  serializedVersion: 6
+  m_ObjectHideFlags: 1
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: proxy
+  m_ChildStates:
+  - serializedVersion: 1
+    m_State: {fileID: -9046052599989551153}
+    m_Position: {x: 360, y: 110, z: 0}
+  m_ChildStateMachines: []
+  m_AnyStateTransitions: []
+  m_EntryTransitions: []
+  m_StateMachineTransitions: {}
+  m_StateMachineBehaviours: []
+  m_AnyStatePosition: {x: 50, y: 20, z: 0}
+  m_EntryPosition: {x: 50, y: 120, z: 0}
+  m_ExitPosition: {x: 800, y: 120, z: 0}
+  m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
+  m_DefaultState: {fileID: -9046052599989551153}
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta
new file mode 100644
index 00000000..b2d87165
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta	
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 60603c8e68ac87447b02be4e3af6a7bd
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 9100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs
new file mode 100644
index 00000000..b67e30d1
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs
@@ -0,0 +1,28 @@
+using modular_avatar_tests;
+using nadena.dev.modular_avatar.core;
+using nadena.dev.modular_avatar.core.editor;
+using NUnit.Framework;
+using UnityEditor.Animations;
+using UnityEngine;
+
+namespace UnitTests.MergeAnimatorTests.ProxyAnim
+{
+    public class ProxyAnimTest : TestBase
+    {
+        [Test]
+        public void whenProxyAnimIsMerged_itIsNotReplaced()
+        {
+            var root = CreatePrefab("ProxyAnimTest.prefab");
+
+            var originalAnimator = (AnimatorController) root.GetComponentInChildren<ModularAvatarMergeAnimator>().animator;
+            var originalClip = originalAnimator.layers[0].stateMachine.states[0].state.motion as AnimationClip;
+            
+            AvatarProcessor.ProcessAvatar(root);
+            
+            var resultLayer = findFxLayer(root, "proxy");
+            var resultClip = resultLayer.stateMachine.states[0].state.motion as AnimationClip;
+            
+            Assert.AreEqual(originalClip, resultClip);
+        }
+    }
+}
\ No newline at end of file
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta
new file mode 100644
index 00000000..bc3638bd
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 2cfba229fa1e4c718f5cb5dd579d3319
+timeCreated: 1728166108
\ No newline at end of file
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab
new file mode 100644
index 00000000..dff56d51
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab
@@ -0,0 +1,378 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &603567390109878184
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2557688482630581002}
+  - component: {fileID: 2868037606074871127}
+  - component: {fileID: 1028276594299388724}
+  - component: {fileID: 223023489903813839}
+  m_Layer: 0
+  m_Name: ProxyAnimTest
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2557688482630581002
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 603567390109878184}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 1.1618705, y: 1.0265146, z: 1.8807894}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children:
+  - {fileID: 2189711873526373063}
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!95 &2868037606074871127
+Animator:
+  serializedVersion: 5
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 603567390109878184}
+  m_Enabled: 1
+  m_Avatar: {fileID: 0}
+  m_Controller: {fileID: 0}
+  m_CullingMode: 0
+  m_UpdateMode: 0
+  m_ApplyRootMotion: 0
+  m_LinearVelocityBlending: 0
+  m_StabilizeFeet: 0
+  m_WarningMessage: 
+  m_HasTransformHierarchy: 1
+  m_AllowConstantClipSamplingOptimization: 1
+  m_KeepAnimatorStateOnDisable: 0
+  m_WriteDefaultValuesOnDisable: 0
+--- !u!114 &1028276594299388724
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 603567390109878184}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Name: 
+  ViewPosition: {x: 0, y: 1.6, z: 0.2}
+  Animations: 0
+  ScaleIPD: 1
+  lipSync: 0
+  lipSyncJawBone: {fileID: 0}
+  lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
+  lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
+  VisemeSkinnedMesh: {fileID: 0}
+  MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
+  VisemeBlendShapes: []
+  unityVersion: 
+  portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
+  portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
+  networkIDs: []
+  customExpressions: 0
+  expressionsMenu: {fileID: 0}
+  expressionParameters: {fileID: 0}
+  enableEyeLook: 0
+  customEyeLookSettings:
+    eyeMovement:
+      confidence: 0.5
+      excitement: 0.5
+    leftEye: {fileID: 0}
+    rightEye: {fileID: 0}
+    eyesLookingStraight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingUp:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingDown:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingLeft:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingRight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidType: 0
+    upperLeftEyelid: {fileID: 0}
+    upperRightEyelid: {fileID: 0}
+    lowerLeftEyelid: {fileID: 0}
+    lowerRightEyelid: {fileID: 0}
+    eyelidsDefault:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsClosed:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingUp:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingDown:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsSkinnedMesh: {fileID: 0}
+    eyelidsBlendshapes: 
+  customizeAnimationLayers: 0
+  baseAnimationLayers:
+  - isEnabled: 0
+    type: 0
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 4
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 5
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  specialAnimationLayers:
+  - isEnabled: 0
+    type: 6
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 7
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 8
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  AnimationPreset: {fileID: 0}
+  animationHashSet: []
+  autoFootsteps: 1
+  autoLocomotion: 1
+  collider_head:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_torso:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+--- !u!114 &223023489903813839
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 603567390109878184}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  launchedFromSDKPipeline: 0
+  completedSDKPipeline: 0
+  blueprintId: 
+  contentType: 0
+  assetBundleUnityVersion: 
+  fallbackStatus: 0
+--- !u!1 &1425973809379277617
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2189711873526373063}
+  - component: {fileID: 3380859768730913427}
+  m_Layer: 0
+  m_Name: GameObject
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2189711873526373063
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1425973809379277617}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 2557688482630581002}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3380859768730913427
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1425973809379277617}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1bb122659f724ebf85fe095ac02dc339, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  animator: {fileID: 9100000, guid: 60603c8e68ac87447b02be4e3af6a7bd, type: 2}
+  layerType: 5
+  deleteAttachedAnimator: 1
+  pathMode: 0
+  matchAvatarWriteDefaults: 0
+  relativePathRoot:
+    referencePath: 
+    targetObject: {fileID: 0}
+  layerPriority: 0
diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta
new file mode 100644
index 00000000..6ceea0ce
--- /dev/null
+++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a77dd3314cc88714bb6e9f1ad014cfc8
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

From 828e6b4548c4fb0817cc48f36b3ce520a9328901 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 5 Oct 2024 17:46:45 -0700
Subject: [PATCH 20/39] fix: propagate shape changer effects through
 BlendshapeSync (#1267)

Closes: #1259
---
 .../AnimationGeneration/AnimatedProperty.cs   |  32 +++++-
 .../AnimationGeneration/ControlCondition.cs   |  32 +++++-
 .../AnimationGeneration/ReactionRule.cs       |  32 +++++-
 .../ReactiveObjectAnalyzer.LocateReactions.cs | 105 +++++++++++++++++-
 .../ReactiveObjectAnalyzer.cs                 |   2 +
 5 files changed, 193 insertions(+), 10 deletions(-)

diff --git a/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs b/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs
index f56a59af..6a488a5a 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs
@@ -1,12 +1,13 @@
-using System.Collections.Generic;
-using UnityEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Object = UnityEngine.Object;
 
 namespace nadena.dev.modular_avatar.core.editor
 {
     internal class AnimatedProperty
     {
         public TargetProp TargetProp { get; }
-        public string ControlParam { get; set; }
 
         public object currentState;
 
@@ -24,5 +25,30 @@ namespace nadena.dev.modular_avatar.core.editor
             TargetProp = key;
             this.currentState = currentState;
         }
+
+        protected bool Equals(AnimatedProperty other)
+        {
+            return Equals(currentState, other.currentState) && actionGroups.SequenceEqual(other.actionGroups) &&
+                   TargetProp.Equals(other.TargetProp);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj is null) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != GetType()) return false;
+            return Equals((AnimatedProperty)obj);
+        }
+
+        public override int GetHashCode()
+        {
+            var actionGroupHash = 0;
+            foreach (var ag in actionGroups)
+            {
+                actionGroupHash = HashCode.Combine(actionGroupHash, ag);
+            }
+
+            return HashCode.Combine(currentState, actionGroupHash, TargetProp);
+        }
     }
 }
\ No newline at end of file
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs b/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs
index 33368ea5..f61b33e0 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs
@@ -1,11 +1,13 @@
-using UnityEngine;
+using System;
+using UnityEngine;
+using Object = UnityEngine.Object;
 
 namespace nadena.dev.modular_avatar.core.editor
 {
     internal class ControlCondition
     {
         public string Parameter;
-        public UnityEngine.Object DebugReference;
+        public Object DebugReference;
         
         public string DebugName;
         public bool IsConstant;
@@ -14,5 +16,31 @@ namespace nadena.dev.modular_avatar.core.editor
         public bool IsConstantActive => InitiallyActive && IsConstant;
 
         public GameObject ReferenceObject;
+
+        protected bool Equals(ControlCondition other)
+        {
+            return Parameter == other.Parameter
+                   && Equals(DebugReference, other.DebugReference)
+                   && DebugName == other.DebugName
+                   && IsConstant == other.IsConstant
+                   && ParameterValueLo.Equals(other.ParameterValueLo)
+                   && ParameterValueHi.Equals(other.ParameterValueHi)
+                   && InitialValue.Equals(other.InitialValue)
+                   && Equals(ReferenceObject, other.ReferenceObject);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj is null) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != GetType()) return false;
+            return Equals((ControlCondition)obj);
+        }
+
+        public override int GetHashCode()
+        {
+            return HashCode.Combine(Parameter, DebugReference, DebugName, IsConstant, ParameterValueLo,
+                ParameterValueHi, InitialValue, ReferenceObject);
+        }
     }
 }
\ No newline at end of file
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
index 12bea0cb..497046ab 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs
@@ -1,6 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using UnityEngine;
+using Object = UnityEngine.Object;
 
 namespace nadena.dev.modular_avatar.core.editor
 {
@@ -59,5 +61,33 @@ namespace nadena.dev.modular_avatar.core.editor
 
             return true;
         }
+
+        protected bool Equals(ReactionRule other)
+        {
+            return TargetProp.Equals(other.TargetProp)
+                   && Equals(Value, other.Value)
+                   && Equals(ControllingObject, other.ControllingObject)
+                   && ControllingConditions.SequenceEqual(other.ControllingConditions)
+                   && Inverted == other.Inverted;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj is null) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != GetType()) return false;
+            return Equals((ReactionRule)obj);
+        }
+
+        public override int GetHashCode()
+        {
+            var ccHash = 0;
+            foreach (var cc in ControllingConditions)
+            {
+                ccHash = HashCode.Combine(ccHash, cc);
+            }
+
+            return HashCode.Combine(TargetProp, Value, ControllingObject, ccHash, Inverted);
+        }
     }
 }
\ No newline at end of file
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs
index f2e500e7..179e4ce4 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs
@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Linq;
 using nadena.dev.ndmf.preview;
 using UnityEngine;
@@ -38,6 +39,77 @@ namespace nadena.dev.modular_avatar.core.editor
                 return param;
             }
         }
+
+        private readonly Dictionary<(SkinnedMeshRenderer, string), HashSet<(SkinnedMeshRenderer, string)>>
+            _blendshapeSyncMappings = new();
+
+        private void LocateBlendshapeSyncs(GameObject root)
+        {
+            var components = _computeContext.GetComponentsInChildren<ModularAvatarBlendshapeSync>(root, true);
+
+            foreach (var bss in components)
+            {
+                var localMesh = _computeContext.GetComponent<SkinnedMeshRenderer>(bss.gameObject);
+                if (localMesh == null) continue;
+
+                foreach (var entry in _computeContext.Observe(bss, bss_ => bss_.Bindings.ToImmutableList(),
+                             Enumerable.SequenceEqual))
+                {
+                    var src = entry.ReferenceMesh.Get(bss);
+                    if (src == null) continue;
+
+                    var srcMesh = _computeContext.GetComponent<SkinnedMeshRenderer>(src);
+
+                    var localBlendshape = entry.LocalBlendshape;
+                    if (string.IsNullOrWhiteSpace(localBlendshape))
+                    {
+                        localBlendshape = entry.Blendshape;
+                    }
+
+                    var srcBinding = (srcMesh, entry.Blendshape);
+                    var dstBinding = (localMesh, localBlendshape);
+
+                    if (!_blendshapeSyncMappings.TryGetValue(srcBinding, out var dstSet))
+                    {
+                        dstSet = new HashSet<(SkinnedMeshRenderer, string)>();
+                        _blendshapeSyncMappings[srcBinding] = dstSet;
+                    }
+
+                    dstSet.Add(dstBinding);
+                }
+            }
+
+            // For recursive blendshape syncs, we need to precompute the full set of affected blendshapes.
+            foreach (var (src, dsts) in _blendshapeSyncMappings)
+            {
+                var visited = new HashSet<(SkinnedMeshRenderer, string)>();
+                foreach (var item in Visit(src, visited).ToList())
+                {
+                    dsts.Add(item);
+                }
+            }
+
+            IEnumerable<(SkinnedMeshRenderer, string)> Visit(
+                (SkinnedMeshRenderer, string) key,
+                HashSet<(SkinnedMeshRenderer, string)> visited
+            )
+            {
+                if (!visited.Add(key)) yield break;
+
+                if (_blendshapeSyncMappings.TryGetValue(key, out var children))
+                {
+                    foreach (var child in children)
+                    {
+                        foreach (var item in Visit(child, visited))
+                        {
+                            yield return item;
+                        }
+                    }
+                }
+
+                yield return key;
+            }
+        }
         
         private void BuildConditions(Component controllingComponent, ReactionRule rule)
         {
@@ -130,8 +202,34 @@ namespace nadena.dev.modular_avatar.core.editor
                     var currentValue = renderer.GetBlendShapeWeight(shapeId);
                     var value = shape.ChangeType == ShapeChangeType.Delete ? 100 : shape.Value;
 
-                    RegisterAction(key, renderer, currentValue, value, changer, shape);
+                    RegisterAction(key, currentValue, value, changer);
 
+                    if (_blendshapeSyncMappings.TryGetValue((renderer, shape.ShapeName), out var bindings))
+                    {
+                        // Propagate the new value through any Blendshape Syncs we might have.
+                        // Note that we don't propagate deletes; it's common to e.g. want to delete breasts from the
+                        // base model while retaining outerwear that matches the breast size.
+                        foreach (var binding in bindings)
+                        {
+                            var bindingKey = new TargetProp
+                            {
+                                TargetObject = binding.Item1,
+                                PropertyName = BlendshapePrefix + binding.Item2
+                            };
+                            var bindingRenderer = binding.Item1;
+
+                            var bindingMesh = bindingRenderer.sharedMesh;
+                            if (bindingMesh == null) continue;
+
+                            var bindingShapeIndex = bindingMesh.GetBlendShapeIndex(binding.Item2);
+                            if (bindingShapeIndex < 0) continue;
+
+                            var bindingInitialState = bindingRenderer.GetBlendShapeWeight(bindingShapeIndex);
+
+                            RegisterAction(bindingKey, bindingInitialState, value, changer);
+                        }
+                    }
+                    
                     key = new TargetProp
                     {
                         TargetObject = renderer,
@@ -139,14 +237,13 @@ namespace nadena.dev.modular_avatar.core.editor
                     };
 
                     value = shape.ChangeType == ShapeChangeType.Delete ? 1 : 0;
-                    RegisterAction(key, renderer, 0, value, changer, shape);
+                    RegisterAction(key, 0, value, changer);
                 }
             }
 
             return shapeKeys;
 
-            void RegisterAction(TargetProp key, SkinnedMeshRenderer renderer, float currentValue, float value,
-                ModularAvatarShapeChanger changer, ChangedShape shape)
+            void RegisterAction(TargetProp key, float currentValue, float value, ModularAvatarShapeChanger changer)
             {
                 if (!shapeKeys.TryGetValue(key, out var info))
                 {
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
index 1c5b97b2..643c4a4e 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
@@ -101,6 +101,8 @@ namespace nadena.dev.modular_avatar.core.editor
                 result.InitialStates = new();
                 return result;
             }
+
+            LocateBlendshapeSyncs(root); 
             
             Dictionary<TargetProp, AnimatedProperty> shapes = FindShapes(root);
             FindObjectToggles(shapes, root);

From 1024f626e84b07eeb2a64d663826b541994b4cb1 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 5 Oct 2024 17:55:29 -0700
Subject: [PATCH 21/39] 1.10.3

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index b6141b32..778562f3 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "nadena.dev.modular-avatar",
   "displayName": "Modular Avatar",
-  "version": "1.10.2",
+  "version": "1.10.3",
   "unity": "2022.3",
   "description": "A suite of tools for assembling your avatar out of reusable components",
   "author": {
@@ -16,6 +16,6 @@
   },
   "vpmDependencies": {
     "com.vrchat.avatars": ">=3.7.0",
-    "nadena.dev.ndmf": ">=1.5.3 <2.0.0-a"
+    "nadena.dev.ndmf": ">=1.5.4 <2.0.0-a"
   }
 }

From 4a376f8723689f514e646bba43aa513e8f76dd57 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Sun, 13 Oct 2024 08:34:30 +0900
Subject: [PATCH 22/39] =?UTF-8?q?Merge=20Armature=E3=81=AE"Reset=20positio?=
 =?UTF-8?q?n=20to=20base=20avatar"=E3=81=ABA/T=E3=83=9D=E3=83=BC=E3=82=BA?=
 =?UTF-8?q?=E5=A4=89=E6=8F=9B=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1188)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: A/T Pose conversion on "Reset position to base avatar"

* chore: reorder posReset options

* chore: unify FixAPose functions into SetupOutfit.FixAPose
---
 Editor/Inspector/MergeArmatureEditor.cs | 17 +++++++++++++----
 Editor/Localization/en-US.json          |  1 +
 Editor/Localization/ja-JP.json          |  1 +
 Editor/SetupOutfit.cs                   | 23 ++++++++++++++---------
 4 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/Editor/Inspector/MergeArmatureEditor.cs b/Editor/Inspector/MergeArmatureEditor.cs
index 3785a608..0fd215b4 100644
--- a/Editor/Inspector/MergeArmatureEditor.cs
+++ b/Editor/Inspector/MergeArmatureEditor.cs
@@ -84,6 +84,7 @@ namespace nadena.dev.modular_avatar.core.editor
         }
 
         private bool posResetOptionFoldout = false;
+        private bool posReset_convertATPose = true;
         private bool posReset_adjustRotation = false;
         private bool posReset_adjustScale = false;
         private bool posReset_heuristicRootScale = true;
@@ -134,14 +135,17 @@ namespace nadena.dev.modular_avatar.core.editor
                             MessageType.Info
                         );
 
+                        posReset_heuristicRootScale = EditorGUILayout.ToggleLeft(
+                            G("merge_armature.reset_pos.heuristic_scale"),
+                            posReset_heuristicRootScale);
+                        posReset_convertATPose = EditorGUILayout.ToggleLeft(
+                            G("merge_armature.reset_pos.convert_atpose"),
+                            posReset_convertATPose);
                         posReset_adjustRotation = EditorGUILayout.ToggleLeft(
                             G("merge_armature.reset_pos.adjust_rotation"),
                             posReset_adjustRotation);
                         posReset_adjustScale = EditorGUILayout.ToggleLeft(G("merge_armature.reset_pos.adjust_scale"),
                             posReset_adjustScale);
-                        posReset_heuristicRootScale = EditorGUILayout.ToggleLeft(
-                            G("merge_armature.reset_pos.heuristic_scale"),
-                            posReset_heuristicRootScale);
 
                         if (GUILayout.Button(G("merge_armature.reset_pos.execute")))
                         {
@@ -188,6 +192,11 @@ namespace nadena.dev.modular_avatar.core.editor
                 }
             }
 
+            if (posReset_convertATPose)
+            {
+                SetupOutfit.FixAPose(RuntimeUtil.FindAvatarTransformInParents(mergeTarget.transform).gameObject, mama.transform, false);
+            }
+
             if (posReset_heuristicRootScale && !suppressRootScale)
             {
                 AdjustRootScale();
@@ -279,4 +288,4 @@ namespace nadena.dev.modular_avatar.core.editor
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Editor/Localization/en-US.json b/Editor/Localization/en-US.json
index bc7e8ec8..8d84f379 100644
--- a/Editor/Localization/en-US.json
+++ b/Editor/Localization/en-US.json
@@ -86,6 +86,7 @@
   "merge_armature.lockmode.bidirectional.body": "The base armature and the merged armature will always have the same position. This is useful when creating animations that are meant to target the base armature. In order to activate this, your armatures must already be in the exact same position.",
   "merge_armature.reset_pos": "Reset position to base avatar",
   "merge_armature.reset_pos.info": "This command will force the position of all bones in the outfit to match that of the base avatar. This can be helpful as a starting point for installing outfits not set up for your current avatar.",
+  "merge_armature.reset_pos.convert_atpose": "Convert A-Pose/T-Pose to match base avatar",
   "merge_armature.reset_pos.adjust_rotation": "Also set rotation to base avatar",
   "merge_armature.reset_pos.adjust_scale": "Also set local scale to base avatar",
   "merge_armature.reset_pos.execute": "Do it!",
diff --git a/Editor/Localization/ja-JP.json b/Editor/Localization/ja-JP.json
index 94c6022a..95c10a1b 100644
--- a/Editor/Localization/ja-JP.json
+++ b/Editor/Localization/ja-JP.json
@@ -82,6 +82,7 @@
   "merge_armature.lockmode.bidirectional.body": "アバターと統合されるアーマチュアは常に同じ位置になります。元のアバターを操作するアニメーションを作る時に便利かもしれません。有効にするためには、統合されるアーマチュアの位置を統合先と同じにしておく必要があります。",
   "merge_armature.reset_pos": "位置を元アバターに合わせてリセット",
   "merge_armature.reset_pos.info": "衣装のボーンの位置をアバターのボーンの位置に合わせます。非対応衣装を導入する際、アバウトに位置を合わせるのに便利です。",
+  "merge_armature.reset_pos.convert_atpose": "Aポーズ/Tポーズを合わせる",
   "merge_armature.reset_pos.adjust_rotation": "回転も合わせる",
   "merge_armature.reset_pos.adjust_scale": "スケールも合わせる",
   "merge_armature.reset_pos.execute": "実行",
diff --git a/Editor/SetupOutfit.cs b/Editor/SetupOutfit.cs
index 922fbc74..ba69ed9f 100644
--- a/Editor/SetupOutfit.cs
+++ b/Editor/SetupOutfit.cs
@@ -229,7 +229,7 @@ namespace nadena.dev.modular_avatar.core.editor
             }
         }
 
-        private static void FixAPose(GameObject avatarRoot, Transform outfitArmature)
+        internal static void FixAPose(GameObject avatarRoot, Transform outfitArmature, bool strictMode = true)
         {
             var mergeArmature = outfitArmature.GetComponent<ModularAvatarMergeArmature>();
             if (mergeArmature == null) return;
@@ -249,7 +249,7 @@ namespace nadena.dev.modular_avatar.core.editor
             {
                 var lowerArm = (HumanBodyBones)((int)arm + 2);
 
-                // check if the rotation of the arm differs, but distances and origin point are the same
+                // check if the rotation of the arm differs(, but distances and origin point are the same when strictMode)
                 var avatarArm = rootAnimator.GetBoneTransform(arm);
                 var outfitArm = avatarToOutfit(avatarArm);
 
@@ -259,22 +259,27 @@ namespace nadena.dev.modular_avatar.core.editor
                 if (outfitArm == null) return;
                 if (outfitLowerArm == null) return;
 
-                if ((avatarArm.position - outfitArm.position).magnitude > 0.001f) return;
+                if (strictMode)
+                {
+                    if ((avatarArm.position - outfitArm.position).magnitude > 0.001f) return;
 
-                // check relative distance to lower arm as well
-                var avatarArmLength = (avatarLowerArm.position - avatarArm.position).magnitude;
-                var outfitArmLength = (outfitLowerArm.position - outfitArm.position).magnitude;
+                    // check relative distance to lower arm as well
+                    var avatarArmLength = (avatarLowerArm.position - avatarArm.position).magnitude;
+                    var outfitArmLength = (outfitLowerArm.position - outfitArm.position).magnitude;
 
-                if (Mathf.Abs(avatarArmLength - outfitArmLength) > 0.001f) return;
+                    if (Mathf.Abs(avatarArmLength - outfitArmLength) > 0.001f) return;
+                } else {
+                    if (Vector3.Dot((outfitLowerArm.position - outfitArm.position).normalized, (avatarLowerArm.position - avatarArm.position).normalized) > 0.999f) return;
+                }
 
-                // Rotate the outfit arm to ensure these two points match.
+                // Rotate the outfit arm to ensure these two bone orientations match.
+                Undo.RecordObject(outfitArm, "Convert A/T Pose");
                 var relRot = Quaternion.FromToRotation(
                     outfitLowerArm.position - outfitArm.position,
                     avatarLowerArm.position - avatarArm.position
                 );
                 outfitArm.rotation = relRot * outfitArm.rotation;
                 PrefabUtility.RecordPrefabInstancePropertyModifications(outfitArm);
-                EditorUtility.SetDirty(outfitArm);
             }
 
             Transform avatarToOutfit(Transform avBone)

From 7f9e65bcbcbb7833d337580dab7f362b9315c429 Mon Sep 17 00:00:00 2001
From: Mooncake Sugar <60507815+Tsukina-7mochi@users.noreply.github.com>
Date: Sun, 13 Oct 2024 08:35:54 +0900
Subject: [PATCH 23/39] docs: delete button group on top page (#1270)

---
 docs~/src/pages/index.module.css | 3 ++-
 docs~/src/pages/index.tsx        | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/docs~/src/pages/index.module.css b/docs~/src/pages/index.module.css
index a8166690..9ef0a6aa 100644
--- a/docs~/src/pages/index.module.css
+++ b/docs~/src/pages/index.module.css
@@ -23,6 +23,7 @@
   justify-content: center;
   column-gap: 2rem;
   row-gap: 1rem;
+  margin-bottom: 1em;
 }
 
 @media screen and (max-width: 996px) {
@@ -36,4 +37,4 @@ div.logo {
     text-align: center;
     display: flex;
     justify-content: center;
-}
\ No newline at end of file
+}
diff --git a/docs~/src/pages/index.tsx b/docs~/src/pages/index.tsx
index 88796298..889f8ced 100644
--- a/docs~/src/pages/index.tsx
+++ b/docs~/src/pages/index.tsx
@@ -29,7 +29,7 @@ function HomepageHeader() {
         <p className="hero__subtitle">
             <Translate>Drag-and-Drop Avatar Assembly</Translate>
         </p>
-        <div className={`button-group ${styles.buttons}`}>
+        <div className={`${styles.buttons}`}>
           <InstallButton/>
           <Link
             className={`button button--secondary button--lg ${styles.button}`}
@@ -43,7 +43,7 @@ function HomepageHeader() {
             <Translate>Tutorials</Translate>
           </Link>
         </div>
-        <div className={`button-group ${styles.buttons}`}>
+        <div className={`${styles.buttons}`}>
             <a href={"https://discord.gg/dV4cVpewmM"} className={`discordLink`}>
                 <img className={`button button--lg ${styles.button}`}
                      alt="Discord"

From 0b8cd3b3b6f606b54fb8c10bf1dff57f7edf28b1 Mon Sep 17 00:00:00 2001
From: nekobako <nekobako.tofu@gmail.com>
Date: Sun, 13 Oct 2024 08:52:37 +0900
Subject: [PATCH 24/39] feat: add support for drag-and-drop on MaterialSetter
 and ShapeChanger (#1271)

* feat: add support for drag-and-drop on MaterialSetter and ShapeChanger

* fix: allow adding known objects to MaterialSetter and ShapeChanger
---
 Editor/Inspector/DragAndDropManipulator.cs    | 106 ++++++++++++++++++
 .../Inspector/DragAndDropManipulator.cs.meta  |  11 ++
 .../MaterialSetter/MaterialSetterEditor.cs    |  38 +++++++
 .../MaterialSetter/MaterialSetterStyles.uss   |  10 ++
 .../ObjectToggle/ObjectSwitcherEditor.cs      |  94 +++-------------
 .../ObjectToggle/ObjectSwitcherStyles.uss     |  10 +-
 .../ShapeChanger/ShapeChangerEditor.cs        |  38 +++++++
 .../ShapeChanger/ShapeChangerStyles.uss       |  10 ++
 8 files changed, 234 insertions(+), 83 deletions(-)
 create mode 100644 Editor/Inspector/DragAndDropManipulator.cs
 create mode 100644 Editor/Inspector/DragAndDropManipulator.cs.meta

diff --git a/Editor/Inspector/DragAndDropManipulator.cs b/Editor/Inspector/DragAndDropManipulator.cs
new file mode 100644
index 00000000..0d3de862
--- /dev/null
+++ b/Editor/Inspector/DragAndDropManipulator.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace nadena.dev.modular_avatar.core.editor
+{
+    internal abstract class DragAndDropManipulator<T> : PointerManipulator where T : Component, IHaveObjReferences
+    {
+        private const string DragActiveClassName = "drop-area--drag-active";
+
+        public T TargetComponent { get; set; }
+
+        protected virtual bool AllowKnownObjects => true;
+
+        private Transform _avatarRoot;
+        private GameObject[] _draggingObjects = Array.Empty<GameObject>();
+
+        public DragAndDropManipulator(VisualElement targetElement, T targetComponent)
+        {
+            target = targetElement;
+            TargetComponent = targetComponent;
+        }
+
+        protected sealed override void RegisterCallbacksOnTarget()
+        {
+            target.RegisterCallback<DragEnterEvent>(OnDragEnter);
+            target.RegisterCallback<DragLeaveEvent>(OnDragLeave);
+            target.RegisterCallback<DragExitedEvent>(OnDragExited);
+            target.RegisterCallback<DragUpdatedEvent>(OnDragUpdated);
+            target.RegisterCallback<DragPerformEvent>(OnDragPerform);
+        }
+
+        protected sealed override void UnregisterCallbacksFromTarget()
+        {
+            target.UnregisterCallback<DragEnterEvent>(OnDragEnter);
+            target.UnregisterCallback<DragLeaveEvent>(OnDragLeave);
+            target.UnregisterCallback<DragExitedEvent>(OnDragExited);
+            target.UnregisterCallback<DragUpdatedEvent>(OnDragUpdated);
+            target.UnregisterCallback<DragPerformEvent>(OnDragPerform);
+        }
+
+        private void OnDragEnter(DragEnterEvent _)
+        {
+            if (TargetComponent == null) return;
+
+            _avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform);
+            if (_avatarRoot == null) return;
+
+            var knownObjects = TargetComponent.GetObjectReferences().Select(x => x.Get(TargetComponent)).ToHashSet();
+            _draggingObjects = DragAndDrop.objectReferences.OfType<GameObject>()
+                .Where(x => AllowKnownObjects || !knownObjects.Contains(x))
+                .Where(x => RuntimeUtil.FindAvatarTransformInParents(x.transform) == _avatarRoot)
+                .Where(FilterGameObject)
+                .ToArray();
+            if (_draggingObjects.Length == 0) return;
+
+            target.AddToClassList(DragActiveClassName);
+        }
+
+        private void OnDragLeave(DragLeaveEvent _)
+        {
+            _draggingObjects = Array.Empty<GameObject>();
+            target.RemoveFromClassList(DragActiveClassName);
+        }
+
+        private void OnDragExited(DragExitedEvent _)
+        {
+            _draggingObjects = Array.Empty<GameObject>();
+            target.RemoveFromClassList(DragActiveClassName);
+        }
+
+        private void OnDragUpdated(DragUpdatedEvent _)
+        {
+            if (TargetComponent == null) return;
+            if (_avatarRoot == null) return;
+            if (_draggingObjects.Length == 0) return;
+
+            DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
+        }
+
+        private void OnDragPerform(DragPerformEvent _)
+        {
+            if (TargetComponent == null) return;
+            if (_avatarRoot == null) return;
+            if (_draggingObjects.Length == 0) return;
+
+            AddObjectReferences(_draggingObjects
+                .Select(x =>
+                {
+                    var reference = new AvatarObjectReference();
+                    reference.Set(x);
+                    return reference;
+                })
+                .ToArray());
+        }
+
+        protected virtual bool FilterGameObject(GameObject obj)
+        {
+            return true;
+        }
+
+        protected abstract void AddObjectReferences(AvatarObjectReference[] references);
+    }
+}
diff --git a/Editor/Inspector/DragAndDropManipulator.cs.meta b/Editor/Inspector/DragAndDropManipulator.cs.meta
new file mode 100644
index 00000000..b78bdf93
--- /dev/null
+++ b/Editor/Inspector/DragAndDropManipulator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 528c660b56905844ea2f88bc73837e9f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs b/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs
index c7410eb8..a2b055b3 100644
--- a/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs
+++ b/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs
@@ -16,6 +16,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
         [SerializeField] private StyleSheet uss;
         [SerializeField] private VisualTreeAsset uxml;
 
+        private DragAndDropManipulator _dragAndDropManipulator;
 
         protected override void OnInnerInspectorGUI()
         {
@@ -37,7 +38,44 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
             listView.showBoundCollectionSize = false;
             listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
 
+            _dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarMaterialSetter);
+
             return root;
         }
+
+        private void OnEnable()
+        {
+            if (_dragAndDropManipulator != null)
+                _dragAndDropManipulator.TargetComponent = target as ModularAvatarMaterialSetter;
+        }
+
+        private class DragAndDropManipulator : DragAndDropManipulator<ModularAvatarMaterialSetter>
+        {
+            public DragAndDropManipulator(VisualElement targetElement, ModularAvatarMaterialSetter targetComponent)
+                : base(targetElement, targetComponent) { }
+
+            protected override bool FilterGameObject(GameObject obj)
+            {
+                if (obj.TryGetComponent<Renderer>(out var renderer))
+                {
+                    return renderer.sharedMaterials.Length > 0;
+                }
+                return false;
+            }
+
+            protected override void AddObjectReferences(AvatarObjectReference[] references)
+            {
+                Undo.RecordObject(TargetComponent, "Add Material Switch Objects");
+
+                foreach (var reference in references)
+                {
+                    var materialSwitchObject = new MaterialSwitchObject { Object = reference, MaterialIndex = 0 };
+                    TargetComponent.Objects.Add(materialSwitchObject);
+                }
+
+                EditorUtility.SetDirty(TargetComponent);
+                PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss b/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss
index 84204231..8e422d2d 100644
--- a/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss
+++ b/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss
@@ -62,3 +62,13 @@
 #f-material {
     flex-grow: 1;
 }
+
+.drop-area--drag-active {
+    background-color: rgba(0, 127, 255, 0.2);
+}
+
+.drop-area--drag-active .unity-scroll-view,
+.drop-area--drag-active .unity-list-view__footer,
+.drop-area--drag-active .unity-list-view__reorderable-item {
+    background-color: rgba(0, 0, 0, 0.0);
+}
diff --git a/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs b/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs
index 627121b7..999f596c 100644
--- a/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs
+++ b/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs
@@ -35,14 +35,12 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
             ROSimulatorButton.BindRefObject(root, target);
 
             var listView = root.Q<ListView>("Shapes");
-            _dragAndDropManipulator = new DragAndDropManipulator(listView)
-            {
-                TargetComponent = target as ModularAvatarObjectToggle
-            };
 
             listView.showBoundCollectionSize = false;
             listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
 
+            _dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarObjectToggle);
+
             return root;
         }
 
@@ -52,91 +50,25 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
                 _dragAndDropManipulator.TargetComponent = target as ModularAvatarObjectToggle;
         }
 
-        private class DragAndDropManipulator : PointerManipulator
+        private class DragAndDropManipulator : DragAndDropManipulator<ModularAvatarObjectToggle>
         {
-            public ModularAvatarObjectToggle TargetComponent;
-            private GameObject[] _nowDragging = Array.Empty<GameObject>();
-            private Transform _avatarRoot;
+            public DragAndDropManipulator(VisualElement targetElement, ModularAvatarObjectToggle targetComponent)
+                : base(targetElement, targetComponent) { }
 
-            private readonly VisualElement _parentElem;
+            protected override bool AllowKnownObjects => false;
 
-            public DragAndDropManipulator(VisualElement target)
+            protected override void AddObjectReferences(AvatarObjectReference[] references)
             {
-                this.target = target;
-                _parentElem = target.parent;
-            }
+                Undo.RecordObject(TargetComponent, "Add Toggled Objects");
 
-            protected override void RegisterCallbacksOnTarget()
-            {
-                target.RegisterCallback<DragEnterEvent>(OnDragEnter);
-                target.RegisterCallback<DragLeaveEvent>(OnDragLeave);
-                target.RegisterCallback<DragPerformEvent>(OnDragPerform);
-                target.RegisterCallback<DragUpdatedEvent>(OnDragUpdate);
-            }
-
-            protected override void UnregisterCallbacksFromTarget()
-            {
-                target.UnregisterCallback<DragEnterEvent>(OnDragEnter);
-                target.UnregisterCallback<DragLeaveEvent>(OnDragLeave);
-                target.UnregisterCallback<DragPerformEvent>(OnDragPerform);
-                target.RegisterCallback<DragUpdatedEvent>(OnDragUpdate);
-            }
-
-
-            private void OnDragEnter(DragEnterEvent evt)
-            {
-                if (TargetComponent == null) return;
-
-                _avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform);
-                if (_avatarRoot == null) return;
-
-                _nowDragging = DragAndDrop.objectReferences.OfType<GameObject>()
-                    .Where(o => RuntimeUtil.FindAvatarTransformInParents(o.transform) == _avatarRoot)
-                    .ToArray();
-
-                if (_nowDragging.Length > 0)
+                foreach (var reference in references)
                 {
-                    DragAndDrop.visualMode = DragAndDropVisualMode.Link;
-
-                    _parentElem.AddToClassList("drop-area--drag-active");
-                }
-            }
-
-            private void OnDragUpdate(DragUpdatedEvent _)
-            {
-                if (_nowDragging.Length > 0) DragAndDrop.visualMode = DragAndDropVisualMode.Link;
-            }
-
-            private void OnDragLeave(DragLeaveEvent evt)
-            {
-                _nowDragging = Array.Empty<GameObject>();
-                _parentElem.RemoveFromClassList("drop-area--drag-active");
-            }
-
-            private void OnDragPerform(DragPerformEvent evt)
-            {
-                if (_nowDragging.Length > 0 && TargetComponent != null && _avatarRoot != null)
-                {
-                    var knownObjs = TargetComponent.Objects.Select(o => o.Object.Get(TargetComponent)).ToHashSet();
-
-                    Undo.RecordObject(TargetComponent, "Add Toggled Objects");
-                    foreach (var obj in _nowDragging)
-                    {
-                        if (knownObjs.Contains(obj)) continue;
-
-                        var aor = new AvatarObjectReference();
-                        aor.Set(obj);
-
-                        var toggledObject = new ToggledObject { Object = aor, Active = !obj.activeSelf };
-                        TargetComponent.Objects.Add(toggledObject);
-                    }
-
-                    EditorUtility.SetDirty(TargetComponent);
-                    PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
+                    var toggledObject = new ToggledObject { Object = reference, Active = !reference.Get(TargetComponent).activeSelf };
+                    TargetComponent.Objects.Add(toggledObject);
                 }
 
-                _nowDragging = Array.Empty<GameObject>();
-                _parentElem.RemoveFromClassList("drop-area--drag-active");
+                EditorUtility.SetDirty(TargetComponent);
+                PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
             }
         }
     }
diff --git a/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss b/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss
index d4bdedc5..12402b5d 100644
--- a/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss
+++ b/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss
@@ -51,6 +51,12 @@
     width: 60px;
 }
 
-.drop-area--drag-active > ListView ScrollView {
-    background-color: rgba(0, 255, 255, 0.1);
+.drop-area--drag-active {
+    background-color: rgba(0, 127, 255, 0.2);
+}
+
+.drop-area--drag-active .unity-scroll-view,
+.drop-area--drag-active .unity-list-view__footer,
+.drop-area--drag-active .unity-list-view__reorderable-item {
+    background-color: rgba(0, 0, 0, 0.0);
 }
diff --git a/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs b/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs
index 9a278278..9bec32a3 100644
--- a/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs
+++ b/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs
@@ -19,6 +19,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
         [SerializeField] private StyleSheet uss;
         [SerializeField] private VisualTreeAsset uxml;
 
+        private DragAndDropManipulator _dragAndDropManipulator;
         private BlendshapeSelectWindow _window;
 
         protected override void OnInnerInspectorGUI()
@@ -41,6 +42,8 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
             listView.showBoundCollectionSize = false;
             listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
 
+            _dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarShapeChanger);
+
             // The Add button callback isn't exposed publicly for some reason...
             var field_addButton = typeof(BaseListView).GetField("m_AddButton", NonPublic | Instance);
             var addButton = (Button)field_addButton.GetValue(listView);
@@ -50,6 +53,41 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
             return root;
         }
 
+        private void OnEnable()
+        {
+            if (_dragAndDropManipulator != null)
+                _dragAndDropManipulator.TargetComponent = target as ModularAvatarShapeChanger;
+        }
+
+        private class DragAndDropManipulator : DragAndDropManipulator<ModularAvatarShapeChanger>
+        {
+            public DragAndDropManipulator(VisualElement targetElement, ModularAvatarShapeChanger targetComponent)
+                : base(targetElement, targetComponent) { }
+
+            protected override bool FilterGameObject(GameObject obj)
+            {
+                if (obj.TryGetComponent<SkinnedMeshRenderer>(out var smr))
+                {
+                    return smr.sharedMesh != null && smr.sharedMesh.blendShapeCount > 0;
+                }
+                return false;
+            }
+
+            protected override void AddObjectReferences(AvatarObjectReference[] references)
+            {
+                Undo.RecordObject(TargetComponent, "Add Changed Shapes");
+
+                foreach (var reference in references)
+                {
+                    var changedShape = new ChangedShape { Object = reference, ShapeName = string.Empty };
+                    TargetComponent.Shapes.Add(changedShape);
+                }
+
+                EditorUtility.SetDirty(TargetComponent);
+                PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
+            }
+        }
+
         private void OnDisable()
         {
             if (_window != null) DestroyImmediate(_window);
diff --git a/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss b/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss
index adff445e..e74734ff 100644
--- a/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss
+++ b/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss
@@ -68,3 +68,13 @@
 .change-type-delete #f-value-delete {
     display: flex;
 }
+
+.drop-area--drag-active {
+    background-color: rgba(0, 127, 255, 0.2);
+}
+
+.drop-area--drag-active .unity-scroll-view,
+.drop-area--drag-active .unity-list-view__footer,
+.drop-area--drag-active .unity-list-view__reorderable-item {
+    background-color: rgba(0, 0, 0, 0.0);
+}

From 7ae98d63b096bf8bc89742e9dc8e9750e017c3b6 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 12 Oct 2024 17:29:19 -0700
Subject: [PATCH 25/39] fix: missing localization strings for Replace Object
 errors (#1282)

Closes: #1281
---
 Editor/Localization/en-US.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Editor/Localization/en-US.json b/Editor/Localization/en-US.json
index 8d84f379..07a5a565 100644
--- a/Editor/Localization/en-US.json
+++ b/Editor/Localization/en-US.json
@@ -150,6 +150,8 @@
   "error.rename_params.default_value_conflict:hint": "To avoid unpredictable behavior, leave the default value field blank in all but on MA Parameters component. If multiple values are present, Modular Avatar will select the first default value specified in the hierarchy order.",
   "error.replace_object.null_target": "[MA-0008] No target specified",
   "error.replace_object.null_target:hint": "Replace object needs a target object to replace. Try setting one.",
+  "error.replace_object.replacing_replacement": "[MA-0009] The same target object cannot be specified in multiple Replace Object components",
+  "error.replace_object.parent_of_target": "[MA-0010] The target object cannot be a parent of this object",
   "validation.blendshape_sync.no_local_renderer": "[MA-1000] No renderer found on this object",
   "validation.blendshape_sync.no_local_renderer:hint": "Blendshape Sync acts on a Skinned Mesh Renderer on the same GameObject. Did you attach it to the right object?",
   "validation.blendshape_sync.no_local_mesh": "[MA-1001] No mesh found on the renderer on this object",

From f40d02ceb95a4583adb7d33fd3b7d8785dbded29 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 12 Oct 2024 17:29:27 -0700
Subject: [PATCH 26/39] fix: NRE when `AnimatorControllerLayer.stateMachine` is
 null (#1283)

Closes: #1056
---
 Editor/Animation/AnimatorCombiner.cs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Editor/Animation/AnimatorCombiner.cs b/Editor/Animation/AnimatorCombiner.cs
index 674140dc..6a64fe28 100644
--- a/Editor/Animation/AnimatorCombiner.cs
+++ b/Editor/Animation/AnimatorCombiner.cs
@@ -573,6 +573,8 @@ namespace nadena.dev.modular_avatar.animation
 
         private AnimatorStateMachine mapStateMachine(string basePath, AnimatorStateMachine layerStateMachine)
         {
+            if (layerStateMachine == null) return null;
+            
             var cacheKey = new KeyValuePair<string, AnimatorStateMachine>(basePath, layerStateMachine);
 
             if (_stateMachines.TryGetValue(cacheKey, out var asm))

From 766f728a8aa65c502bfff3276731127fe39a6d8a Mon Sep 17 00:00:00 2001
From: nadena-dev-ci <bd_+ci-bot@nadena.dev>
Date: Mon, 14 Oct 2024 10:28:53 +0900
Subject: [PATCH 27/39] New Crowdin updates (#1284)

* Update source file en-US.json

* Update source file en-US.json

* New translations en-us.json (Japanese)
---
 Editor/Localization/ja-JP.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Editor/Localization/ja-JP.json b/Editor/Localization/ja-JP.json
index 95c10a1b..ad1378f4 100644
--- a/Editor/Localization/ja-JP.json
+++ b/Editor/Localization/ja-JP.json
@@ -146,6 +146,8 @@
   "error.rename_params.default_value_conflict:hint": "予測不可能な動作を避けるため、MA Parametersコンポーネントの初期値フィールドはパラメーター名毎に1つだけしか指定しないようにし、他のコンポーネントでは空白のままにしてください。複数の値が存在する場合、Modular Avatarは階層順で最初に指定された初期値を採用します。",
   "error.replace_object.null_target": "[MA-0008] 置き換え先が指定されていません",
   "error.replace_object.null_target:hint": "Replace Objectは置き換え先のオブジェクトを指定する必要があります。",
+  "error.replace_object.replacing_replacement": "[MA-0009] 複数のReplace Objectコンポーネントで、同じ置き換え先を指定できません",
+  "error.replace_object.parent_of_target": "[MA-0010] このオブジェクトの親を置き換え先に指定できません",
   "validation.blendshape_sync.no_local_renderer": "[MA-1000] このオブジェクトにはSkinned Mesh Rendererがありません。",
   "validation.blendshape_sync.no_local_renderer:hint": "Blendshape Syncは同じGameObject上のSkinned Mesh Rendererに作用します。コンポーネントが正しいオブジェクトに追加されているか確認してください。",
   "validation.blendshape_sync.no_local_mesh": "[MA-1001] このオブジェクトにはSkinned Mesh Rendererがありますが、メッシュがありません。",

From 5d399dce4a6f036972f300c2f668d16ef5a6eb51 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Mon, 14 Oct 2024 21:17:31 -0700
Subject: [PATCH 28/39] ci: workaround VPM issues

---
 .github/workflows/gameci.yml                                    | 1 +
 .../ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs   | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/gameci.yml b/.github/workflows/gameci.yml
index 2d30547d..adf05e8e 100644
--- a/.github/workflows/gameci.yml
+++ b/.github/workflows/gameci.yml
@@ -116,6 +116,7 @@ jobs:
         with:
           repos: |
             https://vpm.nadena.dev/vpm-prerelease.json
+            https://vrchat.github.io/packages/index.json?download
 
       - if: ${{ steps.setup.outputs.should_test == 'true' }}
         name: "Debug: List project contents"
diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
index 3ff0fe4a..882f96bf 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs
@@ -83,7 +83,7 @@ namespace nadena.dev.modular_avatar.core.editor
                     initialValues[condition.Parameter] = condition.InitialValue;
                 }
             }
-        }
+        } 
 
         private void ProcessInitialStates(Dictionary<TargetProp, object> initialStates,
             Dictionary<TargetProp, AnimatedProperty> shapes)

From 662172c2e5a32563a6d95809c26053e18f25c0b1 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 15 Oct 2024 19:13:01 -0700
Subject: [PATCH 29/39] fix: NRE in RO simulator (#1292)

---
 Editor/ReactiveObjects/Simulator/ROSimulator.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Editor/ReactiveObjects/Simulator/ROSimulator.cs b/Editor/ReactiveObjects/Simulator/ROSimulator.cs
index 84896612..66e1578d 100644
--- a/Editor/ReactiveObjects/Simulator/ROSimulator.cs
+++ b/Editor/ReactiveObjects/Simulator/ROSimulator.cs
@@ -256,7 +256,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
                 return;
             }
 
-            _btn_clear.SetEnabled(!PropertyOverrides.Value.IsEmpty || !MenuItemOverrides.Value.IsEmpty);
+            _btn_clear.SetEnabled(PropertyOverrides.Value?.IsEmpty == false || MenuItemOverrides.Value?.IsEmpty == false);
             
             e_debugInfo.style.display = DisplayStyle.Flex;
 

From b73feb6b71a2f2108417c8a7fc099c9fe4002aa2 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 15 Oct 2024 19:13:08 -0700
Subject: [PATCH 30/39] fix: inactive menu items don't appear in RO debugger
 (#1291)

---
 .../AnimationGeneration/ReactiveObjectAnalyzer.cs         | 8 +++++---
 Editor/ReactiveObjects/Simulator/ROSimulator.cs           | 1 +
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
index 643c4a4e..923ac135 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
@@ -21,6 +21,8 @@ namespace nadena.dev.modular_avatar.core.editor
 
         public const string BlendshapePrefix = "blendShape.";
         public const string DeletedShapePrefix = "deletedShape.";
+
+        public bool OptimizeShapes = true;
         
         public ImmutableDictionary<string, float> ForcePropertyOverrides { get; set; } = ImmutableDictionary<string, float>.Empty;
 
@@ -277,13 +279,13 @@ namespace nadena.dev.modular_avatar.core.editor
             // corresponding mesh. If we can't, delete ops are merged into the main list of operations.
             
             initialStates = new Dictionary<TargetProp, object>();
-
+            
             foreach (var (key, info) in shapes.ToList())
             {
                 if (info.actionGroups.Count == 0)
                 {
                     // never active control; ignore it entirely
-                    shapes.Remove(key);
+                    if (OptimizeShapes) shapes.Remove(key);
                     continue;
                 }
 
@@ -297,7 +299,7 @@ namespace nadena.dev.modular_avatar.core.editor
                 // If we're now constant-on, we can skip animation generation
                 if (info.actionGroups[^1].IsConstant)
                 {
-                    shapes.Remove(key);
+                    if (OptimizeShapes) shapes.Remove(key);
                 }
             }
         }
diff --git a/Editor/ReactiveObjects/Simulator/ROSimulator.cs b/Editor/ReactiveObjects/Simulator/ROSimulator.cs
index 66e1578d..40b9be8c 100644
--- a/Editor/ReactiveObjects/Simulator/ROSimulator.cs
+++ b/Editor/ReactiveObjects/Simulator/ROSimulator.cs
@@ -264,6 +264,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
             _lastComputeContext.InvokeOnInvalidate(this, MaybeRefreshUI);
             
             var analysis = new ReactiveObjectAnalyzer(_lastComputeContext);
+            analysis.OptimizeShapes = false;
             analysis.ForcePropertyOverrides = PropertyOverrides.Value;
             analysis.ForceMenuItems = MenuItemOverrides.Value;
             var result = analysis.Analyze(avatar.gameObject);

From 55ab65e22d121341ca443417362bd2044698bca0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 15 Oct 2024 19:13:24 -0700
Subject: [PATCH 31/39] chore(deps): bump nathanvaughn/actions-cloudflare-purge
 (#1287)

Bumps [nathanvaughn/actions-cloudflare-purge](https://github.com/nathanvaughn/actions-cloudflare-purge) from cd4afdf666c2e6a6720048f27ac9cbdd664a673a to 992cc4e96422fb8ddf077281678373fe41e7736c.
- [Release notes](https://github.com/nathanvaughn/actions-cloudflare-purge/releases)
- [Commits](https://github.com/nathanvaughn/actions-cloudflare-purge/compare/cd4afdf666c2e6a6720048f27ac9cbdd664a673a...992cc4e96422fb8ddf077281678373fe41e7736c)

---
updated-dependencies:
- dependency-name: nathanvaughn/actions-cloudflare-purge
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/deploy-pages.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml
index 2ee06ea0..ec465e5c 100644
--- a/.github/workflows/deploy-pages.yml
+++ b/.github/workflows/deploy-pages.yml
@@ -122,7 +122,7 @@ jobs:
           workingDirectory: docs-site~
           
       - name: Purge cache
-        uses: nathanvaughn/actions-cloudflare-purge@cd4afdf666c2e6a6720048f27ac9cbdd664a673a
+        uses: nathanvaughn/actions-cloudflare-purge@992cc4e96422fb8ddf077281678373fe41e7736c
         continue-on-error: true
         with:
           cf_zone: ${{ secrets.CF_ZONE_ID }}

From 8bf1d29bf3630db5fe738b28368482b90143c45c Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 15 Oct 2024 19:16:43 -0700
Subject: [PATCH 32/39] test: add missing blendshape/RC tests (#1293)

---
 .../ReactiveComponent/BlendshapeSyncTest.cs   |  61 ++
 .../BlendshapeSyncTest.cs.meta                |   3 +
 .../BlendshapeSyncTest.prefab                 | 799 ++++++++++++++++++
 .../BlendshapeSyncTest.prefab.meta            |   7 +
 4 files changed, 870 insertions(+)
 create mode 100644 UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs
 create mode 100644 UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta
 create mode 100644 UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab
 create mode 100644 UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta

diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs
new file mode 100644
index 00000000..19d61e6c
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs
@@ -0,0 +1,61 @@
+using System.Linq;
+using modular_avatar_tests;
+using nadena.dev.modular_avatar.core.editor;
+using NUnit.Framework;
+using UnityEngine;
+
+namespace UnitTests.ReactiveComponent
+{
+    public class BlendshapeSyncTest : TestBase
+    {
+        [Test]
+        public void blendshapeSync_propagatesThroughMeshes()
+        {
+            var root = CreatePrefab("BlendshapeSyncTest.prefab");
+
+            var analysis = new ReactiveObjectAnalyzer().Analyze(root);
+
+            var m1 = analysis.Shapes[new TargetProp()
+            {
+                TargetObject = root.transform.Find("m1").GetComponent<SkinnedMeshRenderer>(),
+                PropertyName = "blendShape.bottom"
+            }];
+            var m2 = analysis.Shapes[new TargetProp()
+            {
+                TargetObject = root.transform.Find("m2").GetComponent<SkinnedMeshRenderer>(),
+                PropertyName = "blendShape.bottom"
+            }];
+            var m3 = analysis.Shapes[new TargetProp()
+            {
+                TargetObject = root.transform.Find("m3").GetComponent<SkinnedMeshRenderer>(),
+                PropertyName = "blendShape.top"
+            }];
+            
+            Assert.IsTrue(analysis.Shapes.ContainsKey(new TargetProp()
+            {
+                TargetObject = root.transform.Find("m1").GetComponent<SkinnedMeshRenderer>(),
+                PropertyName = "deletedShape.bottom"
+            }));
+            
+            Assert.AreEqual(4, analysis.Shapes.Count);
+
+            foreach (var ag in m1.actionGroups)
+            {
+                ag.TargetProp = new TargetProp();
+            }
+            
+            foreach (var ag in m2.actionGroups)
+            {
+                ag.TargetProp = new TargetProp();
+            }
+            
+            foreach (var ag in m3.actionGroups)
+            {
+                ag.TargetProp = new TargetProp();
+            }
+            
+            Assert.AreEqual(m2.actionGroups, m1.actionGroups);
+            Assert.AreEqual(m3.actionGroups, m1.actionGroups);
+        }
+    }
+}
\ No newline at end of file
diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta
new file mode 100644
index 00000000..c614946b
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f74d4e816d0247159d977e21ebc57458
+timeCreated: 1728168066
\ No newline at end of file
diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab
new file mode 100644
index 00000000..c0b98180
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab
@@ -0,0 +1,799 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &4750389987621451750
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3724328547737935111}
+  - component: {fileID: 6141552197888553193}
+  - component: {fileID: 866105862211182099}
+  - component: {fileID: 5955186656294850035}
+  m_Layer: 0
+  m_Name: New Toggle
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3724328547737935111
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4750389987621451750}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 295226914695240947}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6141552197888553193
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4750389987621451750}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Control:
+    name: New Toggle
+    icon: {fileID: 0}
+    type: 102
+    parameter:
+      name: 
+    value: 1
+    style: 0
+    subMenu: {fileID: 0}
+    subParameters: []
+    labels: []
+  MenuSource: 1
+  menuSource_otherObjectChildren: {fileID: 0}
+  isSynced: 1
+  isSaved: 1
+  isDefault: 1
+  automaticValue: 1
+--- !u!114 &866105862211182099
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4750389987621451750}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 7ef83cb0c23d4d7c9d41021e544a1978, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  menuToAppend: {fileID: 0}
+  installTargetMenu: {fileID: 0}
+--- !u!114 &5955186656294850035
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4750389987621451750}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_inverted: 0
+  m_shapes:
+  - Object:
+      referencePath: m1
+      targetObject: {fileID: 7767603723203631002}
+    ShapeName: bottom
+    ChangeType: 1
+    Value: 100
+--- !u!1 &7303978391080220300
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 295226914695240947}
+  - component: {fileID: 1177795503533185300}
+  - component: {fileID: 7792711537747161192}
+  - component: {fileID: 8538029171187693289}
+  m_Layer: 0
+  m_Name: BlendshapeSyncTest
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &295226914695240947
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7303978391080220300}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 1.1618705, y: 1.0265146, z: 1.8807894}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children:
+  - {fileID: 6955742288500591904}
+  - {fileID: 2035853062413530075}
+  - {fileID: 2118711245520540949}
+  - {fileID: 3724328547737935111}
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!95 &1177795503533185300
+Animator:
+  serializedVersion: 5
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7303978391080220300}
+  m_Enabled: 1
+  m_Avatar: {fileID: 0}
+  m_Controller: {fileID: 0}
+  m_CullingMode: 0
+  m_UpdateMode: 0
+  m_ApplyRootMotion: 0
+  m_LinearVelocityBlending: 0
+  m_StabilizeFeet: 0
+  m_WarningMessage: 
+  m_HasTransformHierarchy: 1
+  m_AllowConstantClipSamplingOptimization: 1
+  m_KeepAnimatorStateOnDisable: 0
+  m_WriteDefaultValuesOnDisable: 0
+--- !u!114 &7792711537747161192
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7303978391080220300}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Name: 
+  ViewPosition: {x: 0, y: 1.6, z: 0.2}
+  Animations: 0
+  ScaleIPD: 1
+  lipSync: 0
+  lipSyncJawBone: {fileID: 0}
+  lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
+  lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
+  VisemeSkinnedMesh: {fileID: 0}
+  MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
+  VisemeBlendShapes: []
+  unityVersion: 
+  portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
+  portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
+  networkIDs: []
+  customExpressions: 0
+  expressionsMenu: {fileID: 0}
+  expressionParameters: {fileID: 0}
+  enableEyeLook: 0
+  customEyeLookSettings:
+    eyeMovement:
+      confidence: 0.5
+      excitement: 0.5
+    leftEye: {fileID: 0}
+    rightEye: {fileID: 0}
+    eyesLookingStraight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingUp:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingDown:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingLeft:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyesLookingRight:
+      linked: 1
+      left: {x: 0, y: 0, z: 0, w: 0}
+      right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidType: 0
+    upperLeftEyelid: {fileID: 0}
+    upperRightEyelid: {fileID: 0}
+    lowerLeftEyelid: {fileID: 0}
+    lowerRightEyelid: {fileID: 0}
+    eyelidsDefault:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsClosed:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingUp:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsLookingDown:
+      upper:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+      lower:
+        linked: 1
+        left: {x: 0, y: 0, z: 0, w: 0}
+        right: {x: 0, y: 0, z: 0, w: 0}
+    eyelidsSkinnedMesh: {fileID: 0}
+    eyelidsBlendshapes: 
+  customizeAnimationLayers: 0
+  baseAnimationLayers:
+  - isEnabled: 0
+    type: 0
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 4
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 5
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  specialAnimationLayers:
+  - isEnabled: 0
+    type: 6
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 7
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  - isEnabled: 0
+    type: 8
+    animatorController: {fileID: 0}
+    mask: {fileID: 0}
+    isDefault: 1
+  AnimationPreset: {fileID: 0}
+  animationHashSet: []
+  autoFootsteps: 1
+  autoLocomotion: 1
+  collider_head:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_torso:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_footL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_handL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleL:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerIndexR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerMiddleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerRingR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+  collider_fingerLittleR:
+    isMirrored: 1
+    state: 0
+    transform: {fileID: 0}
+    radius: 0
+    height: 0
+    position: {x: 0, y: 0, z: 0}
+    rotation: {x: 0, y: 0, z: 0, w: 1}
+--- !u!114 &8538029171187693289
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7303978391080220300}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  launchedFromSDKPipeline: 0
+  completedSDKPipeline: 0
+  blueprintId: 
+  contentType: 0
+  assetBundleUnityVersion: 
+  fallbackStatus: 0
+--- !u!1001 &1940371780088492798
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    serializedVersion: 3
+    m_TransformParent: {fileID: 295226914695240947}
+    m_Modifications:
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 5.33
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071067
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_ConstrainProportionsScale
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_DirtyAABB
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.z
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[0]
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_Name
+      value: m3
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+    m_RemovedGameObjects: []
+    m_AddedGameObjects: []
+    m_AddedComponents:
+    - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      insertIndex: -1
+      addedObject: {fileID: 4462016549056391230}
+  m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
+--- !u!1 &1597929094539956143 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 1940371780088492798}
+  m_PrefabAsset: {fileID: 0}
+--- !u!114 &4462016549056391230
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1597929094539956143}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 6fd7cab7d93b403280f2f9da978d8a4f, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Bindings:
+  - ReferenceMesh:
+      referencePath: m2
+      targetObject: {fileID: 1660453041666320737}
+    Blendshape: bottom
+    LocalBlendshape: top
+--- !u!4 &2118711245520540949 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 1940371780088492798}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &2002541556693849136
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    serializedVersion: 3
+    m_TransformParent: {fileID: 295226914695240947}
+    m_Modifications:
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 2.75
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071067
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_ConstrainProportionsScale
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_DirtyAABB
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.z
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[0]
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_Name
+      value: m2
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+    m_RemovedGameObjects: []
+    m_AddedGameObjects: []
+    m_AddedComponents:
+    - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      insertIndex: -1
+      addedObject: {fileID: 1532028097981150578}
+  m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
+--- !u!1 &1660453041666320737 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 2002541556693849136}
+  m_PrefabAsset: {fileID: 0}
+--- !u!114 &1532028097981150578
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1660453041666320737}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 6fd7cab7d93b403280f2f9da978d8a4f, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  Bindings:
+  - ReferenceMesh:
+      referencePath: m1
+      targetObject: {fileID: 7767603723203631002}
+    Blendshape: bottom
+    LocalBlendshape: 
+--- !u!4 &2035853062413530075 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 2002541556693849136}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &7425727422508624587
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    serializedVersion: 3
+    m_TransformParent: {fileID: 295226914695240947}
+    m_Modifications:
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071067
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_DirtyAABB
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_AABB.m_Extent.z
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 0}
+    - target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_BlendShapeWeights.Array.data[0]
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+        type: 3}
+      propertyPath: m_Name
+      value: m1
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+    m_RemovedGameObjects: []
+    m_AddedGameObjects: []
+    m_AddedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
+--- !u!4 &6955742288500591904 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 7425727422508624587}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &7767603723203631002 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
+    type: 3}
+  m_PrefabInstance: {fileID: 7425727422508624587}
+  m_PrefabAsset: {fileID: 0}
diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta
new file mode 100644
index 00000000..d6863646
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 05653f5cab04e764b80709fe866c1b35
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

From 3ba02194300c4432f267e4dec80f2d399a815a0f Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Tue, 15 Oct 2024 19:19:05 -0700
Subject: [PATCH 33/39] 1.10.4

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 778562f3..ec982b70 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "nadena.dev.modular-avatar",
   "displayName": "Modular Avatar",
-  "version": "1.10.3",
+  "version": "1.10.4",
   "unity": "2022.3",
   "description": "A suite of tools for assembling your avatar out of reusable components",
   "author": {

From ae975506d78829de4080d8ca4ed85b9ed5c19c26 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 19 Oct 2024 17:15:14 -0700
Subject: [PATCH 34/39] fix: occasionally objects controlling reactive
 components "flicker" (#1298)

Reported-by: @whipnice
---
 .../Animation/GameObjectDisableDelayPass.cs   | 45 +++++++++++++++++--
 1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/Editor/Animation/GameObjectDisableDelayPass.cs b/Editor/Animation/GameObjectDisableDelayPass.cs
index 8e8bca4c..fb453429 100644
--- a/Editor/Animation/GameObjectDisableDelayPass.cs
+++ b/Editor/Animation/GameObjectDisableDelayPass.cs
@@ -1,9 +1,11 @@
 using System.Linq;
+using nadena.dev.modular_avatar.core.editor;
 using nadena.dev.ndmf;
 using UnityEditor;
 using UnityEditor.Animations;
 using UnityEngine;
 using VRC.SDK3.Avatars.Components;
+using BuildContext = nadena.dev.ndmf.BuildContext;
 
 namespace nadena.dev.modular_avatar.animation
 {
@@ -23,11 +25,16 @@ namespace nadena.dev.modular_avatar.animation
 
             if (fx == null) return;
 
+            var nullMotion = new AnimationClip();
+            nullMotion.name = "NullMotion";
+            
             var blendTree = new BlendTree();
             blendTree.blendType = BlendTreeType.Direct;
             blendTree.useAutomaticThresholds = false;
 
-            blendTree.children = asc.BoundReadableProperties.Select(GenerateDelayChild).ToArray();
+            blendTree.children = asc.BoundReadableProperties
+                .Select(prop => GenerateDelayChild(nullMotion, prop))
+                .ToArray();
 
             var asm = new AnimatorStateMachine();
             var state = new AnimatorState();
@@ -54,7 +61,7 @@ namespace nadena.dev.modular_avatar.animation
             }).ToArray();
         }
 
-        private ChildMotion GenerateDelayChild((EditorCurveBinding, string) binding)
+        private ChildMotion GenerateDelayChild(Motion nullMotion, (EditorCurveBinding, string) binding)
         {
             var ecb = binding.Item1;
             var prop = binding.Item2;
@@ -64,10 +71,40 @@ namespace nadena.dev.modular_avatar.animation
             curve.AddKey(0, 1);
             AnimationUtility.SetEditorCurve(motion, ecb, curve);
 
+            // Occasionally, we'll have a very small value pop up, probably due to FP errors.
+            // To correct for this, instead of directly using the property in the direct blend tree,
+            // we'll use a 1D blend tree to give ourselves a buffer.
+
+            var bufferBlendTree = new BlendTree();
+            bufferBlendTree.blendType = BlendTreeType.Simple1D;
+            bufferBlendTree.useAutomaticThresholds = false;
+            bufferBlendTree.blendParameter = prop;
+            bufferBlendTree.children = new[]
+            {
+                new ChildMotion
+                {
+                    motion = nullMotion,
+                    timeScale = 1,
+                    threshold = 0
+                },
+                new ChildMotion
+                {
+                    motion = nullMotion,
+                    timeScale = 1,
+                    threshold = 0.01f
+                },
+                new ChildMotion
+                {
+                    motion = motion,
+                    timeScale = 1,
+                    threshold = 1
+                }
+            };
+
             return new ChildMotion
             {
-                motion = motion,
-                directBlendParameter = prop,
+                motion = bufferBlendTree,
+                directBlendParameter = MergeBlendTreePass.ALWAYS_ONE,
                 timeScale = 1
             };
         }

From 9dc342e81e099f3f50d749ab4017e4214a568704 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 19 Oct 2024 17:15:23 -0700
Subject: [PATCH 35/39] fix: always-on toggles fail to animate objects to off
 (#1300)

Closes: #1285
---
 .../Animation/GameObjectDisableDelayPass.cs   | 15 ++++++
 .../ReactiveComponent/ObjectToggleTests.cs    | 50 +++++++++++++++++++
 .../ObjectToggleTests.cs.meta                 |  3 ++
 3 files changed, 68 insertions(+)
 create mode 100644 UnitTests~/ReactiveComponent/ObjectToggleTests.cs
 create mode 100644 UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta

diff --git a/Editor/Animation/GameObjectDisableDelayPass.cs b/Editor/Animation/GameObjectDisableDelayPass.cs
index fb453429..0eed02d2 100644
--- a/Editor/Animation/GameObjectDisableDelayPass.cs
+++ b/Editor/Animation/GameObjectDisableDelayPass.cs
@@ -59,6 +59,21 @@ namespace nadena.dev.modular_avatar.animation
                 defaultWeight = 1,
                 blendingMode = AnimatorLayerBlendingMode.Override
             }).ToArray();
+
+            // Ensure the initial state of readable props matches the actual state of the gameobject
+            var parameters = fx.parameters;
+            var paramToIndex = parameters.Select((p, i) => (p, i)).ToDictionary(x => x.p.name, x => x.i);
+            foreach (var (binding, prop) in asc.BoundReadableProperties)
+            {
+                var obj = asc.PathMappings.PathToObject(binding.path);
+
+                if (obj != null && paramToIndex.TryGetValue(prop, out var index))
+                {
+                    parameters[index].defaultFloat = obj.activeSelf ? 1 : 0;
+                }
+            }
+
+            fx.parameters = parameters;
         }
 
         private ChildMotion GenerateDelayChild(Motion nullMotion, (EditorCurveBinding, string) binding)
diff --git a/UnitTests~/ReactiveComponent/ObjectToggleTests.cs b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs
new file mode 100644
index 00000000..6637ff05
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs
@@ -0,0 +1,50 @@
+using System.Linq;
+using modular_avatar_tests;
+using nadena.dev.modular_avatar.core;
+using nadena.dev.modular_avatar.core.editor;
+using NUnit.Framework;
+using UnityEditor.Animations;
+using UnityEngine;
+
+namespace UnitTests.ReactiveComponent
+{
+    internal class ObjectToggleTests : TestBase
+    {
+        [Test]
+        public void WhenObjectIsAlwaysOn_CorrectProxyParameterIsGenerated()
+        {
+            var root = CreateRoot("root");
+            var obj = CreateChild(root, "obj");
+            var toggle = CreateChild(root, "toggle");
+            
+            // Prevent obj from being removed by the GC game objects pass
+            obj.AddComponent<MeshRenderer>();
+            
+            var toggleComponent = toggle.AddComponent<ModularAvatarObjectToggle>();
+            var aor = new AvatarObjectReference();
+            aor.Set(obj);
+            
+            toggleComponent.Objects = new()
+            {
+                new()
+                {
+                    Active = false,
+                    Object = aor
+                }
+            };
+            
+            AvatarProcessor.ProcessAvatar(root);
+
+            // TODO: Ideally we should start using play mode testing for these things...
+            var fx = (AnimatorController)FindFxController(root).animatorController;
+            var readableProp = fx.parameters.FirstOrDefault(
+                p => p.name.StartsWith("__MA/ReadableProp/obj/UnityEngine.GameObject/m_IsActive")
+            );
+            
+            Assert.IsNotNull(readableProp);
+            Assert.AreEqual(readableProp.defaultFloat, 0);
+            
+            Assert.IsFalse(obj.activeSelf);
+        }
+    }
+}
\ No newline at end of file
diff --git a/UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta
new file mode 100644
index 00000000..9e86034a
--- /dev/null
+++ b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 7c68d69f7b4a46c5b2ce3d8f26b0fa76
+timeCreated: 1729376563
\ No newline at end of file

From ab4d1fd2f4285780b8f281b8292e2964fc69ee56 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 19 Oct 2024 17:15:33 -0700
Subject: [PATCH 36/39] fix: transient NRE when opening reaction debugger
 (#1301)

---
 .../AnimationGeneration/ReactiveObjectAnalyzer.cs               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
index 923ac135..9b64dde8 100644
--- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
+++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs
@@ -128,7 +128,7 @@ namespace nadena.dev.modular_avatar.core.editor
                     foreach (var cond in rule.ControllingConditions)
                     {
                         var paramName = cond.Parameter;
-                        if (ForcePropertyOverrides.TryGetValue(paramName, out var value))
+                        if (ForcePropertyOverrides?.TryGetValue(paramName, out var value) == true)
                         {
                             cond.InitialValue = value;
                         }

From 123523540eddc8a682c6f55af63fd22e76266a17 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 19 Oct 2024 17:15:43 -0700
Subject: [PATCH 37/39] fix: parameters lose their default value when others
 are moved around (#1302)

Closes: #1296
---
 .../Inspector/Parameters/DefaultValueField.cs | 34 ++++++++++++++-----
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/Editor/Inspector/Parameters/DefaultValueField.cs b/Editor/Inspector/Parameters/DefaultValueField.cs
index 6ba6cd22..f29f217b 100644
--- a/Editor/Inspector/Parameters/DefaultValueField.cs
+++ b/Editor/Inspector/Parameters/DefaultValueField.cs
@@ -20,7 +20,8 @@ namespace nadena.dev.modular_avatar.core.editor
         private readonly DropdownField _boolField;
 
         private ParameterSyncType _syncType;
-
+        private bool _hasInitialBinding;
+        
         public DefaultValueField()
         {
             // Hidden binding elements
@@ -57,28 +58,39 @@ namespace nadena.dev.modular_avatar.core.editor
             {
                 _numberField.style.display = DisplayStyle.Flex;
                 _boolField.style.display = DisplayStyle.None;
-                OnUpdateNumberValue(_numberField.value);
+                OnUpdateNumberValue(_numberField.value, true);
             }
             else
             {
                 _numberField.style.display = DisplayStyle.None;
                 _boolField.style.display = DisplayStyle.Flex;
-                OnUpdateBoolValue(_boolField.value);
+                OnUpdateBoolValue(_boolField.value, true);
             }
         }
 
-        private void OnUpdateNumberValue(string value)
+        private void OnUpdateNumberValue(string value, bool implicitUpdate = false)
         {
+            // Upon initial creation, sometimes the OnUpdateSyncType fires before we receive the initial value event.
+            // In this case, suppress the update to avoid losing data.
+            if (implicitUpdate && !_hasInitialBinding) return;
+
+            var theValue = _defaultValueField.value;
             if (string.IsNullOrWhiteSpace(value))
             {
-                _defaultValueField.value = 0;
+                if (!implicitUpdate)
+                {
+                    _defaultValueField.value = 0;
+                }
+
+                theValue = _defaultValueField.value;
+
                 _hasExplicitDefaultValueField.value = false;
             }
             else if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed)
                 && !float.IsNaN(parsed)
                 && !float.IsInfinity(parsed))
             {
-                _defaultValueField.value = _syncType switch
+                theValue = _defaultValueField.value = _syncType switch
                 {
                     ParameterSyncType.Int => Mathf.FloorToInt(Mathf.Clamp(parsed, 0, 255)),
                     ParameterSyncType.Float => Mathf.Clamp(parsed, -1, 1),
@@ -88,11 +100,15 @@ namespace nadena.dev.modular_avatar.core.editor
                 _hasExplicitDefaultValueField.value = true;
             }
 
-            UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value);
+            UpdateVisibleField(theValue, _hasExplicitDefaultValueField.value);
         }
 
-        private void OnUpdateBoolValue(string value)
+        private void OnUpdateBoolValue(string value, bool implicitUpdate = false)
         {
+            // Upon initial creation, sometimes the OnUpdateSyncType fires before we receive the initial value event.
+            // In this case, suppress the update to avoid losing data.
+            if (implicitUpdate && !_hasInitialBinding) return;
+            
             _defaultValueField.value = value == V_True ? 1 : 0;
             _hasExplicitDefaultValueField.value = value != V_None;
 
@@ -101,6 +117,8 @@ namespace nadena.dev.modular_avatar.core.editor
 
         private void UpdateVisibleField(float value, bool hasExplicitValue)
         {
+            _hasInitialBinding = true;
+            
             if (hasExplicitValue || Mathf.Abs(value) > 0.0000001)
             {
                 _numberField.SetValueWithoutNotify(value.ToString(CultureInfo.InvariantCulture));

From 11a62c88d49298df22f064c97151f705af419f6d Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 19 Oct 2024 17:46:31 -0700
Subject: [PATCH 38/39] fix: issues when handling VRCExpressionMenus with an
 uninitialized type field (#1303)

---
 Editor/Inspector/Menu/MenuItemGUI.cs | 7 +++----
 Editor/Menu/MenuExtractor.cs         | 4 +++-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/Editor/Inspector/Menu/MenuItemGUI.cs b/Editor/Inspector/Menu/MenuItemGUI.cs
index 30439a32..8f4ab313 100644
--- a/Editor/Inspector/Menu/MenuItemGUI.cs
+++ b/Editor/Inspector/Menu/MenuItemGUI.cs
@@ -301,10 +301,9 @@ namespace nadena.dev.modular_avatar.core.editor
                 EditorGUILayout.BeginVertical();
 
                 if (_type.hasMultipleDifferentValues) return;
-                VRCExpressionsMenu.Control.ControlType type =
-                    (VRCExpressionsMenu.Control.ControlType) Enum
-                        .GetValues(typeof(VRCExpressionsMenu.Control.ControlType))
-                        .GetValue(_type.enumValueIndex);
+                var controlTypeArray = Enum.GetValues(typeof(VRCExpressionsMenu.Control.ControlType));
+                var index = Math.Clamp(_type.enumValueIndex, 0, controlTypeArray.Length - 1);
+                var type = (VRCExpressionsMenu.Control.ControlType)controlTypeArray.GetValue(index);
 
                 switch (type)
                 {
diff --git a/Editor/Menu/MenuExtractor.cs b/Editor/Menu/MenuExtractor.cs
index 89ee232c..9f670382 100644
--- a/Editor/Menu/MenuExtractor.cs
+++ b/Editor/Menu/MenuExtractor.cs
@@ -119,9 +119,11 @@ namespace nadena.dev.modular_avatar.core.editor
 
         internal static VRCExpressionsMenu.Control CloneControl(VRCExpressionsMenu.Control c)
         {
+            var type = c.type != 0 ? c.type : VRCExpressionsMenu.Control.ControlType.Button;
+            
             return new VRCExpressionsMenu.Control()
             {
-                type = c.type,
+                type = type,
                 name = c.name,
                 icon = c.icon,
                 parameter = new VRCExpressionsMenu.Control.Parameter() { name = c.parameter?.name },

From 5bafb0ba9d2f027f9b938529389fd7595fdd5312 Mon Sep 17 00:00:00 2001
From: bd_ <bd_@nadena.dev>
Date: Sat, 19 Oct 2024 17:46:39 -0700
Subject: [PATCH 39/39] fix: "extract menu to objects" does not set the target
 of its menu group properly (#1304)

Closes: #1297
---
 Editor/Inspector/Menu/MenuInstallerEditor.cs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Editor/Inspector/Menu/MenuInstallerEditor.cs b/Editor/Inspector/Menu/MenuInstallerEditor.cs
index 5110aa36..f32efe0f 100644
--- a/Editor/Inspector/Menu/MenuInstallerEditor.cs
+++ b/Editor/Inspector/Menu/MenuInstallerEditor.cs
@@ -368,7 +368,7 @@ namespace nadena.dev.modular_avatar.core.editor
 
             serializedObject.ApplyModifiedProperties();
 
-            Localization.ShowLanguageUI();
+            ShowLanguageUI();
         }
 
         private string ObjectHierarchyOrder(Component arg)
@@ -415,6 +415,9 @@ namespace nadena.dev.modular_avatar.core.editor
                     var group = installer.gameObject.AddComponent<ModularAvatarMenuGroup>();
                     var menuRoot = new GameObject();
                     menuRoot.name = "Menu";
+
+                    group.targetObject = menuRoot;
+                    
                     Undo.RegisterCreatedObjectUndo(menuRoot, "Extract menu");
                     menuRoot.transform.SetParent(group.transform, false);
                     foreach (var control in menu.controls)