diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 1030457..22a73f5 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using UnityEngine; using UnityEditor; +/// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. public class NodeEditor : Editor { - public override void OnInspectorGUI() { base.OnInspectorGUI(); diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index a6806a7..1668c2a 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -3,79 +3,111 @@ using System.Collections.Generic; using UnityEngine; using UnityEditor; using System; -using UNEC; -public static class NodeEditorAction { +/// User input related UNEC functionality +public partial class NodeEditorWindow { - public static bool dragging; + public static bool isPanning { get; private set; } public static Vector2 dragOffset; - public static void Controls(NodeEditorWindow window) { + private bool IsDraggingNode { get { return draggedNode != null; } } + private bool IsDraggingPort { get { return draggedOutput != null; } } + private bool IsHoveringPort { get { return hoveredPort != null; } } + private bool IsHoveringNode { get { return hoveredNode != null; } } + private bool HasSelectedNode { get { return selectedNode != null; } } + + private Node hoveredNode; + private Node selectedNode; + private Node draggedNode; + private NodePort hoveredPort; + private NodePort draggedOutput; + private NodePort draggedOutputTarget; + + private Rect nodeRects; + + public void Controls() { + wantsMouseMove = true; + Event e = Event.current; switch (e.type) { - + case EventType.MouseMove: + UpdateHovered(); + break; case EventType.ScrollWheel: - if (e.delta.y > 0) window.zoom += 0.1f * window.zoom; - else window.zoom -= 0.1f * window.zoom; + if (e.delta.y > 0) zoom += 0.1f * zoom; + else zoom -= 0.1f * zoom; break; case EventType.MouseDrag: + UpdateHovered(); if (e.button == 0) { - if (window.HasSelectedNode) { - //If we are currently dragging a connection, check if we are hovering any matching port to connect to - if (window.IsDraggingConnection) { - if (window.IsHoveringPort && window.IsHoveringNode && window.hoveredPort.IsInput) { - window.draggedConnection.outputNodeId = window.graph.GetNodeId(window.hoveredNode); - window.draggedConnection.inputPortId = window.hoveredNode.GetInputPortId(window.hoveredPort); - } else { - window.draggedConnection.outputNodeId = -1; - window.draggedConnection.inputPortId = -1; - } - } - //If we just started dragging from a port, grab connection - else if (window.IsHoveringPort) { - if (window.hoveredPort.direction == NodePort.IO.Output) { - dragging = true; - int inputNodeId = window.graph.GetNodeId(window.selectedNode); - int outputPortId = window.selectedNode.GetOutputPortId(window.hoveredPort); - window.draggedConnection = new NodeConnection(inputNodeId, outputPortId, -1, -1); - } - else { - Debug.Log("input"); - /*int outputNodeId = window.graph.GetNodeId(window.activeNode); - int outputPortId = window.activeNode.GetInputPortId(window.hoveredPort); - window.tempConnection = new NodeConnection(-1,-1,outputNodeId,outputPortId);*/ + if (IsDraggingPort) { + if (IsHoveringPort && hoveredPort.IsInput) { + if (!draggedOutput.IsConnectedTo(hoveredPort)) { + draggedOutputTarget = hoveredPort; } } else { - window.selectedNode.position.position = window.WindowToGridPosition(e.mousePosition) + dragOffset; - window.Repaint(); + draggedOutputTarget = null; } + Repaint(); } - } + else if (IsDraggingNode) { + draggedNode.position.position = WindowToGridPosition(e.mousePosition) + dragOffset; + Repaint(); + } + } else if (e.button == 1) { - window.panOffset += e.delta * window.zoom; - dragging = true; + panOffset += e.delta * zoom; + isPanning = true; } break; case EventType.KeyDown: - if (e.keyCode == KeyCode.F) Focus(window); + if (e.keyCode == KeyCode.F) Home(); break; case EventType.MouseDown: - dragging = false; - window.Repaint(); - window.SelectNode(window.hoveredNode); - if (window.hoveredNode != null) { - dragOffset = window.hoveredNode.position.position - window.WindowToGridPosition(e.mousePosition); + UpdateHovered(); + Repaint(); + SelectNode(hoveredNode); + if (IsHoveringPort) { + if (hoveredPort.IsOutput) { + draggedOutput = hoveredPort; + } + else { + if (hoveredPort.IsConnected) { + NodePort output = hoveredPort.Connection; + hoveredPort.Disconnect(output); + draggedOutput = output; + draggedOutputTarget = hoveredPort; + } + } + } + else if (IsHoveringNode) { + draggedNode = hoveredNode; + dragOffset = hoveredNode.position.position - WindowToGridPosition(e.mousePosition); } - window.Repaint(); break; case EventType.MouseUp: - window.draggedConnection.enabled = false; - if (dragging) return; - - if (e.button == 1) { - NodeEditorGUI.RightClickContextMenu(window); + if (e.button == 0) { + //Port drag release + if (IsDraggingPort) { + //If connection is valid, save it + if (draggedOutputTarget != null) { + draggedOutput.Connect(draggedOutputTarget); + } + //Release dragged connection + draggedOutput = null; + draggedOutputTarget = null; + Repaint(); + } + else if (IsDraggingNode) { + draggedNode = null; + } } + else if (e.button == 1) { + if (!isPanning) RightClickContextMenu(); + isPanning = false; + } + UpdateHovered(); break; case EventType.repaint: @@ -84,13 +116,63 @@ public static class NodeEditorAction { } /// Puts all nodes in focus. If no nodes are present, resets view to - public static void Focus(this NodeEditorWindow window) { - window.zoom = 2; - window.panOffset = Vector2.zero; + public void Home() { + zoom = 2; + panOffset = Vector2.zero; } - public static void CreateNode(this NodeEditorWindow window, Type type, Vector2 position) { - Node node = window.graph.AddNode(type); + public void CreateNode(Type type, Vector2 position) { + Node node = graph.AddNode(type); node.position.position = position; } + + /// Draw a connection as we are dragging it + public void DrawDraggedConnection() { + if (IsDraggingPort) { + Vector2 from = _portConnectionPoints[draggedOutput]; + Vector2 to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget] : WindowToGridPosition(Event.current.mousePosition); + DrawConnection(from, to); + } + } + + void UpdateHovered() { + Vector2 mousePos = Event.current.mousePosition; + Node newHoverNode = null; + foreach (Node node in graph.nodes) { + //Get node position + Vector2 nodePos = GridToWindowPosition(node.position.position); + Rect windowRect = new Rect(nodePos, new Vector2(node.position.size.x / zoom, node.position.size.y / zoom)); + if (windowRect.Contains(mousePos)) { + newHoverNode = node; + } + } + if (newHoverNode != hoveredNode) { + hoveredNode = newHoverNode; + Repaint(); + } + if (IsHoveringNode) { + NodePort newHoverPort = null; + for (int i = 0; i < hoveredNode.InputCount; i++) { + NodePort port = hoveredNode.GetInput(i); + if (!portRects.ContainsKey(port)) continue; + Rect r = portRects[port]; + r.position = GridToWindowPosition(r.position + hoveredNode.position.position); + r.size /= zoom; + if (r.Contains(mousePos)) newHoverPort = port; + } + for (int i = 0; i < hoveredNode.OutputCount; i++) { + NodePort port = hoveredNode.GetOutput(i); + if (!portRects.ContainsKey(port)) continue; + Rect r = portRects[port]; + r.position = GridToWindowPosition(r.position + hoveredNode.position.position); + r.size /= zoom; + if (r.Contains(mousePos)) newHoverPort = port; + } + if (newHoverPort != hoveredPort) { + hoveredPort = newHoverPort; + Repaint(); + } + } + else hoveredPort = null; + } } diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 472ff83..e3eb2a1 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -4,116 +4,136 @@ using UnityEngine; using UnityEditor; using System; -namespace UNEC { - /// Contains GUI methods - public static class NodeEditorGUI { +/// Contains GUI methods +public partial class NodeEditorWindow { - public static void DrawConnection(Vector2 from, Vector2 to, Color col) { - Handles.DrawBezier(from, to, from, to, col, new Texture2D(2, 2), 2); - } - - public static void BeginZoomed(Rect rect, float zoom) { - GUI.EndClip(); - - GUIUtility.ScaleAroundPivot(Vector2.one / zoom, rect.size * 0.5f); - Vector4 padding = new Vector4(0, 22, 0, 0); - padding *= zoom; - GUI.BeginClip(new Rect( - -((rect.width * zoom) - rect.width) * 0.5f, - -(((rect.height * zoom) - rect.height) * 0.5f) + (22 * zoom), - rect.width * zoom, - rect.height * zoom)); - } - - public static void EndZoomed(Rect rect, float zoom) { - GUIUtility.ScaleAroundPivot(Vector2.one * zoom, rect.size * 0.5f); - Vector3 offset = new Vector3( - (((rect.width * zoom) - rect.width) * 0.5f), - (((rect.height * zoom) - rect.height) * 0.5f) + (-22 * zoom)+22, - 0); - GUI.matrix = Matrix4x4.TRS(offset, Quaternion.identity, Vector3.one); - } - - public static void DrawGrid(Rect rect, float zoom, Vector2 panOffset) { - - rect.position = Vector2.zero; - - Vector2 center = rect.size / 2f; - Texture2D gridTex = NodeEditorResources.gridTexture; - Texture2D crossTex = NodeEditorResources.crossTexture; - - // Offset from origin in tile units - float xOffset = -(center.x * zoom + panOffset.x) / gridTex.width; - float yOffset = ((center.y - rect.size.y) * zoom + panOffset.y) / gridTex.height; - - Vector2 tileOffset = new Vector2(xOffset, yOffset); - - // Amount of tiles - float tileAmountX = Mathf.Round(rect.size.x * zoom) / gridTex.width; - float tileAmountY = Mathf.Round(rect.size.y * zoom) / gridTex.height; - - Vector2 tileAmount = new Vector2(tileAmountX, tileAmountY); - - // Draw tiled background - GUI.DrawTextureWithTexCoords(rect, gridTex, new Rect(tileOffset, tileAmount)); - GUI.DrawTextureWithTexCoords(rect, crossTex, new Rect(tileOffset + new Vector2(0.5f,0.5f), tileAmount)); - } - - public static void DrawToolbar(NodeEditorWindow window) { - EditorGUILayout.BeginHorizontal("Toolbar"); - - if (DropdownButton("File", 50)) FileContextMenu(); - if (DropdownButton("Edit", 50)) EditContextMenu(window); - if (DropdownButton("View", 50)) { } - if (DropdownButton("Settings", 70)) { } - if (DropdownButton("Tools", 50)) { } - - // Make the toolbar extend all throughout the window extension. - GUILayout.FlexibleSpace(); - - EditorGUILayout.EndHorizontal(); - } - - public static bool DropdownButton(string name, float width) { - return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width)); - } - - public static void RightClickContextMenu(NodeEditorWindow window) { - GenericMenu contextMenu = new GenericMenu(); - - if (window.hoveredNode != null) { - Node node = window.hoveredNode; - contextMenu.AddItem(new GUIContent("Remove"), false, () => window.graph.RemoveNode(node)); - } - else { - Vector2 pos = window.WindowToGridPosition(Event.current.mousePosition); - for (int i = 0; i < NodeEditorReflection.nodeTypes.Length; i++) { - Type type = NodeEditorReflection.nodeTypes[i]; - contextMenu.AddItem(new GUIContent(NodeEditorReflection.nodeTypes[i].ToString()), false, () => { - window.CreateNode(type, pos); - }); - } - } - contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); - } - - public static void FileContextMenu() { - GenericMenu contextMenu = new GenericMenu(); - contextMenu.AddItem(new GUIContent("Create New"), false, null); - contextMenu.AddItem(new GUIContent("Load"), false, null); - - contextMenu.AddSeparator(""); - contextMenu.AddItem(new GUIContent("Save"), false, null); - contextMenu.AddItem(new GUIContent("Save As"), false, null); - - contextMenu.DropDown(new Rect(5f, 17f, 0f, 0f)); - } - - public static void EditContextMenu(NodeEditorWindow window) { - GenericMenu contextMenu = new GenericMenu(); - contextMenu.AddItem(new GUIContent("Clear"), false, () => window.graph.Clear()); - - contextMenu.DropDown(new Rect(5f, 17f, 0f, 0f)); - } + public static void DrawConnection(Vector2 from, Vector2 to, Color col) { + Handles.DrawBezier(from, to, from, to, col, new Texture2D(2, 2), 2); } -} \ No newline at end of file + + public static void BeginZoomed(Rect rect, float zoom) { + GUI.EndClip(); + + GUIUtility.ScaleAroundPivot(Vector2.one / zoom, rect.size * 0.5f); + Vector4 padding = new Vector4(0, 22, 0, 0); + padding *= zoom; + GUI.BeginClip(new Rect( + -((rect.width * zoom) - rect.width) * 0.5f, + -(((rect.height * zoom) - rect.height) * 0.5f) + (22 * zoom), + rect.width * zoom, + rect.height * zoom)); + } + + public static void EndZoomed(Rect rect, float zoom) { + GUIUtility.ScaleAroundPivot(Vector2.one * zoom, rect.size * 0.5f); + Vector3 offset = new Vector3( + (((rect.width * zoom) - rect.width) * 0.5f), + (((rect.height * zoom) - rect.height) * 0.5f) + (-22 * zoom)+22, + 0); + GUI.matrix = Matrix4x4.TRS(offset, Quaternion.identity, Vector3.one); + } + + public static void DrawGrid(Rect rect, float zoom, Vector2 panOffset) { + + rect.position = Vector2.zero; + + Vector2 center = rect.size / 2f; + Texture2D gridTex = gridTexture; + Texture2D crossTex = crossTexture; + + // Offset from origin in tile units + float xOffset = -(center.x * zoom + panOffset.x) / gridTex.width; + float yOffset = ((center.y - rect.size.y) * zoom + panOffset.y) / gridTex.height; + + Vector2 tileOffset = new Vector2(xOffset, yOffset); + + // Amount of tiles + float tileAmountX = Mathf.Round(rect.size.x * zoom) / gridTex.width; + float tileAmountY = Mathf.Round(rect.size.y * zoom) / gridTex.height; + + Vector2 tileAmount = new Vector2(tileAmountX, tileAmountY); + + // Draw tiled background + GUI.DrawTextureWithTexCoords(rect, gridTex, new Rect(tileOffset, tileAmount)); + GUI.DrawTextureWithTexCoords(rect, crossTex, new Rect(tileOffset + new Vector2(0.5f,0.5f), tileAmount)); + } + + public void DrawToolbar() { + EditorGUILayout.BeginHorizontal("Toolbar"); + + if (DropdownButton("File", 50)) FileContextMenu(); + if (DropdownButton("Edit", 50)) EditContextMenu(); + if (DropdownButton("View", 50)) { } + if (DropdownButton("Settings", 70)) { } + if (DropdownButton("Tools", 50)) { } + if (IsHoveringNode) { + GUILayout.Space(20); + string hoverInfo = hoveredNode.GetType().ToString(); + if (IsHoveringPort) hoverInfo += " > "+hoveredPort.name; + GUILayout.Label(hoverInfo); + } + + // Make the toolbar extend all throughout the window extension. + GUILayout.FlexibleSpace(); + + EditorGUILayout.EndHorizontal(); + } + + public static bool DropdownButton(string name, float width) { + return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width)); + } + + public void RightClickContextMenu() { + GenericMenu contextMenu = new GenericMenu(); + + if (hoveredNode != null) { + Node node = hoveredNode; + contextMenu.AddItem(new GUIContent("Remove"), false, () => graph.RemoveNode(node)); + } + else { + Vector2 pos = WindowToGridPosition(Event.current.mousePosition); + for (int i = 0; i < nodeTypes.Length; i++) { + Type type = nodeTypes[i]; + contextMenu.AddItem(new GUIContent(nodeTypes[i].ToString()), false, () => { + CreateNode(type, pos); + }); + } + } + contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); + } + + public static void FileContextMenu() { + GenericMenu contextMenu = new GenericMenu(); + contextMenu.AddItem(new GUIContent("Create New"), false, null); + contextMenu.AddItem(new GUIContent("Load"), false, null); + + contextMenu.AddSeparator(""); + contextMenu.AddItem(new GUIContent("Save"), false, null); + contextMenu.AddItem(new GUIContent("Save As"), false, null); + + contextMenu.DropDown(new Rect(5f, 17f, 0f, 0f)); + } + + public void EditContextMenu() { + GenericMenu contextMenu = new GenericMenu(); + contextMenu.AddItem(new GUIContent("Clear"), false, () => graph.Clear()); + + contextMenu.DropDown(new Rect(5f, 17f, 0f, 0f)); + } + + public void DrawConnection(Vector2 startPoint, Vector2 endPoint) { + startPoint = GridToWindowPosition(startPoint); + endPoint = GridToWindowPosition(endPoint); + + Vector2 startTangent = startPoint; + if (startPoint.x < endPoint.x) startTangent.x = Mathf.LerpUnclamped(startPoint.x, endPoint.x, 0.7f); + else startTangent.x = Mathf.LerpUnclamped(startPoint.x, endPoint.x, -0.7f); + + Vector2 endTangent = endPoint; + if (startPoint.x > endPoint.x) endTangent.x = Mathf.LerpUnclamped(endPoint.x, startPoint.x, -0.7f); + else endTangent.x = Mathf.LerpUnclamped(endPoint.x, startPoint.x, 0.7f); + + Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, Color.gray, null, 4); + Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, Color.white, null, 2); + } +} diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs index 2902abd..1334bba 100644 --- a/Scripts/Editor/NodeEditorReflection.cs +++ b/Scripts/Editor/NodeEditorReflection.cs @@ -2,21 +2,19 @@ using System.Linq; using System; -namespace UNEC { - /// Contains reflection-related info - public static class NodeEditorReflection { +/// Contains reflection-related info +public partial class NodeEditorWindow { - public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } } - private static Type[] _nodeTypes; + public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } } + private static Type[] _nodeTypes; - public static Type[] GetNodeTypes() { - //Get all classes deriving from Node via reflection - Type derivedType = typeof(Node); - Assembly assembly = Assembly.GetAssembly(derivedType); - return assembly.GetTypes().Where(t => - t != derivedType && - derivedType.IsAssignableFrom(t) - ).ToArray(); - } + public static Type[] GetNodeTypes() { + //Get all classes deriving from Node via reflection + Type derivedType = typeof(Node); + Assembly assembly = Assembly.GetAssembly(derivedType); + return assembly.GetTypes().Where(t => + t != derivedType && + derivedType.IsAssignableFrom(t) + ).ToArray(); } } diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index ec262cf..4535aef 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -2,102 +2,101 @@ using UnityEditor; using System; -namespace UNEC { - public static class NodeEditorResources { +public partial class NodeEditorWindow { - public static Texture2D gridTexture { get { return _gridTexture != null ? _gridTexture : _gridTexture = GenerateGridTexture(); } } - private static Texture2D _gridTexture; - public static Texture2D crossTexture { get { return _crossTexture != null ? _crossTexture : _crossTexture = GenerateCrossTexture(); } } - private static Texture2D _crossTexture; + public static Texture2D gridTexture { get { return _gridTexture != null ? _gridTexture : _gridTexture = GenerateGridTexture(); } } + private static Texture2D _gridTexture; + public static Texture2D crossTexture { get { return _crossTexture != null ? _crossTexture : _crossTexture = GenerateCrossTexture(); } } + private static Texture2D _crossTexture; - private static Color backgroundColor = new Color(0.18f, 0.18f, 0.18f); - private static Color veinColor = new Color(0.25f, 0.25f, 0.25f); - private static Color arteryColor = new Color(0.34f, 0.34f, 0.34f); - private static Color crossColor = new Color(0.45f, 0.45f, 0.45f); + private static Color backgroundColor = new Color(0.18f, 0.18f, 0.18f); + private static Color veinColor = new Color(0.25f, 0.25f, 0.25f); + private static Color arteryColor = new Color(0.34f, 0.34f, 0.34f); + private static Color crossColor = new Color(0.45f, 0.45f, 0.45f); - public static Styles styles = new Styles(); + public static Styles styles { get { return _styles != null ? _styles : _styles = new Styles(); } } + public static Styles _styles = null; - public class Styles { - GUIStyle inputInt, inputString, inputFloat, inputObject, inputTexture, inputColor; - GUIStyle outputInt, outputString, outputFloat, outputObject, outputTexture, outputColor; + public class Styles { + GUIStyle inputInt, inputString, inputFloat, inputObject, inputTexture, inputColor; + GUIStyle outputInt, outputString, outputFloat, outputObject, outputTexture, outputColor; - public Styles() { - inputObject = new GUIStyle((GUIStyle)"flow shader in 0"); - inputString = new GUIStyle((GUIStyle)"flow shader in 1"); - inputInt = new GUIStyle((GUIStyle)"flow shader in 2"); - inputFloat = new GUIStyle((GUIStyle)"flow shader in 3"); - inputColor = new GUIStyle((GUIStyle)"flow shader in 4"); - inputTexture = new GUIStyle((GUIStyle)"flow shader in 5"); - outputObject = new GUIStyle((GUIStyle)"flow shader out 0"); - outputString = new GUIStyle((GUIStyle)"flow shader out 1"); - outputInt = new GUIStyle((GUIStyle)"flow shader out 2"); - outputFloat = new GUIStyle((GUIStyle)"flow shader out 3"); - outputColor = new GUIStyle((GUIStyle)"flow shader out 4"); - outputTexture = new GUIStyle((GUIStyle)"flow shader out 5"); + public Styles() { + inputObject = new GUIStyle((GUIStyle)"flow shader in 0"); + inputString = new GUIStyle((GUIStyle)"flow shader in 1"); + inputInt = new GUIStyle((GUIStyle)"flow shader in 2"); + inputFloat = new GUIStyle((GUIStyle)"flow shader in 3"); + inputColor = new GUIStyle((GUIStyle)"flow shader in 4"); + inputTexture = new GUIStyle((GUIStyle)"flow shader in 5"); + outputObject = new GUIStyle((GUIStyle)"flow shader out 0"); + outputString = new GUIStyle((GUIStyle)"flow shader out 1"); + outputInt = new GUIStyle((GUIStyle)"flow shader out 2"); + outputFloat = new GUIStyle((GUIStyle)"flow shader out 3"); + outputColor = new GUIStyle((GUIStyle)"flow shader out 4"); + outputTexture = new GUIStyle((GUIStyle)"flow shader out 5"); - foreach (GUIStyle style in new GUIStyle[] { inputInt, inputString, inputFloat, inputObject, inputTexture, inputColor, outputInt, outputString, outputFloat, outputObject, outputTexture, outputColor }) { - style.normal.textColor = Color.black; - style.fixedHeight = 18; - style.alignment = TextAnchor.MiddleLeft; - style.onHover.textColor = Color.red; - } - } - - public GUIStyle GetInputStyle(Type type) { - if (type == typeof(int)) return inputInt; - else if (type == typeof(string)) return inputString; - else if (type == typeof(Texture2D)) return inputTexture; - else if (type == typeof(float)) return inputFloat; - else if (type == typeof(Color)) return inputColor; - else return inputObject; - } - - public GUIStyle GetOutputStyle(Type type) { - if (type == typeof(int)) return outputInt; - else if (type == typeof(string)) return outputString; - else if (type == typeof(Texture2D)) return outputTexture; - else if (type == typeof(float)) return outputFloat; - else if (type == typeof(Color)) return outputColor; - else return outputObject; + foreach (GUIStyle style in new GUIStyle[] { inputInt, inputString, inputFloat, inputObject, inputTexture, inputColor, outputInt, outputString, outputFloat, outputObject, outputTexture, outputColor }) { + style.normal.textColor = Color.black; + style.fixedHeight = 18; + style.alignment = TextAnchor.MiddleLeft; + style.onHover.textColor = Color.red; } } - public static Texture2D GenerateGridTexture() { - Texture2D tex = new Texture2D(64,64); - Color[] cols = new Color[64 * 64]; - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 64; x++) { - Color col = backgroundColor; - if (y % 16 == 0 || x % 16 == 0) col = veinColor; - if (y == 63 || x == 63) col = arteryColor; - cols[(y * 64) + x] = col; - } - } - tex.SetPixels(cols); - tex.wrapMode = TextureWrapMode.Repeat; - tex.filterMode = FilterMode.Bilinear; - tex.name = "Grid"; - tex.Apply(); - return tex; + public GUIStyle GetInputStyle(Type type) { + if (type == typeof(int)) return inputInt; + else if (type == typeof(string)) return inputString; + else if (type == typeof(Texture2D)) return inputTexture; + else if (type == typeof(float)) return inputFloat; + else if (type == typeof(Color)) return inputColor; + else return inputObject; } - public static Texture2D GenerateCrossTexture() { - Texture2D tex = new Texture2D(64, 64); - Color[] cols = new Color[64 * 64]; - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 64; x++) { - Color col = crossColor; - if (y != 31 && x != 31) col.a = 0; - cols[(y * 64) + x] = col; - } - } - tex.SetPixels(cols); - tex.wrapMode = TextureWrapMode.Clamp; - tex.filterMode = FilterMode.Bilinear; - tex.name = "Grid"; - tex.Apply(); - return tex; + public GUIStyle GetOutputStyle(Type type) { + if (type == typeof(int)) return outputInt; + else if (type == typeof(string)) return outputString; + else if (type == typeof(Texture2D)) return outputTexture; + else if (type == typeof(float)) return outputFloat; + else if (type == typeof(Color)) return outputColor; + else return outputObject; } } -} \ No newline at end of file + + public static Texture2D GenerateGridTexture() { + Texture2D tex = new Texture2D(64,64); + Color[] cols = new Color[64 * 64]; + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 64; x++) { + Color col = backgroundColor; + if (y % 16 == 0 || x % 16 == 0) col = veinColor; + if (y == 63 || x == 63) col = arteryColor; + cols[(y * 64) + x] = col; + } + } + tex.SetPixels(cols); + tex.wrapMode = TextureWrapMode.Repeat; + tex.filterMode = FilterMode.Bilinear; + tex.name = "Grid"; + tex.Apply(); + return tex; + } + + public static Texture2D GenerateCrossTexture() { + Texture2D tex = new Texture2D(64, 64); + Color[] cols = new Color[64 * 64]; + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 64; x++) { + Color col = crossColor; + if (y != 31 && x != 31) col.a = 0; + cols[(y * 64) + x] = col; + } + } + tex.SetPixels(cols); + tex.wrapMode = TextureWrapMode.Clamp; + tex.filterMode = FilterMode.Bilinear; + tex.name = "Grid"; + tex.Apply(); + return tex; + } +} diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 7575862..9f29740 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -2,91 +2,70 @@ using System.Collections.Generic; using UnityEngine; using UnityEditor; -using UNEC; -public class NodeEditorWindow : EditorWindow { +[InitializeOnLoad] +public partial class NodeEditorWindow : EditorWindow { - private Dictionary portConnectionPoints = new Dictionary(); - public bool IsDraggingConnection { get { return draggedConnection.enabled; } } - public bool IsHoveringPort { get { return hoveredPort != null; } } - public bool IsHoveringNode { get { return hoveredNode != null; } } - public bool HasSelectedNode { get { return selectedNode != null; } } + public Dictionary portConnectionPoints { get { return _portConnectionPoints; } } + private Dictionary _portConnectionPoints = new Dictionary(); + private Dictionary portRects = new Dictionary(); public NodeGraph graph { get { return _graph != null ? _graph : _graph = new NodeGraph(); } } public NodeGraph _graph; - public Node hoveredNode; - /// Currently selected node - public Node selectedNode { get; private set; } - public NodePort hoveredPort; - public NodeConnection draggedConnection; - public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } } private Vector2 _panOffset; public float zoom { get { return _zoom; } set { _zoom = Mathf.Clamp(value, 1f, 5f); Repaint(); } } - private float _zoom = 1; + private float _zoom = 1; + partial void OnEnable(); [MenuItem("Window/UNEC")] static void Init() { NodeEditorWindow w = CreateInstance(); w.titleContent = new GUIContent("UNEC"); + w.wantsMouseMove = true; w.Show(); } private void OnGUI() { + Event e = Event.current; Matrix4x4 m = GUI.matrix; - NodeEditorAction.Controls(this); + Controls(); - NodeEditorGUI.DrawGrid(position, zoom, panOffset); + DrawGrid(position, zoom, panOffset); DrawNodes(); + DrawConnections(); DrawDraggedConnection(); - NodeEditorGUI.DrawToolbar(this); + if (e.type == EventType.Repaint || e.type == EventType.Layout) DrawToolbar(); GUI.matrix = m; } - /// Draw a connection as we are dragging it - private void DrawDraggedConnection() { - if (IsDraggingConnection) { - Node inputNode = graph.GetNode(draggedConnection.inputNodeId); - Node outputNode = graph.GetNode(draggedConnection.outputNodeId); - if (inputNode != null) { - NodePort outputPort = inputNode.GetOutput(draggedConnection.outputPortId); - Vector2 startPoint = GridToWindowPosition( portConnectionPoints[outputPort]); - Vector2 endPoint = Event.current.mousePosition; - if (outputNode != null) { - NodePort inputPort = outputNode.GetInput(draggedConnection.inputPortId); - if (inputPort != null) endPoint = GridToWindowPosition(portConnectionPoints[inputPort]); + public void DrawConnections() { + foreach(Node node in graph.nodes) { + for (int i = 0; i < node.OutputCount; i++) { + NodePort output = node.GetOutput(i); + Vector2 from = _portConnectionPoints[output]; + for (int k = 0; k < output.ConnectionCount; k++) { + NodePort input = output.GetConnection(k); + Vector2 to = _portConnectionPoints[input]; + DrawConnection(from, to); } - - Vector2 startTangent = startPoint; - startTangent.x = Mathf.Lerp(startPoint.x,endPoint.x,0.7f); - Vector2 endTangent = endPoint; - endTangent.x = Mathf.Lerp(endPoint.x, startPoint.x, 0.7f); - Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, Color.gray, null, 4); - Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, Color.white, null, 2); - Repaint(); } } } + private void DrawNodes() { portConnectionPoints.Clear(); Event e = Event.current; BeginWindows(); - NodeEditorGUI.BeginZoomed(position, zoom); - if (e.type == EventType.repaint) { - hoveredPort = null; - } - hoveredNode = null; - foreach (KeyValuePair kvp in graph.nodes) { - Node node = kvp.Value; - int id = kvp.Key; - + BeginZoomed(position, zoom); + if (e.type == EventType.Repaint) portRects.Clear(); + foreach (Node node in graph.nodes) { //Get node position Vector2 nodePos = GridToWindowPositionNoClipped(node.position.position); - Rect windowRect = new Rect(nodePos, new Vector2(200, 200)); - if (windowRect.Contains(e.mousePosition)) hoveredNode = node; + Rect windowRect = new Rect(nodePos, node.position.size); GUIStyle style = (node == selectedNode) ? (GUIStyle)"flow node 0 on" : (GUIStyle)"flow node 0"; GUILayout.BeginArea(windowRect, node.ToString(), style); @@ -96,11 +75,9 @@ public class NodeEditorWindow : EditorWindow { GUILayout.BeginVertical(); for (int i = 0; i < node.InputCount; i++) { NodePort input = node.GetInput(i); - Rect r = GUILayoutUtility.GetRect(new GUIContent(input.name), NodeEditorResources.styles.GetInputStyle(input.type)); - GUI.Label(r, input.name, NodeEditorResources.styles.GetInputStyle(input.type)); - if (e.type == EventType.repaint) { - if (r.Contains(e.mousePosition)) hoveredPort = input; - } + Rect r = GUILayoutUtility.GetRect(new GUIContent(input.name), styles.GetInputStyle(input.type)); + GUI.Label(r, input.name, styles.GetInputStyle(input.type)); + if (e.type == EventType.Repaint) portRects.Add(input, r); portConnectionPoints.Add(input, new Vector2(r.xMin, r.yMin + (r.height * 0.5f)) + node.position.position); } GUILayout.EndVertical(); @@ -109,11 +86,9 @@ public class NodeEditorWindow : EditorWindow { GUILayout.BeginVertical(); for (int i = 0; i < node.OutputCount; i++) { NodePort output = node.GetOutput(i); - Rect r = GUILayoutUtility.GetRect(new GUIContent(output.name), NodeEditorResources.styles.GetOutputStyle(output.type)); - GUI.Label(r, output.name, NodeEditorResources.styles.GetOutputStyle(output.type)); - if (e.type == EventType.repaint) { - if (r.Contains(e.mousePosition)) hoveredPort = output; - } + Rect r = GUILayoutUtility.GetRect(new GUIContent(output.name), styles.GetOutputStyle(output.type)); + GUI.Label(r, output.name, styles.GetOutputStyle(output.type)); + if (e.type == EventType.Repaint) portRects.Add(output, r); portConnectionPoints.Add(output, new Vector2(r.xMax, r.yMin + (r.height * 0.5f)) + node.position.position); } GUILayout.EndVertical(); @@ -132,7 +107,7 @@ public class NodeEditorWindow : EditorWindow { } } - NodeEditorGUI.EndZoomed(position, zoom); + EndZoomed(position, zoom); EndWindows(); } diff --git a/Scripts/Node.cs b/Scripts/Node.cs index ec3612c..24f6eab 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using UnityEngine; using System; -using UNEC; /// Base class for all nodes public abstract class Node { @@ -19,14 +18,14 @@ public abstract class Node { abstract protected void Init(); - public int GetInputPortId(NodePort input) { + public int GetInputId(NodePort input) { for (int i = 0; i < inputs.Length; i++) { if (input == inputs[i]) return i; } return -1; } - public int GetOutputPortId(NodePort output) { + public int GetOutputId(NodePort output) { for (int i = 0; i < outputs.Length; i++) { if (output == outputs[i]) return i; @@ -43,9 +42,18 @@ public abstract class Node { } public NodePort CreateNodeInput(string name, Type type, bool enabled = true) { - return new NodePort(name, type, this, enabled); + return new NodePort(name, type, this, enabled, NodePort.IO.Input); } public NodePort CreateNodeOutput(string name, Type type, bool enabled = true) { - return new NodePort(name, type, this, enabled); + return new NodePort(name, type, this, enabled, NodePort.IO.Output); + } + + public void ClearConnections() { + for (int i = 0; i < inputs.Length; i++) { + inputs[i].ClearConnections(); + } + for (int i = 0; i < outputs.Length; i++) { + outputs[i].ClearConnections(); + } } } diff --git a/Scripts/NodeConnection.cs b/Scripts/NodeConnection.cs deleted file mode 100644 index 5a87258..0000000 --- a/Scripts/NodeConnection.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace UNEC { - /// Data travels from Input Node's Output port to Output Node's Input port - public struct NodeConnection { - public bool enabled; - /// Data travels from Input Node's Output port to Output Node's Input port - public int inputNodeId, inputPortId, outputNodeId, outputPortId; - - /// Data travels from Input Node's Output port to Output Node's Input port - public NodeConnection(int inputNodeId, int outputPortId, int outputNodeId, int inputPortId) { - this.inputNodeId = inputNodeId; - this.outputPortId = outputPortId; - this.outputNodeId = outputNodeId; - this.inputPortId = inputPortId; - enabled = true; - } - } -} \ No newline at end of file diff --git a/Scripts/NodeConnection.cs.meta b/Scripts/NodeConnection.cs.meta deleted file mode 100644 index 819c7d8..0000000 --- a/Scripts/NodeConnection.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 1f29a204e0cc2934e8f9cb2010723de9 -timeCreated: 1505747662 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index 1a4df9b..b07b1ba 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -2,67 +2,38 @@ using System.Collections.Generic; using UnityEngine; using System; -using UNEC; /// Base class for all node graphs public class NodeGraph { - public Dictionary nodes = new Dictionary(); - private List connections = new List(); + /// All nodes in the graph. + /// See: + public List nodes = new List(); public T AddNode() where T : Node { T node = default(T); - nodes.Add(GetUniqueID(), node); + nodes.Add(node); return node; } public Node AddNode(Type type) { Node node = (Node)Activator.CreateInstance(type); - if (node == null) return null; - nodes.Add(GetUniqueID(), node); + if (node == null) { + Debug.LogError("Node could node be instanced"); + return null; + } + nodes.Add(node); return node; } + /// Safely remove a node and all its connections + /// public void RemoveNode(Node node) { - int id = GetNodeId(node); - if (id != -1) nodes.Remove(id); - else Debug.LogWarning("Node " + node.ToString() + " is not part of NodeGraph"); - } - - public void RemoveNode(int nodeId) { - nodes.Remove(nodeId); - } - - public int GetNodeId(Node node) { - foreach(var kvp in nodes) { - if (kvp.Value == node) return kvp.Key; - } - return -1; - } - - public Node GetNode(int nodeId) { - if (nodes.ContainsKey(nodeId)) return nodes[nodeId]; - return null; - } - - public void AddConnection(NodePort input, NodePort output) { - int inputNodeId = GetNodeId(input.node); - int outputPortId = input.node.GetInputPortId(output); - - int outputNodeId = GetNodeId(output.node); - int inputPortId = output.node.GetInputPortId(input); - - NodeConnection connection = new NodeConnection(inputNodeId, inputPortId, outputNodeId, outputPortId); - } - - private int GetUniqueID() { - int id = 0; - while (nodes.ContainsKey(id)) id++; - return id; + node.ClearConnections(); + nodes.Remove(node); } public void Clear() { nodes.Clear(); - connections.Clear(); } } diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 093c3bf..df2967a 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -4,46 +4,65 @@ using UnityEngine; using System; public class NodePort { - public enum IO { None, Input, Output} + public enum IO { Input, Output} - public IO direction { - get { - for (int i = 0; i < node.InputCount; i++) { - if (node.GetInput(i) == this) return IO.Input; - } - for (int i = 0; i < node.OutputCount; i++) { - if (node.GetOutput(i) == this) return IO.Output; - } - return IO.None; - } - } + public int ConnectionCount { get { return connections.Count; } } + /// Return the first connection + public NodePort Connection { get { return connections.Count > 0 ? connections[0] : null; } } + /// Returns a copy of the connections list + public List Connections { get { return new List(connections); } } + public IO direction { get { return _direction; } } + /// Is this port connected to anytihng? + public bool IsConnected { get { return connections.Count != 0; } } public bool IsInput { get { return direction == IO.Input; } } public bool IsOutput { get { return direction == IO.Output; } } + public Type type { get { return _type; } } public Node node { get; private set; } public string name { get { return _name; } set { _name = value; } } - [SerializeField] - private string _name; - public Type type { get; private set; } - [SerializeField] - private string _type; public bool enabled { get { return _enabled; } set { _enabled = value; } } - [SerializeField] - private bool _enabled; - public NodePort(string name, Type type, Node node, bool enabled) { + private Type _type; + private List connections = new List(); + + [SerializeField] private string _name; + [SerializeField] private bool _enabled; + [SerializeField] private IO _direction; + + public NodePort(string name, Type type, Node node, bool enabled, IO direction) { _name = name; _enabled = enabled; - this.type = type; - _type = type.FullName; + _type = type; this.node = node; + _direction = direction; } - - public NodePort GetConnection() { - return null; + + public void Connect(NodePort port) { + if (port == this) { Debug.LogWarning("Attempting to connect port to self."); return; } + if (connections.Contains(port)) { Debug.LogWarning("Port already connected."); return; } + if (direction == port.direction) { Debug.LogWarning("Cannot connect two " + (direction == IO.Input ? "input" : "output") + " connections"); return; } + connections.Add(port); + port.connections.Add(this); } - public NodePort[] GetConnections() { - return null; + + public NodePort GetConnection(int i) { + return connections[i]; + } + + public bool IsConnectedTo(NodePort port) { + return connections.Contains(port); + } + + public void Disconnect(NodePort port) { + connections.Remove(port); + port.connections.Remove(this); + } + + public void ClearConnections() { + for (int i = 0; i < connections.Count; i++) { + connections[i].connections.Remove(this); + } + connections.Clear(); } }