using System; using System.Collections.Generic; using UnityEditorInternal; using UnityEngine; using UnityEngine.XR.Hands; namespace UnityEditor.XR.Hands { /// /// Custom editor for an . /// [CustomEditor(typeof(XRHandSkeletonDriver), true), CanEditMultipleObjects] public class XRHandSkeletonDriverEditor : BaseXRHandsEditor { /// /// Draws a list of joint transform references that draws both the joint and the transform in a single line. /// class JointTransformReferenceReorderableList : ReorderableList { public JointTransformReferenceReorderableList(SerializedObject serializedObject, SerializedProperty serializedProperty) : base(serializedObject, serializedProperty, true, true, true, true) { drawElementCallback = OnDrawListElement; drawHeaderCallback = OnDrawListHeader; elementHeight = EditorGUIUtility.singleLineHeight; } void OnDrawListHeader(Rect rect) { GUI.Label(rect, Contents.jointTransformReferences); } void OnDrawListElement(Rect rect, int elementIndex, bool isActive, bool isFocused) { var element = serializedProperty.GetArrayElementAtIndex(elementIndex); rect.yMin += 2; var width = rect.width; var middle = rect.xMin + width * 0.5f; const float padding = 8f; rect.xMax = middle - padding; EditorGUI.PropertyField(rect, element.FindPropertyRelative("m_XRHandJointID"), GUIContent.none); rect.xMax = rect.xMin + width; rect.xMin = middle; EditorGUI.PropertyField(rect, element.FindPropertyRelative("m_JointTransform"), GUIContent.none); } } const string k_JointsExpandedStateKey = "XRHands." + nameof(XRHandSkeletonDriverEditor) + ".JointsExpanded"; const string k_FindJointsFromParentUndoMessage = "Find Joints from Parent"; const string k_ScriptPropertyName = "m_Script"; const string k_XRHandTrackingEventsPropertyName = "m_XRHandTrackingEvents"; const string k_JointTransformReferencesPropertyName = "m_JointTransformReferences"; const string k_RootTransformPropertyName = "m_RootTransform"; static readonly string[] k_KnownProperties = { k_ScriptPropertyName, k_XRHandTrackingEventsPropertyName, k_JointTransformReferencesPropertyName, k_RootTransformPropertyName, }; SerializedProperty m_XRHandTrackingEvents; SerializedProperty m_JointTransformReferences; SerializedProperty m_RootTransform; JointTransformReferenceReorderableList m_JointTransformReferencesList; List m_MissingJointNames = new List(); bool m_DrawHelpBoxForFindingJoints; string m_ErrorFindingJointsMessage; bool m_JointsExpanded; /// /// Contents of GUI elements used by this editor. /// static class Contents { public static readonly GUIContent handTrackingEvents = EditorGUIUtility.TrTextContent("XR Hand Tracking Events"); public static readonly GUIContent jointTransformReferences = EditorGUIUtility.TrTextContent("Joint Transform References"); public static readonly GUIContent parentTransform = EditorGUIUtility.TrTextContent("Root Transform"); public static readonly GUIContent findJoints = EditorGUIUtility.TrTextContent("Find Joints", "Search the root transform's children for transforms that start or end with a joint name."); public static readonly GUIContent clearJoints = EditorGUIUtility.TrTextContent("Clear"); } void OnEnable() { m_XRHandTrackingEvents = serializedObject.FindProperty(k_XRHandTrackingEventsPropertyName); m_JointTransformReferences = serializedObject.FindProperty(k_JointTransformReferencesPropertyName); m_RootTransform = serializedObject.FindProperty(k_RootTransformPropertyName); m_JointTransformReferencesList = new JointTransformReferenceReorderableList(serializedObject, m_JointTransformReferences); m_JointsExpanded = SessionState.GetBool(k_JointsExpandedStateKey, false); } void OnDisable() { SessionState.SetBool(k_JointsExpandedStateKey, m_JointsExpanded); } void OnFindJointsButtonPressed() { foreach (var targetObject in targets) { var skeletonDriver = (XRHandSkeletonDriver)targetObject; Undo.RecordObject(skeletonDriver, k_FindJointsFromParentUndoMessage); skeletonDriver.FindJointsFromRoot(m_MissingJointNames); EditorUtility.SetDirty(skeletonDriver); if (m_MissingJointNames.Count > 0) { m_DrawHelpBoxForFindingJoints = true; m_ErrorFindingJointsMessage = $"Could not find joints: {string.Join(", ", m_MissingJointNames)}"; } else { m_DrawHelpBoxForFindingJoints = false; } } serializedObject.SetIsDifferentCacheDirty(); } /// public override void OnInspectorGUI() { serializedObject.Update(); DrawInspector(); DrawPropertiesExcluding(serializedObject, k_KnownProperties); serializedObject.ApplyModifiedProperties(); } void DrawInspector() { DrawScript(); EditorGUILayout.PropertyField(m_XRHandTrackingEvents, Contents.handTrackingEvents, true); m_JointsExpanded = EditorGUILayout.Foldout(m_JointsExpanded, Contents.jointTransformReferences, true); if (m_JointsExpanded) { using (new EditorGUI.IndentLevelScope()) { DrawJointTransformsFoldout(); } } EditorGUILayout.Space(); } void DrawJointTransformsFoldout() { EditorGUILayout.PropertyField(m_RootTransform, Contents.parentTransform); using (new EditorGUILayout.HorizontalScope()) { using (new EditorGUI.DisabledScope(m_RootTransform.objectReferenceValue == null)) { if (GUILayout.Button(Contents.findJoints)) { OnFindJointsButtonPressed(); } } using (new EditorGUI.DisabledScope(m_JointTransformReferences.arraySize == 0)) { if (GUILayout.Button(Contents.clearJoints)) { m_JointTransformReferences.ClearArray(); } } } if (m_DrawHelpBoxForFindingJoints) { EditorGUILayout.HelpBox(m_ErrorFindingJointsMessage, MessageType.Warning, true); } m_JointTransformReferencesList.DoLayoutList(); } } }