ShadowCascadeSplitGUI.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. using System.Linq;
  3. using UnityEngine;
  4. namespace UnityEditor.Rendering.Universal
  5. {
  6. static class ShadowCascadeSplitGUI
  7. {
  8. private const int kSliderbarTopMargin = 2;
  9. private const int kSliderbarHeight = 29;
  10. private const int kSliderbarBottomMargin = 2;
  11. private const int kPartitionHandleWidth = 2;
  12. private const int kPartitionHandleExtraHitAreaWidth = 2;
  13. private static readonly Color[] kCascadeColors =
  14. {
  15. new Color(0.5f, 0.5f, 0.6f, 1.0f),
  16. new Color(0.5f, 0.6f, 0.5f, 1.0f),
  17. new Color(0.6f, 0.6f, 0.5f, 1.0f),
  18. new Color(0.6f, 0.5f, 0.5f, 1.0f),
  19. };
  20. // using a LODGroup skin
  21. private static readonly GUIStyle s_CascadeSliderBG = "LODSliderRange";
  22. private static readonly GUIStyle s_TextCenteredStyle = new GUIStyle(EditorStyles.whiteMiniLabel)
  23. {
  24. alignment = TextAnchor.MiddleCenter
  25. };
  26. // Internal struct to bundle drag information
  27. private class DragCache
  28. {
  29. public int m_ActivePartition; // the cascade partition that we are currently dragging/resizing
  30. public float m_NormalizedPartitionSize; // the normalized size of the partition (0.0f < size < 1.0f)
  31. public Vector2 m_LastCachedMousePosition; // mouse position the last time we registered a drag or mouse down.
  32. public DragCache(int activePartition, float normalizedPartitionSize, Vector2 currentMousePos)
  33. {
  34. m_ActivePartition = activePartition;
  35. m_NormalizedPartitionSize = normalizedPartitionSize;
  36. m_LastCachedMousePosition = currentMousePos;
  37. }
  38. };
  39. private static DragCache s_DragCache;
  40. private static readonly int s_CascadeSliderId = "s_CascadeSliderId".GetHashCode();
  41. private static SceneView s_RestoreSceneView;
  42. private static SceneView.CameraMode s_OldSceneDrawMode;
  43. private static bool s_OldSceneLightingMode;
  44. /**
  45. * Static function to handle the GUI and User input related to the cascade slider.
  46. *
  47. * @param normalizedCascadePartition The array of partition sizes in the range 0.0f - 1.0f; expects ONE entry if cascades = 2, and THREE if cascades=4
  48. * The last entry will be automatically determined by summing up the array, and doing 1.0f - sum
  49. */
  50. public static void HandleCascadeSliderGUI(ref float[] normalizedCascadePartitions)
  51. {
  52. EditorGUILayout.BeginHorizontal();
  53. GUILayout.Space(EditorGUI.indentLevel * 15f);
  54. // get the inspector width since we need it while drawing the partition rects.
  55. // Only way currently is to reserve the block in the layout using GetRect(), and then immediately drawing the empty box
  56. // to match the call to GetRect.
  57. // From this point on, we move to non-layout based code.
  58. var sliderRect = GUILayoutUtility.GetRect(GUIContent.none
  59. , s_CascadeSliderBG
  60. , GUILayout.Height(kSliderbarTopMargin + kSliderbarHeight + kSliderbarBottomMargin)
  61. , GUILayout.ExpandWidth(true));
  62. GUI.Box(sliderRect, GUIContent.none);
  63. EditorGUILayout.EndHorizontal();
  64. float currentX = sliderRect.x;
  65. float cascadeBoxStartY = sliderRect.y + kSliderbarTopMargin;
  66. float cascadeSliderWidth = sliderRect.width - (normalizedCascadePartitions.Length * kPartitionHandleWidth);
  67. Color origTextColor = GUI.color;
  68. Color origBackgroundColor = GUI.backgroundColor;
  69. int colorIndex = -1;
  70. // setup the array locally with the last partition
  71. float[] adjustedCascadePartitions = new float[normalizedCascadePartitions.Length + 1];
  72. System.Array.Copy(normalizedCascadePartitions, adjustedCascadePartitions, normalizedCascadePartitions.Length);
  73. adjustedCascadePartitions[adjustedCascadePartitions.Length - 1] = 1.0f - normalizedCascadePartitions.Sum();
  74. // check for user input on any of the partition handles
  75. // this mechanism gets the current event in the queue... make sure that the mouse is over our control before consuming the event
  76. int sliderControlId = GUIUtility.GetControlID(s_CascadeSliderId, FocusType.Passive);
  77. Event currentEvent = Event.current;
  78. int hotPartitionHandleIndex = -1; // the index of any partition handle that we are hovering over or dragging
  79. // draw each cascade partition
  80. for (int i = 0; i < adjustedCascadePartitions.Length; ++i)
  81. {
  82. float currentPartition = adjustedCascadePartitions[i];
  83. colorIndex = (colorIndex + 1) % kCascadeColors.Length;
  84. GUI.backgroundColor = kCascadeColors[colorIndex];
  85. float boxLength = (cascadeSliderWidth * currentPartition);
  86. // main cascade box
  87. Rect partitionRect = new Rect(currentX, cascadeBoxStartY, boxLength, kSliderbarHeight);
  88. GUI.Box(partitionRect, GUIContent.none, s_CascadeSliderBG);
  89. currentX += boxLength;
  90. // cascade box percentage text
  91. GUI.color = Color.white;
  92. Rect textRect = partitionRect;
  93. var cascadeText = string.Format("{0}\n{1:F1}%", i, currentPartition * 100.0f);
  94. GUI.Label(textRect, cascadeText, s_TextCenteredStyle);
  95. // no need to draw the partition handle for last box
  96. if (i == adjustedCascadePartitions.Length - 1)
  97. break;
  98. // partition handle
  99. GUI.backgroundColor = Color.black;
  100. Rect handleRect = partitionRect;
  101. handleRect.x = currentX;
  102. handleRect.width = kPartitionHandleWidth;
  103. GUI.Box(handleRect, GUIContent.none, s_CascadeSliderBG);
  104. // we want a thin handle visually (since wide black bar looks bad), but a slightly larger
  105. // hit area for easier manipulation
  106. Rect handleHitRect = handleRect;
  107. handleHitRect.xMin -= kPartitionHandleExtraHitAreaWidth;
  108. handleHitRect.xMax += kPartitionHandleExtraHitAreaWidth;
  109. if (handleHitRect.Contains(currentEvent.mousePosition))
  110. hotPartitionHandleIndex = i;
  111. // add regions to slider where the cursor changes to Resize-Horizontal
  112. if (s_DragCache == null)
  113. {
  114. EditorGUIUtility.AddCursorRect(handleHitRect, MouseCursor.ResizeHorizontal, sliderControlId);
  115. }
  116. currentX += kPartitionHandleWidth;
  117. }
  118. GUI.color = origTextColor;
  119. GUI.backgroundColor = origBackgroundColor;
  120. EventType eventType = currentEvent.GetTypeForControl(sliderControlId);
  121. switch (eventType)
  122. {
  123. case EventType.MouseDown:
  124. if (hotPartitionHandleIndex >= 0)
  125. {
  126. s_DragCache = new DragCache(hotPartitionHandleIndex, normalizedCascadePartitions[hotPartitionHandleIndex], currentEvent.mousePosition);
  127. if (GUIUtility.hotControl == 0)
  128. GUIUtility.hotControl = sliderControlId;
  129. currentEvent.Use();
  130. // Switch active scene view into shadow cascades visualization mode, once we start
  131. // tweaking cascade splits.
  132. if (s_RestoreSceneView == null)
  133. {
  134. s_RestoreSceneView = SceneView.lastActiveSceneView;
  135. if (s_RestoreSceneView != null)
  136. {
  137. s_OldSceneDrawMode = s_RestoreSceneView.cameraMode;
  138. #if UNITY_2019_1_OR_NEWER
  139. s_OldSceneLightingMode = s_RestoreSceneView.sceneLighting;
  140. #else
  141. s_OldSceneLightingMode = s_RestoreSceneView.m_SceneLighting;
  142. #endif
  143. s_RestoreSceneView.cameraMode = SceneView.GetBuiltinCameraMode(DrawCameraMode.ShadowCascades);
  144. }
  145. }
  146. }
  147. break;
  148. case EventType.MouseUp:
  149. // mouseUp event anywhere should release the hotcontrol (if it belongs to us), drags (if any)
  150. if (GUIUtility.hotControl == sliderControlId)
  151. {
  152. GUIUtility.hotControl = 0;
  153. currentEvent.Use();
  154. }
  155. s_DragCache = null;
  156. // Restore previous scene view drawing mode once we stop tweaking cascade splits.
  157. if (s_RestoreSceneView != null)
  158. {
  159. s_RestoreSceneView.cameraMode = s_OldSceneDrawMode;
  160. #if UNITY_2019_1_OR_NEWER
  161. s_RestoreSceneView.sceneLighting = s_OldSceneLightingMode;
  162. #else
  163. s_RestoreSceneView.m_SceneLighting = s_OldSceneLightingMode;
  164. #endif
  165. s_RestoreSceneView = null;
  166. }
  167. break;
  168. case EventType.MouseDrag:
  169. if (GUIUtility.hotControl != sliderControlId)
  170. break;
  171. // convert the mouse movement to normalized cascade width. Make sure that we are safe to apply the delta before using it.
  172. float delta = (currentEvent.mousePosition - s_DragCache.m_LastCachedMousePosition).x / cascadeSliderWidth;
  173. bool isLeftPartitionHappy = ((adjustedCascadePartitions[s_DragCache.m_ActivePartition] + delta) > 0.0f);
  174. bool isRightPartitionHappy = ((adjustedCascadePartitions[s_DragCache.m_ActivePartition + 1] - delta) > 0.0f);
  175. if (isLeftPartitionHappy && isRightPartitionHappy)
  176. {
  177. s_DragCache.m_NormalizedPartitionSize += delta;
  178. normalizedCascadePartitions[s_DragCache.m_ActivePartition] = s_DragCache.m_NormalizedPartitionSize;
  179. if (s_DragCache.m_ActivePartition < normalizedCascadePartitions.Length - 1)
  180. normalizedCascadePartitions[s_DragCache.m_ActivePartition + 1] -= delta;
  181. GUI.changed = true;
  182. }
  183. s_DragCache.m_LastCachedMousePosition = currentEvent.mousePosition;
  184. currentEvent.Use();
  185. break;
  186. }
  187. }
  188. }
  189. }