ComparisonGizmoController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. using System;
  2. using UnityEngine.UIElements;
  3. using UnityEngine;
  4. namespace UnityEditor.Rendering.LookDev
  5. {
  6. //TODO: clamps to always have both node on screen
  7. class ComparisonGizmoController : Manipulator
  8. {
  9. const float k_DragPadding = 0.05f;
  10. const float k_ReferenceScale = 1080f;
  11. ComparisonGizmoState m_State;
  12. SwitchableCameraController m_Switcher;
  13. enum Selected
  14. {
  15. None,
  16. NodeFirstView,
  17. NodeSecondView,
  18. PlaneSeparator,
  19. Fader
  20. }
  21. Selected m_Selected;
  22. Vector2 m_SavedRelativePositionOnMouseDown;
  23. bool m_IsDragging;
  24. bool isDragging
  25. {
  26. get => m_IsDragging;
  27. set
  28. {
  29. //As in scene view, stop dragging as first button is release in case of multiple button down
  30. if (value ^ m_IsDragging)
  31. {
  32. if (value)
  33. {
  34. target.RegisterCallback<MouseMoveEvent>(OnMouseDrag);
  35. target.CaptureMouse();
  36. }
  37. else
  38. {
  39. target.ReleaseMouse();
  40. target.UnregisterCallback<MouseMoveEvent>(OnMouseDrag);
  41. }
  42. m_IsDragging = value;
  43. }
  44. }
  45. }
  46. public ComparisonGizmoController(SwitchableCameraController switcher)
  47. {
  48. m_Switcher = switcher;
  49. }
  50. public void UpdateGizmoState(ComparisonGizmoState state)
  51. {
  52. m_State = state;
  53. }
  54. protected override void RegisterCallbacksOnTarget()
  55. {
  56. target.RegisterCallback<MouseDownEvent>(OnMouseDown);
  57. target.RegisterCallback<MouseUpEvent>(OnMouseUp);
  58. target.RegisterCallback<WheelEvent>(OnScrollWheel);
  59. }
  60. protected override void UnregisterCallbacksFromTarget()
  61. {
  62. target.UnregisterCallback<MouseDownEvent>(OnMouseDown);
  63. target.UnregisterCallback<MouseUpEvent>(OnMouseUp);
  64. target.UnregisterCallback<WheelEvent>(OnScrollWheel);
  65. }
  66. void OnScrollWheel(WheelEvent evt)
  67. {
  68. if (LookDev.currentContext.layout.viewLayout != Layout.CustomSplit)
  69. return;
  70. if (GetViewFromComposition(evt.localMousePosition) == ViewIndex.Second)
  71. m_Switcher.SwitchUntilNextWheelEvent();
  72. //let event be propagated to views
  73. }
  74. void OnMouseDown(MouseDownEvent evt)
  75. {
  76. if (LookDev.currentContext.layout.viewLayout != Layout.CustomSplit)
  77. return;
  78. Rect displayRect = target.contentRect;
  79. SelectGizmoZone(GetNormalizedCoordinates(evt.localMousePosition, displayRect));
  80. if (m_Selected != Selected.None)
  81. {
  82. m_SavedRelativePositionOnMouseDown = GetNormalizedCoordinates(evt.localMousePosition, displayRect) - m_State.center;
  83. isDragging = true;
  84. //We do not want to move camera and gizmo at the same time.
  85. evt.StopImmediatePropagation();
  86. }
  87. else
  88. {
  89. //else let event be propagated to views
  90. if (GetViewFromComposition(evt.localMousePosition) == ViewIndex.Second)
  91. m_Switcher.SwitchUntilNextEndOfDrag();
  92. }
  93. }
  94. void OnMouseUp(MouseUpEvent evt)
  95. {
  96. if (LookDev.currentContext.layout.viewLayout != Layout.CustomSplit
  97. || m_Selected == Selected.None)
  98. return;
  99. // deadzone in fader gizmo
  100. if (m_Selected == Selected.Fader && Mathf.Abs(m_State.blendFactor) < ComparisonGizmoState.circleRadiusSelected / (m_State.length - ComparisonGizmoState.circleRadius))
  101. m_State.blendFactor = 0f;
  102. m_Selected = Selected.None;
  103. isDragging = false;
  104. //We do not want to move camera and gizmo at the same time.
  105. evt.StopImmediatePropagation();
  106. LookDev.SaveConfig();
  107. }
  108. void OnMouseDrag(MouseMoveEvent evt)
  109. {
  110. if (m_Selected == Selected.None)
  111. return;
  112. switch (m_Selected)
  113. {
  114. case Selected.PlaneSeparator: OnDragPlaneSeparator(evt); break;
  115. case Selected.NodeFirstView:
  116. case Selected.NodeSecondView: OnDragPlaneNodeExtremity(evt); break;
  117. case Selected.Fader: OnDragFader(evt); break;
  118. default: throw new ArgumentException("Unknown kind of Selected");
  119. }
  120. }
  121. void OnDragPlaneSeparator(MouseMoveEvent evt)
  122. {
  123. //TODO: handle case when resizing window (clamping)
  124. Vector2 newPosition = GetNormalizedCoordinates(evt.localMousePosition, target.contentRect) - m_SavedRelativePositionOnMouseDown;
  125. // We clamp the center of the gizmo to the border of the screen in order to avoid being able to put it out of the screen.
  126. // The safe band is here to ensure that you always see at least part of the gizmo in order to be able to grab it again.
  127. //Vector2 extends = GetNormalizedCoordinates(new Vector2(displayRect.width, displayRect.height), displayRect);
  128. //newPosition.x = Mathf.Clamp(newPosition.x, -extends.x + k_DragPadding, extends.x - k_DragPadding);
  129. //newPosition.y = Mathf.Clamp(newPosition.y, -extends.y + k_DragPadding, extends.y - k_DragPadding);
  130. m_State.Update(newPosition, m_State.length, m_State.angle);
  131. //We do not want to move camera and gizmo at the same time.
  132. evt.StopImmediatePropagation();
  133. }
  134. void OnDragPlaneNodeExtremity(MouseMoveEvent evt)
  135. {
  136. Vector2 normalizedCoord = GetNormalizedCoordinates(evt.localMousePosition, target.contentRect);
  137. Vector2 basePoint, newPoint;
  138. float angleSnapping = Mathf.Deg2Rad * 45.0f * 0.5f;
  139. newPoint = normalizedCoord;
  140. basePoint = m_Selected == Selected.NodeFirstView ? m_State.point2 : m_State.point1;
  141. // Snap to a multiple of "angleSnapping"
  142. if ((evt.modifiers & EventModifiers.Shift) != 0)
  143. {
  144. Vector3 verticalPlane = new Vector3(-1.0f, 0.0f, basePoint.x);
  145. float side = Vector3.Dot(new Vector3(normalizedCoord.x, normalizedCoord.y, 1.0f), verticalPlane);
  146. float angle = Mathf.Deg2Rad * Vector2.Angle(new Vector2(0.0f, 1.0f), normalizedCoord - basePoint);
  147. if (side > 0.0f)
  148. angle = 2.0f * Mathf.PI - angle;
  149. angle = (int)(angle / angleSnapping) * angleSnapping;
  150. Vector2 dir = normalizedCoord - basePoint;
  151. float length = dir.magnitude;
  152. newPoint = basePoint + new Vector2(Mathf.Sin(angle), Mathf.Cos(angle)) * length;
  153. }
  154. if (m_Selected == Selected.NodeFirstView)
  155. m_State.Update(newPoint, basePoint);
  156. else
  157. m_State.Update(basePoint, newPoint);
  158. //We do not want to move camera and gizmo at the same time.
  159. evt.StopImmediatePropagation();
  160. }
  161. void OnDragFader(MouseMoveEvent evt)
  162. {
  163. Vector2 mousePosition = GetNormalizedCoordinates(evt.localMousePosition, target.contentRect);
  164. float distanceToOrthoPlane = -Vector3.Dot(new Vector3(mousePosition.x, mousePosition.y, 1.0f), m_State.planeOrtho) / m_State.blendFactorMaxGizmoDistance;
  165. m_State.blendFactor = Mathf.Clamp(distanceToOrthoPlane, -1.0f, 1.0f);
  166. //We do not want to move camera and gizmo at the same time.
  167. evt.StopImmediatePropagation();
  168. }
  169. void SelectGizmoZone(Vector2 normalizedMousePosition)
  170. {
  171. //TODO: Optimize
  172. Vector3 normalizedMousePositionZ1 = new Vector3(normalizedMousePosition.x, normalizedMousePosition.y, 1.0f);
  173. float distanceToPlane = Vector3.Dot(normalizedMousePositionZ1, m_State.plane);
  174. float absDistanceToPlane = Mathf.Abs(distanceToPlane);
  175. float distanceFromCenter = Vector2.Distance(normalizedMousePosition, m_State.center);
  176. float distanceToOrtho = Vector3.Dot(normalizedMousePositionZ1, m_State.planeOrtho);
  177. float side = (distanceToOrtho > 0.0f) ? 1.0f : -1.0f;
  178. Vector2 orthoPlaneNormal = new Vector2(m_State.planeOrtho.x, m_State.planeOrtho.y);
  179. Selected selected = Selected.None;
  180. if (absDistanceToPlane < ComparisonGizmoState.circleRadiusSelected && (distanceFromCenter < (m_State.length + ComparisonGizmoState.circleRadiusSelected)))
  181. {
  182. if (absDistanceToPlane < ComparisonGizmoState.thicknessSelected)
  183. selected = Selected.PlaneSeparator;
  184. Vector2 circleCenter = m_State.center + side * orthoPlaneNormal * m_State.length;
  185. float d = Vector2.Distance(normalizedMousePosition, circleCenter);
  186. if (d <= ComparisonGizmoState.circleRadiusSelected)
  187. selected = side > 0.0f ? Selected.NodeFirstView : Selected.NodeSecondView;
  188. float maxBlendCircleDistanceToCenter = m_State.blendFactorMaxGizmoDistance;
  189. float blendCircleDistanceToCenter = m_State.blendFactor * maxBlendCircleDistanceToCenter;
  190. Vector2 blendCircleCenter = m_State.center - orthoPlaneNormal * blendCircleDistanceToCenter;
  191. float blendCircleSelectionRadius = Mathf.Lerp(ComparisonGizmoState.blendFactorCircleRadius, ComparisonGizmoState.blendFactorCircleRadiusSelected, Mathf.Clamp((maxBlendCircleDistanceToCenter - Mathf.Abs(blendCircleDistanceToCenter)) / (ComparisonGizmoState.blendFactorCircleRadiusSelected - ComparisonGizmoState.blendFactorCircleRadius), 0.0f, 1.0f));
  192. if ((normalizedMousePosition - blendCircleCenter).magnitude < blendCircleSelectionRadius)
  193. selected = Selected.Fader;
  194. }
  195. m_Selected = selected;
  196. }
  197. //normalize in [-1,1]^2 for a 1080^2. Can be above 1 for higher than 1080.
  198. internal static Vector2 GetNormalizedCoordinates(Vector2 localMousePosition, Rect rect)
  199. => new Vector2(
  200. (2f * localMousePosition.x - rect.width) / k_ReferenceScale,
  201. (-2f * localMousePosition.y + rect.height) / k_ReferenceScale);
  202. ViewIndex GetViewFromComposition(Vector2 localCoordinate)
  203. {
  204. Vector2 normalizedLocalCoordinate = GetNormalizedCoordinates(localCoordinate, target.contentRect);
  205. return Vector3.Dot(new Vector3(normalizedLocalCoordinate.x, normalizedLocalCoordinate.y, 1), m_State.plane) >= 0
  206. ? ViewIndex.First
  207. : ViewIndex.Second;
  208. }
  209. }
  210. }