From 802fea09d91cdef7c74e2e2dae6e6ce86fa06aa0 Mon Sep 17 00:00:00 2001 From: bd_ Date: Fri, 30 Aug 2024 19:19:46 -0700 Subject: [PATCH] feat: serialize Move Independently grouping (#1067) Closed: #842 --- .../MoveIndependentlyEditor.cs | 39 +++++++++++++++++-- Runtime/MAMoveIndependently.cs | 1 + 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs b/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs index 0acf66e8..8c6da1a7 100644 --- a/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs +++ b/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using nadena.dev.modular_avatar.core.ArmatureAwase; +using nadena.dev.ndmf.preview; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; @@ -15,15 +16,38 @@ namespace nadena.dev.modular_avatar.core.editor [SerializeField] private StyleSheet uss; [SerializeField] private VisualTreeAsset uxml; + private ComputeContext _ctx; + private VisualElement _root; + private TransformChildrenNode _groupedNodesElem; protected override void OnInnerInspectorGUI() { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } protected override VisualElement CreateInnerInspectorGUI() { + _root = new VisualElement(); + + RebuildInnerGUI(); + + return _root; + } + + private void RebuildInnerGUI() + { + _root.Clear(); + _ctx = new ComputeContext("MoveIndependentlyEditor"); + _root.Add(BuildInnerGUI(_ctx)); + } + + private VisualElement BuildInnerGUI(ComputeContext ctx) + { + if (this.target == null) return new VisualElement(); + + _ctx.InvokeOnInvalidate(this, editor => editor.RebuildInnerGUI()); + #pragma warning disable CS0618 // Type or member is obsolete var root = uxml.Localize(); #pragma warning restore CS0618 // Type or member is obsolete @@ -32,9 +56,14 @@ namespace nadena.dev.modular_avatar.core.editor var container = root.Q("group-container"); MAMoveIndependently target = (MAMoveIndependently) this.target; - var grouped = (target.GroupedBones ?? Array.Empty()) - .Select(obj => obj.transform) - .ToImmutableHashSet(); + // Note: We specifically _don't_ use an ImmutableHashSet here as we want to update the previously-returned + // set in place to avoid rebuilding GUI elements after the user changes the grouping. + var grouped = ctx.Observe(target, + t => (t.GroupedBones ?? Array.Empty()) + .Select(obj => obj.transform) + .ToHashSet(new ObjectIdentityComparer()), + (x, y) => x.SetEquals(y) + ); _groupedNodesElem = new TransformChildrenNode(target.transform, grouped); _groupedNodesElem.AddToClassList("group-root"); @@ -43,6 +72,8 @@ namespace nadena.dev.modular_avatar.core.editor { Undo.RecordObject(target, "Toggle grouped nodes"); target.GroupedBones = _groupedNodesElem.Active().Select(t => t.gameObject).ToArray(); + grouped.Clear(); + grouped.UnionWith(target.GroupedBones.Select(obj => obj.transform)); PrefabUtility.RecordPrefabInstancePropertyModifications(target); }; diff --git a/Runtime/MAMoveIndependently.cs b/Runtime/MAMoveIndependently.cs index 374fbc24..b193e4a2 100644 --- a/Runtime/MAMoveIndependently.cs +++ b/Runtime/MAMoveIndependently.cs @@ -17,6 +17,7 @@ namespace nadena.dev.modular_avatar.core.ArmatureAwase { private float EPSILON = 0.0000001f; + [SerializeField] private GameObject[] m_groupedBones; public GameObject[] GroupedBones