using System; using System.Reflection; using System.Runtime.InteropServices; using UnityEditor; using UnityEditorInternal; using UnityEngine; using UnityEngine.PlayerLoop; namespace net.fushizen.modular_avatar.core.editor { [CustomEditor(typeof(ModularAvatarBlendshapeSync))] [CanEditMultipleObjects] internal class BlendshapeSyncEditor : Editor { private static FieldInfo f_m_SerializedObject; private BlendshapeSelectWindow _window; private ReorderableList _list; private SerializedProperty _bindings; static BlendshapeSyncEditor() { f_m_SerializedObject = typeof(Editor).GetField("m_SerializedObject", BindingFlags.Instance | BindingFlags.NonPublic); } // Workaround unity bug: When we modify the number of array elements via the underlying objects, the serialized // object will throw exceptions trying to access the new element, even after calling Update() and recreating all // serialized properties. So force the serialized object to be recreated as a workaround. private void ClearSerializedObject() { f_m_SerializedObject.SetValue(this, null); } private void OnDisable() { if (_window != null) DestroyImmediate(_window); } private void OnDestroy() { if (_window != null) DestroyImmediate(_window); } private void OnEnable() { InitList(); } private void InitList() { _bindings = serializedObject.FindProperty(nameof(ModularAvatarBlendshapeSync.Bindings)); _list = new ReorderableList(serializedObject, _bindings, true, true, true, true ); _list.drawHeaderCallback = DrawHeader; _list.drawElementCallback = DrawElement; _list.onAddCallback = list => OpenAddWindow(); _list.elementHeight += 2; } private void DrawHeader(Rect rect) { var leftHalf = rect; leftHalf.width /= 2; var rightHalf = rect; rightHalf.width /= 2; rightHalf.x += rightHalf.width; EditorGUI.LabelField(leftHalf, "Mesh"); EditorGUI.LabelField(rightHalf, "Blendshape"); } private void DrawElement(Rect rect, int index, bool isactive, bool isfocused) { rect.height -= 2; rect.y += 1; var leftHalf = rect; leftHalf.width /= 2; leftHalf.width -= 12; var rightHalf = rect; rightHalf.width /= 2; rightHalf.x += rightHalf.width; var item = _bindings.GetArrayElementAtIndex(index); var mesh = item.FindPropertyRelative(nameof(BlendshapeBinding.ReferenceMesh)); var blendshape = item.FindPropertyRelative(nameof(BlendshapeBinding.Blendshape)); using (var scope = new ZeroIndentScope()) { EditorGUI.PropertyField(leftHalf, mesh, GUIContent.none); EditorGUI.PropertyField(rightHalf, blendshape, GUIContent.none); } } public override void OnInspectorGUI() { LogoDisplay.DisplayLogo(); serializedObject.Update(); _list.DoLayoutList(); serializedObject.ApplyModifiedProperties(); } private void OpenAddWindow() { if (_window != null) DestroyImmediate(_window); _window = ScriptableObject.CreateInstance(); _window.AvatarRoot = RuntimeUtil.FindAvatarInParents(((ModularAvatarBlendshapeSync) target).transform) .gameObject; _window.OfferBinding += OfferBinding; _window.Show(); } private void OfferBinding(BlendshapeBinding binding) { foreach (var obj in targets) { var sync = (ModularAvatarBlendshapeSync) obj; Undo.RecordObject(sync, "Adding blendshape binding"); if (!sync.Bindings.Contains(binding)) sync.Bindings.Add(binding); EditorUtility.SetDirty(sync); } ClearSerializedObject(); InitList(); } } }