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