From 5a36db7b7f44a64ffdce378f0c64dce2ffa42f85 Mon Sep 17 00:00:00 2001 From: bd_ Date: Fri, 5 May 2023 17:09:34 +0900 Subject: [PATCH] ui: add action blendshape editor --- .../Editor/Inspector/ColumnReorderableList.cs | 11 + .../Inspector/Menu/ActionBlendshapeEditor.cs | 188 ++++++++++++++++++ .../Menu/ActionBlendshapeEditor.cs.meta | 3 + 3 files changed, 202 insertions(+) create mode 100644 Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs create mode 100644 Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs.meta diff --git a/Packages/nadena.dev.modular-avatar/Editor/Inspector/ColumnReorderableList.cs b/Packages/nadena.dev.modular-avatar/Editor/Inspector/ColumnReorderableList.cs index dc111d88..62578190 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/Inspector/ColumnReorderableList.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/Inspector/ColumnReorderableList.cs @@ -112,6 +112,17 @@ namespace nadena.dev.modular_avatar.core.editor if (_columns.Count == 0) { + if (_elementWidth < 1.0f) + { + if (rect.width < 1.0f) + { + return; + } + + _elementWidth = rect.width - 10f; + Repaint(); + } + _columnPos = 0; _remainingWidth = _elementWidth; OnGenerateColumns(_elementWidth); diff --git a/Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs b/Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs new file mode 100644 index 00000000..6c1c8d3d --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +namespace nadena.dev.modular_avatar.core.editor +{ + [CustomEditor(typeof(ActionBlendshape))] + internal class ActionBlendshapeEditor : MAEditorBase + { + private ColumnReorderableList _list; + private SerializedProperty _listProp; + private Dictionary _blendshapeNamesCache = new Dictionary(); + private Transform _avatarRoot; + + private void OnEnable() + { + _listProp = serializedObject.FindProperty(nameof(ActionBlendshape.Blendshapes)); + _list = new ColumnReorderableList( + serializedObject, + _listProp, + true, + true, + true, + true + ); + + _list.Repaint = Repaint; + _list.OnGenerateColumns = BuildColumns; + _list.onRemoveCallback += OnRemoveElement; + _list.onAddCallback += OnAddElement; + + _list.elementHeight += 2; + + if (targets.Length == 1) + { + _avatarRoot = RuntimeUtil.FindAvatarInParents(((Component) target).transform).transform; + } + } + + private void OnAddElement(ReorderableList list) + { + _listProp.arraySize++; + } + + private void OnRemoveElement(ReorderableList reorderableList) + { + if (reorderableList.index < _listProp.arraySize) + { + _listProp.DeleteArrayElementAtIndex(reorderableList.index); + } + } + + private void RenderBlendshapeColumn(Rect rect, SerializedProperty elem) + { + var prop_obj = elem.FindPropertyRelative(nameof(ActionBlendshape.BlendshapeSpec.target)) + ?.FindPropertyRelative(nameof(AvatarObjectReference.referencePath)); + var prop_blendshape = elem.FindPropertyRelative(nameof(ActionBlendshape.BlendshapeSpec.blendshape)); + + SkinnedMeshRenderer targetRenderer = null; + + if (_avatarRoot != null) + { + var targetObj = _avatarRoot.Find(prop_obj.stringValue); + if (targetObj != null) + { + targetRenderer = targetObj.GetComponent(); + } + } + + if (targetRenderer == null || targetRenderer.sharedMesh == null) + { + EditorGUI.PropertyField(rect, prop_blendshape, GUIContent.none); + return; + } + + var sharedMesh = targetRenderer.sharedMesh; + + if (!_blendshapeNamesCache.TryGetValue(sharedMesh, out var names)) + { + names = new string[sharedMesh.blendShapeCount + 1]; + names[0] = ""; + for (var i = 1; i < sharedMesh.blendShapeCount; i++) + { + names[i] = sharedMesh.GetBlendShapeName(i - 1); + } + + _blendshapeNamesCache[sharedMesh] = names; + } + + var blendshapeIndex = Array.IndexOf(names, prop_blendshape.stringValue); + var style = new GUIStyle(EditorStyles.popup); + + EditorGUI.BeginChangeCheck(); + blendshapeIndex = EditorGUI.Popup(rect, blendshapeIndex, names, style); + if (EditorGUI.EndChangeCheck()) + { + prop_blendshape.stringValue = names[blendshapeIndex]; + } + else if (blendshapeIndex < 0) + { + var toDisplay = prop_blendshape.stringValue; + + UpdateAllStates(style, s => s.textColor = Color.Lerp(s.textColor, Color.red, 0.85f)); + + GUI.Label(rect, toDisplay, style); + } + } + + private static void UpdateAllStates(GUIStyle style, Action action) + { + var state = style.normal; + action(state); + style.normal = state; + + state = style.hover; + action(state); + style.hover = state; + + state = style.active; + action(state); + style.active = state; + + state = style.focused; + action(state); + style.focused = state; + + state = style.onNormal; + action(state); + style.onNormal = state; + + state = style.onHover; + action(state); + style.onHover = state; + + state = style.onActive; + action(state); + style.onActive = state; + + state = style.onFocused; + action(state); + style.onFocused = state; + } + + private void BuildColumns(float width) + { + GUIContent testValueContent = new GUIContent("100"); + var valueFieldSize = EditorStyles.textField.CalcSize(testValueContent); + var remainingWidth = width - valueFieldSize.x - _list.margin; + var fieldWidth = (remainingWidth - _list.margin) / 2; + + _list.AddColumn(fieldWidth, "action.blendshape.header.object", (rect, elem) => + { + var targetProp = elem.FindPropertyRelative(nameof(ActionBlendshape.BlendshapeSpec.target)); + EditorGUI.PropertyField(rect, targetProp, GUIContent.none); + }); + + _list.AddColumn(fieldWidth, "action.blendshape.header.blendshape", RenderBlendshapeColumn); + + _list.AddColumn(valueFieldSize.x, "action.blendshape.header.value", (rect, elem) => + { + var valueProp = elem.FindPropertyRelative(nameof(ActionBlendshape.BlendshapeSpec.value)); + EditorGUI.PropertyField(rect, valueProp, GUIContent.none); + }); + } + + protected override void OnInnerInspectorGUI() + { + serializedObject.Update(); + + Rect rect = GUILayoutUtility.GetRect( + 10f, + _list.headerHeight + _list.elementHeight * Math.Max(1, _list.serializedProperty.arraySize) + + _list.footerHeight, + GUILayout.ExpandWidth(true) + ); + + _list.DoList(rect); + + EditorGUILayout.Space(8); + + Localization.ShowLanguageUI(); + + serializedObject.ApplyModifiedProperties(); + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs.meta new file mode 100644 index 00000000..dbe86e5e --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/Inspector/Menu/ActionBlendshapeEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 78859e260e8e426bb4673c9077ed5d7b +timeCreated: 1683272729 \ No newline at end of file