From 3e68635735b0c7788fc7e75e48590e7a23167285 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sun, 1 Apr 2018 21:42:44 +0200 Subject: [PATCH] Finished Reroutes --- Scripts/Editor/NodeEditorAction.cs | 110 ++++++++++++++++++----------- Scripts/Editor/NodeEditorGUI.cs | 82 ++++++++++----------- Scripts/NodeGraph.cs | 2 - Scripts/NodePort.cs | 19 +++-- 4 files changed, 118 insertions(+), 95 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index bd3661a..4c20270 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -13,17 +13,36 @@ namespace XNodeEditor { private bool IsDraggingPort { get { return draggedOutput != null; } } private bool IsHoveringPort { get { return hoveredPort != null; } } private bool IsHoveringNode { get { return hoveredNode != null; } } - private bool IsHoveringReroute { get { return hoveredReroute >= 0; } } + private bool IsHoveringReroute { get { return hoveredReroute.port != null; } } private XNode.Node hoveredNode = null; [NonSerialized] private XNode.NodePort hoveredPort = null; [NonSerialized] private XNode.NodePort draggedOutput = null; [NonSerialized] private XNode.NodePort draggedOutputTarget = null; - private int hoveredReroute = -1; - private List selectedReroutes = new List(); + [NonSerialized] private List draggedOutputReroutes = new List(); + private RerouteReference hoveredReroute = new RerouteReference(); + private List selectedReroutes = new List(); private Rect nodeRects; private Vector2 dragBoxStart; private UnityEngine.Object[] preBoxSelection; - private int[] preBoxSelectionReroute; + private RerouteReference[] preBoxSelectionReroute; + private Rect selectionBox; + + private struct RerouteReference { + public XNode.NodePort port; + public int connectionIndex; + public int pointIndex; + + public RerouteReference(XNode.NodePort port, int connectionIndex, int pointIndex) { + this.port = port; + this.connectionIndex = connectionIndex; + this.pointIndex = pointIndex; + } + + public void InsertPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex).Insert(pointIndex, pos); } + public void SetPoint(Vector2 pos) { port.GetReroutePoints(connectionIndex) [pointIndex] = pos; } + public void RemovePoint() { port.GetReroutePoints(connectionIndex).RemoveAt(pointIndex); } + public Vector2 GetPoint() { return port.GetReroutePoints(connectionIndex) [pointIndex]; } + } public void Controls() { wantsMouseMove = true; @@ -77,7 +96,7 @@ namespace XNodeEditor { pos.x = (Mathf.Round((pos.x + 8) / 16) * 16); pos.y = (Mathf.Round((pos.y + 8) / 16) * 16); } - SetReroute(selectedReroutes[i], pos); + selectedReroutes[i].SetPoint(pos); } Repaint(); } else if (currentActivity == NodeActivity.HoldGrid) { @@ -87,6 +106,11 @@ namespace XNodeEditor { dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); } else if (currentActivity == NodeActivity.DragGrid) { + Vector2 boxStartPos = GridToWindowPosition(dragBoxStart); + Vector2 boxSize = e.mousePosition - boxStartPos; + if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } + if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); } + selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } } else if (e.button == 1 || e.button == 2) { @@ -102,6 +126,8 @@ namespace XNodeEditor { case EventType.MouseDown: Repaint(); if (e.button == 0) { + draggedOutputReroutes.Clear(); + if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; @@ -110,6 +136,8 @@ namespace XNodeEditor { if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; + int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); + draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex); hoveredPort.Disconnect(output); draggedOutput = output; draggedOutputTarget = hoveredPort; @@ -131,7 +159,7 @@ namespace XNodeEditor { if (e.control || e.shift) selectedReroutes.Add(hoveredReroute); // Select it else { - selectedReroutes = new List() { hoveredReroute }; + selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } @@ -159,6 +187,8 @@ namespace XNodeEditor { if (draggedOutputTarget != null) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget); + int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget); + draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); EditorUtility.SetDirty(graph); } @@ -190,7 +220,7 @@ namespace XNodeEditor { // If click reroute, select it. if (IsHoveringReroute && !(e.control || e.shift)) { - selectedReroutes = new List() { hoveredReroute }; + selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } @@ -198,7 +228,12 @@ namespace XNodeEditor { currentActivity = NodeActivity.Idle; } else if (e.button == 1) { if (!isPanning) { - if (IsHoveringReroute) { + if (IsDraggingPort) { + draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition)); + } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) { + selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint()); + selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1); + } else if (IsHoveringReroute) { ShowRerouteContextMenu(hoveredReroute); } else if (IsHoveringPort) { ShowPortContextMenu(hoveredPort); @@ -243,7 +278,7 @@ namespace XNodeEditor { // Selected reroutes for (int i = 0; i < selectedReroutes.Count; i++) { - dragOffset[Selection.objects.Length + i] = GetReroutePos(selectedReroutes[i]) - WindowToGridPosition(current.mousePosition); + dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition); } } @@ -310,44 +345,37 @@ namespace XNodeEditor { Selection.objects = newNodes; } - /// Add a reroute node to a graph and return index - public int AddReroute(Vector2 position) { - SerializedProperty reroutes = graphEditor.serializedObject.FindProperty("reroutes"); - reroutes.arraySize++; - reroutes.GetArrayElementAtIndex(reroutes.arraySize - 1).vector2Value = position; - graphEditor.serializedObject.ApplyModifiedProperties(); - return reroutes.arraySize - 1; - } - - /// Set the position of a reroute node - public void SetReroute(int index, Vector2 position) { - SerializedProperty reroutes = graphEditor.serializedObject.FindProperty("reroutes"); - reroutes.GetArrayElementAtIndex(index).vector2Value = position; - graphEditor.serializedObject.ApplyModifiedProperties(); - } - - /// Get the position of a reroute node - public Vector2 GetReroutePos(int index) { - SerializedProperty reroutes = graphEditor.serializedObject.FindProperty("reroutes"); - return reroutes.GetArrayElementAtIndex(index).vector2Value; - } - - /// Remove a reroute node - public void RemoveReroute(int index) { - SerializedProperty reroutes = graphEditor.serializedObject.FindProperty("reroutes"); - reroutes.DeleteArrayElementAtIndex(index); - graphEditor.serializedObject.ApplyModifiedProperties(); - } - /// Draw a connection as we are dragging it public void DrawDraggedConnection() { if (IsDraggingPort) { - if (!_portConnectionPoints.ContainsKey(draggedOutput)) return; - Vector2 from = _portConnectionPoints[draggedOutput].center; - Vector2 to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition); Color col = NodeEditorPreferences.GetTypeColor(draggedOutput.ValueType); + + if (!_portConnectionPoints.ContainsKey(draggedOutput)) return; col.a = 0.6f; + Vector2 from = _portConnectionPoints[draggedOutput].center; + Vector2 to = Vector2.zero; + for (int i = 0; i < draggedOutputReroutes.Count; i++) { + to = draggedOutputReroutes[i]; + DrawConnection(from, to, col); + from = to; + } + to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition); DrawConnection(from, to, col); + + Color bgcol = Color.black; + Color frcol = col; + bgcol.a = 0.6f; + frcol.a = 0.6f; + + // Loop through reroute points again and draw the points + for (int i = 0; i < draggedOutputReroutes.Count; i++) { + // Draw reroute point at position + Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16)); + rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8); + rect = GridToWindowRect(rect); + + NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol); + } } } diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index a09588f..2ff98ec 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -20,7 +20,6 @@ namespace XNodeEditor { DrawGrid(position, zoom, panOffset); DrawConnections(); DrawDraggedConnection(); - DrawReroutes(); DrawNodes(); DrawSelectionBox(); DrawTooltip(); @@ -89,9 +88,9 @@ namespace XNodeEditor { } /// Show right-click context menu for hovered reroute - void ShowRerouteContextMenu(int reroute) { + void ShowRerouteContextMenu(RerouteReference reroute) { GenericMenu contextMenu = new GenericMenu(); - contextMenu.AddItem(new GUIContent("Remove"), false, () => RemoveReroute(reroute)); + contextMenu.AddItem(new GUIContent("Remove"), false, () => reroute.RemovePoint()); contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); AssetDatabase.SaveAssets(); } @@ -147,7 +146,6 @@ namespace XNodeEditor { }); } contextMenu.AddSeparator(""); - contextMenu.AddItem(new GUIContent("Reroute"), false, () => AddReroute(pos)); contextMenu.AddItem(new GUIContent("Preferences"), false, () => OpenPreferences()); AddCustomContextMenuItems(contextMenu, graph); contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -217,19 +215,21 @@ namespace XNodeEditor { /// Draws all connections public void DrawConnections() { - List drawnReroutes = new List(); + Vector2 mousePos = Event.current.mousePosition; + List selection = preBoxSelectionReroute != null ? new List(preBoxSelectionReroute) : new List(); + hoveredReroute = new RerouteReference(); foreach (XNode.Node node in graph.nodes) { //If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset. if (node == null) continue; + // Draw full connections and output > reroute foreach (XNode.NodePort output in node.Outputs) { //Needs cleanup. Null checks are ugly if (!portConnectionPoints.ContainsKey(output)) continue; Color connectionColor = graphEditor.GetTypeColor(output.ValueType); - Vector2 from = _portConnectionPoints[output].center; for (int k = 0; k < output.ConnectionCount; k++) { XNode.NodePort input = output.GetConnection(k); @@ -238,48 +238,36 @@ namespace XNodeEditor { if (!input.IsConnectedTo(output)) input.Connect(output); if (!_portConnectionPoints.ContainsKey(input)) continue; + Vector2 from = _portConnectionPoints[output].center; Vector2 to = Vector2.zero; - int[] rerouteIndices = output.GetReroutes(k); - for (int i = 0; i < rerouteIndices.Length + 1; i++) { - if (i != rerouteIndices.Length) to = graph.reroutes[rerouteIndices[i]]; - else to = _portConnectionPoints[input].center; + List reroutePoints = output.GetReroutePoints(k); + // Loop through reroute points and draw the path + for (int i = 0; i < reroutePoints.Count; i++) { + to = reroutePoints[i]; DrawConnection(from, to, connectionColor); from = to; + } + to = _portConnectionPoints[input].center; + DrawConnection(from, to, connectionColor); + + // Loop through reroute points again and draw the points + for (int i = 0; i < reroutePoints.Count; i++) { + // Draw reroute point at position + Rect rect = new Rect(reroutePoints[i], new Vector2(16, 16)); + rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8); + rect = GridToWindowRect(rect); + Color bgcol = new Color32(90, 97, 105, 255);; + if (selectedReroutes.Contains(new RerouteReference(output, k, i))) bgcol = Color.yellow; + NodeEditorGUILayout.DrawPortHandle(rect, bgcol, connectionColor); + + if (rect.Overlaps(selectionBox)) selection.Add(new RerouteReference(output, k, i)); + if (rect.Contains(mousePos)) hoveredReroute = new RerouteReference(output, k, i); - if (drawnReroutes.Contains(i)) break; - else drawnReroutes.Add(i); } } } } - } - - /// Draws all connections - public void DrawReroutes() { - Vector2 mousePos = Event.current.mousePosition; - SerializedProperty reroutes = graphEditor.serializedObject.FindProperty("reroutes"); - - // Box selection support - List selection = preBoxSelectionReroute != null ? new List(preBoxSelectionReroute) : new List(); - Vector2 boxStartPos = GridToWindowPosition(dragBoxStart); - Vector2 boxSize = mousePos - boxStartPos; - if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } - if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); } - Rect boxRect = new Rect(boxStartPos, boxSize); - - hoveredReroute = -1; - for (int i = 0; i < reroutes.arraySize; i++) { - Rect rect = new Rect(reroutes.GetArrayElementAtIndex(i).vector2Value, new Vector2(16, 16)); - rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8); - rect = GridToWindowRect(rect); - Color bgcol = Color.black; - if (selectedReroutes.Contains(i)) bgcol = Color.yellow; - NodeEditorGUILayout.DrawPortHandle(rect, bgcol, Color.white); - - if (rect.Overlaps(boxRect)) selection.Add(i); - if (rect.Contains(mousePos)) hoveredReroute = i; - } - if (Event.current.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) selectedReroutes = new List(selection); + if (Event.current.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) selectedReroutes = selection; } private void DrawNodes() { @@ -311,6 +299,13 @@ namespace XNodeEditor { List preSelection = preBoxSelection != null ? new List(preBoxSelection) : new List(); + // Selection box stuff + Vector2 boxStartPos = GridToWindowPositionNoClipped(dragBoxStart); + Vector2 boxSize = mousePos - boxStartPos; + if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } + if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); } + Rect selectionBox = new Rect(boxStartPos, boxSize); + //Save guiColor so we can revert it Color guiColor = GUI.color; for (int n = 0; n < graph.nodes.Count; n++) { @@ -380,12 +375,7 @@ namespace XNodeEditor { //If dragging a selection box, add nodes inside to selection if (currentActivity == NodeActivity.DragGrid) { - Vector2 startPos = GridToWindowPositionNoClipped(dragBoxStart); - Vector2 size = mousePos - startPos; - if (size.x < 0) { startPos.x += size.x; size.x = Mathf.Abs(size.x); } - if (size.y < 0) { startPos.y += size.y; size.y = Mathf.Abs(size.y); } - Rect r = new Rect(startPos, size); - if (windowRect.Overlaps(r)) preSelection.Add(node); + if (windowRect.Overlaps(selectionBox)) preSelection.Add(node); } //Check if we are hovering any of this nodes ports diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index f7d70d0..c2092e0 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -10,8 +10,6 @@ namespace XNode { /// All nodes in the graph. /// See: [SerializeField] public List nodes = new List(); - /// Nodes used primarily for organization - [SerializeField] public List reroutes = new List(); /// Add a node to the graph by type public T AddNode() where T : Node { diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 299559e..65ad4be 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -216,6 +216,14 @@ namespace XNode { return port; } + /// Get index of the connection connecting this and specified ports + public int GetConnectionIndex(NodePort port) { + for (int i = 0; i < ConnectionCount; i++) { + if (connections[i].Port == port) return i; + } + return -1; + } + public bool IsConnectedTo(NodePort port) { for (int i = 0; i < connections.Count; i++) { if (connections[i].Port == port) return true; @@ -250,10 +258,9 @@ namespace XNode { } } - /// Get reroute indices. This is used for graph organization purposes - /// Connection index - public int[] GetReroutes(int i) { - return connections[i].reroutes; + /// Get reroute points for a given connection. This is used for organization + public List GetReroutePoints(int index) { + return connections[index].reroutePoints; } /// Swap connected nodes from the old list with nodes from the new list @@ -269,10 +276,10 @@ namespace XNode { [SerializeField] public string fieldName; [SerializeField] public Node node; public NodePort Port { get { return port != null ? port : port = GetPort(); } } - /// Used for organization - [SerializeField] public int[] reroutes = new int[0]; [NonSerialized] private NodePort port; + /// Extra connection path points for organization + [SerializeField] public List reroutePoints = new List(); public PortConnection(NodePort port) { this.port = port;