diff --git a/Example/ExampleNodeGraph.cs b/Example/ExampleNodeGraph.cs
index 8b6f750..8b09ece 100644
--- a/Example/ExampleNodeGraph.cs
+++ b/Example/ExampleNodeGraph.cs
@@ -1,7 +1,7 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
+using UnityEngine;
using System;
+using XNode;
+
/// Defines an example nodegraph.
[Serializable, CreateAssetMenu(fileName = "ExampleNodeGraph", menuName = "Node Graph/Example")]
public class ExampleNodeGraph : NodeGraph {
diff --git a/Example/Nodes/DisplayValue.cs b/Example/Nodes/DisplayValue.cs
index 4e22d9c..d36b4d4 100644
--- a/Example/Nodes/DisplayValue.cs
+++ b/Example/Nodes/DisplayValue.cs
@@ -1,4 +1,6 @@
-namespace BasicNodes {
+using XNode;
+
+namespace BasicNodes {
public class DisplayValue : Node {
[Input(ShowBackingValue.Never)] public object value;
diff --git a/Example/Nodes/Editor/DisplayValueEditor.cs b/Example/Nodes/Editor/DisplayValueEditor.cs
index 0cc9c67..8e0b985 100644
--- a/Example/Nodes/Editor/DisplayValueEditor.cs
+++ b/Example/Nodes/Editor/DisplayValueEditor.cs
@@ -1,7 +1,5 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEditor;
-using UnityEngine;
+using UnityEditor;
+using XNodeEditor;
namespace BasicNodes {
[CustomNodeEditor(typeof(DisplayValue))]
diff --git a/Example/Nodes/MathNode.cs b/Example/Nodes/MathNode.cs
index bfa9630..25576b5 100644
--- a/Example/Nodes/MathNode.cs
+++ b/Example/Nodes/MathNode.cs
@@ -1,4 +1,6 @@
-namespace BasicNodes {
+using XNode;
+
+namespace BasicNodes {
[System.Serializable]
public class MathNode : Node {
// Adding [Input] or [Output] is all you need to do to register a field as a valid port on your node
diff --git a/Example/Nodes/Vector.cs b/Example/Nodes/Vector.cs
index 3d8b0a8..b866283 100644
--- a/Example/Nodes/Vector.cs
+++ b/Example/Nodes/Vector.cs
@@ -1,4 +1,5 @@
using UnityEngine;
+using XNode;
namespace BasicNodes {
public class Vector : Node {
diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs
index 10138c0..d5e7c0c 100644
--- a/Scripts/Editor/NodeEditor.cs
+++ b/Scripts/Editor/NodeEditor.cs
@@ -1,59 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
using UnityEditor;
using UnityEngine;
+using XNode;
-/// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes.
-public class NodeEditor {
- /// Fires every whenever a node was modified through the editor
- public static Action onUpdateNode;
- public Node target;
- public SerializedObject serializedObject;
- public static Dictionary portPositions;
+namespace XNodeEditor {
+ /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes.
+ public class NodeEditor {
+ /// Fires every whenever a node was modified through the editor
+ public static Action onUpdateNode;
+ public Node target;
+ public SerializedObject serializedObject;
+ public static Dictionary portPositions;
- /// Draws the node GUI.
- /// Port handle positions need to be returned to the NodeEditorWindow
- public void OnNodeGUI() {
- OnHeaderGUI();
- OnBodyGUI();
- }
+ /// Draws the node GUI.
+ /// Port handle positions need to be returned to the NodeEditorWindow
+ public void OnNodeGUI() {
+ OnHeaderGUI();
+ OnBodyGUI();
+ }
- public void OnHeaderGUI() {
- GUI.color = Color.white;
- string title = NodeEditorUtilities.PrettifyCamelCase(target.name);
- GUILayout.Label(title, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
- }
+ public void OnHeaderGUI() {
+ GUI.color = Color.white;
+ string title = NodeEditorUtilities.PrettifyCamelCase(target.name);
+ GUILayout.Label(title, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
+ }
- /// Draws standard field editors for all public fields
- public virtual void OnBodyGUI() {
- string[] excludes = { "m_Script", "graph", "position", "ports" };
- portPositions = new Dictionary();
+ /// Draws standard field editors for all public fields
+ public virtual void OnBodyGUI() {
+ string[] excludes = { "m_Script", "graph", "position", "ports" };
+ portPositions = new Dictionary();
- SerializedProperty iterator = serializedObject.GetIterator();
- bool enterChildren = true;
- EditorGUIUtility.labelWidth = 84;
- while (iterator.NextVisible(enterChildren)) {
- enterChildren = false;
- if (excludes.Contains(iterator.name)) continue;
- NodeEditorGUILayout.PropertyField(iterator, true);
+ SerializedProperty iterator = serializedObject.GetIterator();
+ bool enterChildren = true;
+ EditorGUIUtility.labelWidth = 84;
+ while (iterator.NextVisible(enterChildren)) {
+ enterChildren = false;
+ if (excludes.Contains(iterator.name)) continue;
+ NodeEditorGUILayout.PropertyField(iterator, true);
+ }
+ }
+
+ public virtual int GetWidth() {
+ return 200;
}
}
- public virtual int GetWidth() {
- return 200;
- }
-}
-
-[AttributeUsage(AttributeTargets.Class)]
-public class CustomNodeEditorAttribute : Attribute {
- public Type inspectedType { get { return _inspectedType; } }
- private Type _inspectedType;
- /// Tells a NodeEditor which Node type it is an editor for
- /// Type that this editor can edit
- /// Path to the node
- public CustomNodeEditorAttribute(Type inspectedType) {
- _inspectedType = inspectedType;
+ [AttributeUsage(AttributeTargets.Class)]
+ public class CustomNodeEditorAttribute : Attribute {
+ public Type inspectedType { get { return _inspectedType; } }
+ private Type _inspectedType;
+ /// Tells a NodeEditor which Node type it is an editor for
+ /// Type that this editor can edit
+ /// Path to the node
+ public CustomNodeEditorAttribute(Type inspectedType) {
+ _inspectedType = inspectedType;
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs
index 8db4d10..14d19e0 100644
--- a/Scripts/Editor/NodeEditorAction.cs
+++ b/Scripts/Editor/NodeEditorAction.cs
@@ -1,145 +1,146 @@
using System;
-using System.Collections;
-using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
+using XNode;
-public partial class NodeEditorWindow {
+namespace XNodeEditor {
+ public partial class NodeEditorWindow {
- public static bool isPanning { get; private set; }
- public static Vector2 dragOffset;
+ public static bool isPanning { get; private set; }
+ public static Vector2 dragOffset;
- 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 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 = null;
+ private Node hoveredNode = null;
- [NonSerialized] private Node selectedNode = null;
- [NonSerialized] private Node draggedNode = null;
- [NonSerialized] private NodePort hoveredPort = null;
- [NonSerialized] private NodePort draggedOutput = null;
- [NonSerialized] private NodePort draggedOutputTarget = null;
+ [NonSerialized] private Node selectedNode = null;
+ [NonSerialized] private Node draggedNode = null;
+ [NonSerialized] private NodePort hoveredPort = null;
+ [NonSerialized] private NodePort draggedOutput = null;
+ [NonSerialized] private NodePort draggedOutputTarget = null;
- private Rect nodeRects;
+ private Rect nodeRects;
- public void Controls() {
- wantsMouseMove = true;
+ public void Controls() {
+ wantsMouseMove = true;
- Event e = Event.current;
- switch (e.type) {
- case EventType.MouseMove:
- break;
- case EventType.ScrollWheel:
- if (e.delta.y > 0) zoom += 0.1f * zoom;
- else zoom -= 0.1f * zoom;
- break;
- case EventType.MouseDrag:
- if (e.button == 0) {
- if (IsDraggingPort) {
- if (IsHoveringPort && hoveredPort.IsInput) {
- if (!draggedOutput.IsConnectedTo(hoveredPort)) {
- draggedOutputTarget = hoveredPort;
+ Event e = Event.current;
+ switch (e.type) {
+ case EventType.MouseMove:
+ break;
+ case EventType.ScrollWheel:
+ if (e.delta.y > 0) zoom += 0.1f * zoom;
+ else zoom -= 0.1f * zoom;
+ break;
+ case EventType.MouseDrag:
+ if (e.button == 0) {
+ if (IsDraggingPort) {
+ if (IsHoveringPort && hoveredPort.IsInput) {
+ if (!draggedOutput.IsConnectedTo(hoveredPort)) {
+ draggedOutputTarget = hoveredPort;
+ }
+ } else {
+ draggedOutputTarget = null;
}
+ Repaint();
+ } else if (IsDraggingNode) {
+ draggedNode.position = WindowToGridPosition(e.mousePosition) + dragOffset;
+ Repaint();
+ }
+ } else if (e.button == 1) {
+ panOffset += e.delta * zoom;
+ isPanning = true;
+ }
+ break;
+ case EventType.KeyDown:
+ if (e.keyCode == KeyCode.F) Home();
+ break;
+ case EventType.MouseDown:
+ Repaint();
+ SelectNode(hoveredNode);
+ if (IsHoveringPort) {
+ if (hoveredPort.IsOutput) {
+ draggedOutput = hoveredPort;
} else {
+ hoveredPort.VerifyConnections();
+ if (hoveredPort.IsConnected) {
+ Node node = hoveredPort.node;
+ NodePort output = hoveredPort.Connection;
+ hoveredPort.Disconnect(output);
+ draggedOutput = output;
+ draggedOutputTarget = hoveredPort;
+ NodeEditor.onUpdateNode(node);
+ }
+ }
+ } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
+ draggedNode = hoveredNode;
+ dragOffset = hoveredNode.position - WindowToGridPosition(e.mousePosition);
+ }
+ break;
+ case EventType.MouseUp:
+ if (e.button == 0) {
+ //Port drag release
+ if (IsDraggingPort) {
+ //If connection is valid, save it
+ if (draggedOutputTarget != null) {
+ Node node = draggedOutputTarget.node;
+ if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
+ NodeEditor.onUpdateNode(node);
+ EditorUtility.SetDirty(graph);
+ }
+ //Release dragged connection
+ draggedOutput = null;
draggedOutputTarget = null;
- }
- Repaint();
- } else if (IsDraggingNode) {
- draggedNode.position = WindowToGridPosition(e.mousePosition) + dragOffset;
- Repaint();
- }
- } else if (e.button == 1) {
- panOffset += e.delta * zoom;
- isPanning = true;
- }
- break;
- case EventType.KeyDown:
- if (e.keyCode == KeyCode.F) Home();
- break;
- case EventType.MouseDown:
- Repaint();
- SelectNode(hoveredNode);
- if (IsHoveringPort) {
- if (hoveredPort.IsOutput) {
- draggedOutput = hoveredPort;
- } else {
- hoveredPort.VerifyConnections();
- if (hoveredPort.IsConnected) {
- Node node = hoveredPort.node;
- NodePort output = hoveredPort.Connection;
- hoveredPort.Disconnect(output);
- draggedOutput = output;
- draggedOutputTarget = hoveredPort;
- NodeEditor.onUpdateNode(node);
- }
- }
- } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
- draggedNode = hoveredNode;
- dragOffset = hoveredNode.position - WindowToGridPosition(e.mousePosition);
- }
- break;
- case EventType.MouseUp:
- if (e.button == 0) {
- //Port drag release
- if (IsDraggingPort) {
- //If connection is valid, save it
- if (draggedOutputTarget != null) {
- Node node = draggedOutputTarget.node;
- if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
- NodeEditor.onUpdateNode(node);
EditorUtility.SetDirty(graph);
+ Repaint();
+ } else if (IsDraggingNode) {
+ draggedNode = null;
}
- //Release dragged connection
- draggedOutput = null;
- draggedOutputTarget = null;
- EditorUtility.SetDirty(graph);
- Repaint();
- } else if (IsDraggingNode) {
- draggedNode = null;
+ } else if (e.button == 1) {
+ if (!isPanning) ShowContextMenu();
+ isPanning = false;
}
- } else if (e.button == 1) {
- if (!isPanning) ShowContextMenu();
- isPanning = false;
- }
- AssetDatabase.SaveAssets();
- break;
+ AssetDatabase.SaveAssets();
+ break;
+ }
}
- }
- /// Puts all nodes in focus. If no nodes are present, resets view to
- public void Home() {
- zoom = 2;
- panOffset = Vector2.zero;
- }
-
- public void CreateNode(Type type, Vector2 position) {
- Node node = graph.AddNode(type);
- node.position = position;
- Repaint();
- }
-
- /// 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);
- col.a = 0.6f;
- DrawConnection(from, to, col);
+ /// Puts all nodes in focus. If no nodes are present, resets view to
+ public void Home() {
+ zoom = 2;
+ panOffset = Vector2.zero;
}
- }
- bool IsHoveringTitle(Node node) {
- Vector2 mousePos = Event.current.mousePosition;
- //Get node position
- Vector2 nodePos = GridToWindowPosition(node.position);
- float width = 200;
- if (nodeWidths.ContainsKey(node)) width = nodeWidths[node];
- Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));
- return windowRect.Contains(mousePos);
+ public void CreateNode(Type type, Vector2 position) {
+ Node node = graph.AddNode(type);
+ node.position = position;
+ Repaint();
+ }
+
+ /// 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);
+ col.a = 0.6f;
+ DrawConnection(from, to, col);
+ }
+ }
+
+ bool IsHoveringTitle(Node node) {
+ Vector2 mousePos = Event.current.mousePosition;
+ //Get node position
+ Vector2 nodePos = GridToWindowPosition(node.position);
+ float width = 200;
+ if (nodeWidths.ContainsKey(node)) width = nodeWidths[node];
+ Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));
+ return windowRect.Contains(mousePos);
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorAssetModProcessor.cs b/Scripts/Editor/NodeEditorAssetModProcessor.cs
index a1756f5..aa50f02 100644
--- a/Scripts/Editor/NodeEditorAssetModProcessor.cs
+++ b/Scripts/Editor/NodeEditorAssetModProcessor.cs
@@ -1,35 +1,36 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEditor;
+using UnityEditor;
using UnityEngine;
+using XNode;
-public class NodeEditorAssetModProcessor : UnityEditor.AssetModificationProcessor {
- public static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options) {
- UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(path);
+namespace XNodeEditor {
+ public class NodeEditorAssetModProcessor : UnityEditor.AssetModificationProcessor {
+ public static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options) {
+ UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(path);
- if (!(obj is UnityEditor.MonoScript)) return AssetDeleteResult.DidNotDelete;
+ if (!(obj is UnityEditor.MonoScript)) return AssetDeleteResult.DidNotDelete;
- UnityEditor.MonoScript script = obj as UnityEditor.MonoScript;
- System.Type scriptType = script.GetClass();
+ UnityEditor.MonoScript script = obj as UnityEditor.MonoScript;
+ System.Type scriptType = script.GetClass();
- if (scriptType != typeof(Node) && !scriptType.IsSubclassOf(typeof(Node))) return AssetDeleteResult.DidNotDelete;
+ if (scriptType != typeof(Node) && !scriptType.IsSubclassOf(typeof(Node))) return AssetDeleteResult.DidNotDelete;
- //Find ScriptableObjects using this script
- string[] guids = AssetDatabase.FindAssets("t:" + scriptType);
- for (int i = 0; i < guids.Length; i++) {
- string assetpath = AssetDatabase.GUIDToAssetPath(guids[i]);
- Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath);
- for (int k = 0; k < objs.Length; k++) {
- Node node = objs[k] as Node;
- if (node.GetType() == scriptType) {
- if (node != null && node.graph != null) {
- Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
- node.graph.RemoveNode(node);
+ //Find ScriptableObjects using this script
+ string[] guids = AssetDatabase.FindAssets("t:" + scriptType);
+ for (int i = 0; i < guids.Length; i++) {
+ string assetpath = AssetDatabase.GUIDToAssetPath(guids[i]);
+ Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath);
+ for (int k = 0; k < objs.Length; k++) {
+ Node node = objs[k] as Node;
+ if (node.GetType() == scriptType) {
+ if (node != null && node.graph != null) {
+ Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
+ node.graph.RemoveNode(node);
+ }
}
}
- }
+ }
+ return AssetDeleteResult.DidNotDelete;
}
- return AssetDeleteResult.DidNotDelete;
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs
index 6032fe2..5523abd 100644
--- a/Scripts/Editor/NodeEditorGUI.cs
+++ b/Scripts/Editor/NodeEditorGUI.cs
@@ -1,286 +1,288 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
+using XNode;
-/// Contains GUI methods
-public partial class NodeEditorWindow {
+namespace XNodeEditor {
+ /// Contains GUI methods
+ public partial class NodeEditorWindow {
- private void OnGUI() {
- Event e = Event.current;
- Matrix4x4 m = GUI.matrix;
- Controls();
+ private void OnGUI() {
+ Event e = Event.current;
+ Matrix4x4 m = GUI.matrix;
+ Controls();
- DrawGrid(position, zoom, panOffset);
- DrawConnections();
- DrawDraggedConnection();
- DrawNodes();
- DrawTooltip();
+ DrawGrid(position, zoom, panOffset);
+ DrawConnections();
+ DrawDraggedConnection();
+ DrawNodes();
+ DrawTooltip();
- GUI.matrix = m;
- }
-
- 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 bool DropdownButton(string name, float width) {
- return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width));
- }
-
- /// Show right-click context menu
- public void ShowContextMenu() {
- GenericMenu contextMenu = new GenericMenu();
- Vector2 pos = WindowToGridPosition(Event.current.mousePosition);
-
- if (hoveredNode != null) {
- Node node = hoveredNode;
- contextMenu.AddItem(new GUIContent("Remove"), false, () => graph.RemoveNode(node));
- } else {
- for (int i = 0; i < nodeTypes.Length; i++) {
- Type type = nodeTypes[i];
-
- string name = nodeTypes[i].ToString().Replace('.', '/');
- Node.CreateNodeMenuAttribute attrib;
- if (NodeEditorUtilities.GetAttrib(type, out attrib)) {
- name = attrib.menuName;
- }
- contextMenu.AddItem(new GUIContent(name), false, () => {
- CreateNode(type, pos);
- });
- }
+ GUI.matrix = m;
}
- contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
- }
- /// Draw a bezier from startpoint to endpoint, both in grid coordinates
- public void DrawConnection(Vector2 startPoint, Vector2 endPoint, Color col) {
- startPoint = GridToWindowPosition(startPoint);
- endPoint = GridToWindowPosition(endPoint);
+ public static void BeginZoomed(Rect rect, float zoom) {
+ GUI.EndClip();
- 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);
+ 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));
+ }
- 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);
+ 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);
+ }
- Handles.DrawBezier(startPoint, endPoint, startTangent, endTangent, col, null, 4);
- }
+ public static void DrawGrid(Rect rect, float zoom, Vector2 panOffset) {
- /// Draws all connections
- public void DrawConnections() {
- foreach (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;
+ rect.position = Vector2.zero;
- foreach (NodePort output in node.Outputs) {
- //Needs cleanup. Null checks are ugly
- if (!portConnectionPoints.ContainsKey(output)) continue;
- Vector2 from = _portConnectionPoints[output].center;
- for (int k = 0; k < output.ConnectionCount; k++) {
+ Vector2 center = rect.size / 2f;
+ Texture2D gridTex = NodeEditorResources.gridTexture;
+ Texture2D crossTex = NodeEditorResources.crossTexture;
- NodePort input = output.GetConnection(k);
- if (input == null) continue; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return.
- if (!input.IsConnectedTo(output)) input.Connect(output);
- if (!_portConnectionPoints.ContainsKey(input)) continue;
- Vector2 to = _portConnectionPoints[input].center;
- DrawConnection(from, to, NodeEditorPreferences.GetTypeColor(output.ValueType));
+ // 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 bool DropdownButton(string name, float width) {
+ return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width));
+ }
+
+ /// Show right-click context menu
+ public void ShowContextMenu() {
+ GenericMenu contextMenu = new GenericMenu();
+ Vector2 pos = WindowToGridPosition(Event.current.mousePosition);
+
+ if (hoveredNode != null) {
+ Node node = hoveredNode;
+ contextMenu.AddItem(new GUIContent("Remove"), false, () => graph.RemoveNode(node));
+ } else {
+ for (int i = 0; i < nodeTypes.Length; i++) {
+ Type type = nodeTypes[i];
+
+ string name = nodeTypes[i].ToString().Replace('.', '/');
+ Node.CreateNodeMenuAttribute attrib;
+ if (NodeEditorUtilities.GetAttrib(type, out attrib)) {
+ name = attrib.menuName;
+ }
+ contextMenu.AddItem(new GUIContent(name), false, () => {
+ CreateNode(type, pos);
+ });
+ }
+ }
+ contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
+ }
+
+ /// Draw a bezier from startpoint to endpoint, both in grid coordinates
+ public void DrawConnection(Vector2 startPoint, Vector2 endPoint, Color col) {
+ 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, col, null, 4);
+ }
+
+ /// Draws all connections
+ public void DrawConnections() {
+ foreach (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;
+
+ foreach (NodePort output in node.Outputs) {
+ //Needs cleanup. Null checks are ugly
+ if (!portConnectionPoints.ContainsKey(output)) continue;
+ Vector2 from = _portConnectionPoints[output].center;
+ for (int k = 0; k < output.ConnectionCount; k++) {
+
+ NodePort input = output.GetConnection(k);
+ if (input == null) continue; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return.
+ if (!input.IsConnectedTo(output)) input.Connect(output);
+ if (!_portConnectionPoints.ContainsKey(input)) continue;
+ Vector2 to = _portConnectionPoints[input].center;
+ DrawConnection(from, to, NodeEditorPreferences.GetTypeColor(output.ValueType));
+ }
}
}
}
- }
-
- private void DrawNodes() {
- Event e = Event.current;
- if (e.type == EventType.Repaint) {
- portConnectionPoints.Clear();
- nodeWidths.Clear();
- }
-
- //Selected node is hashed before and after node GUI to detect changes
- int nodeHash = 0;
- System.Reflection.MethodInfo onValidate = null;
- if (selectedNode != null) {
- onValidate = selectedNode.GetType().GetMethod("OnValidate");
- if (onValidate != null) nodeHash = selectedNode.GetHashCode();
- }
-
- BeginZoomed(position, zoom);
-
- Vector2 mousePos = Event.current.mousePosition;
-
- if (e.type != EventType.Layout) {
- hoveredNode = null;
- hoveredPort = null;
- }
-
- for (int n = 0; n < graph.nodes.Count; n++) {
- while (graph.nodes[n] == null) graph.nodes.RemoveAt(n);
- if (n >= graph.nodes.Count) return;
- Node node = graph.nodes[n];
-
- NodeEditor nodeEditor = GetNodeEditor(node.GetType());
- nodeEditor.target = node;
- nodeEditor.serializedObject = new SerializedObject(node);
- NodeEditor.portPositions = new Dictionary();
-
- //Get node position
- Vector2 nodePos = GridToWindowPositionNoClipped(node.position);
-
- GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000)));
-
- GUIStyle style = NodeEditorResources.styles.nodeBody;
- GUILayout.BeginVertical(new GUIStyle(style));
- EditorGUI.BeginChangeCheck();
-
- //Draw node contents
- nodeEditor.OnNodeGUI();
-
- //Apply
- nodeEditor.serializedObject.ApplyModifiedProperties();
-
- //If user changed a value, notify other scripts through onUpdateNode
- if (EditorGUI.EndChangeCheck()) {
- if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
- }
+ private void DrawNodes() {
+ Event e = Event.current;
if (e.type == EventType.Repaint) {
- nodeWidths.Add(node, nodeEditor.GetWidth());
-
- foreach (var kvp in NodeEditor.portPositions) {
- Vector2 portHandlePos = kvp.Value;
- portHandlePos += node.position;
- Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16);
- portConnectionPoints.Add(kvp.Key, rect);
- }
+ portConnectionPoints.Clear();
+ nodeWidths.Clear();
}
- GUILayout.EndVertical();
+ //Selected node is hashed before and after node GUI to detect changes
+ int nodeHash = 0;
+ System.Reflection.MethodInfo onValidate = null;
+ if (selectedNode != null) {
+ onValidate = selectedNode.GetType().GetMethod("OnValidate");
+ if (onValidate != null) nodeHash = selectedNode.GetHashCode();
+ }
+
+ BeginZoomed(position, zoom);
+
+ Vector2 mousePos = Event.current.mousePosition;
if (e.type != EventType.Layout) {
- //Check if we are hovering this node
- Vector2 nodeSize = GUILayoutUtility.GetLastRect().size;
- Rect windowRect = new Rect(nodePos, nodeSize);
- if (windowRect.Contains(mousePos)) hoveredNode = node;
+ hoveredNode = null;
+ hoveredPort = null;
+ }
- //Check if we are hovering any of this nodes ports
- //Check input ports
- foreach (NodePort input in node.Inputs) {
- //Check if port rect is available
- if (!portConnectionPoints.ContainsKey(input)) continue;
- Rect r = GridToWindowRect(portConnectionPoints[input]);
- if (r.Contains(mousePos)) hoveredPort = input;
+ for (int n = 0; n < graph.nodes.Count; n++) {
+ while (graph.nodes[n] == null) graph.nodes.RemoveAt(n);
+ if (n >= graph.nodes.Count) return;
+ Node node = graph.nodes[n];
+
+ NodeEditor nodeEditor = GetNodeEditor(node.GetType());
+ nodeEditor.target = node;
+ nodeEditor.serializedObject = new SerializedObject(node);
+ NodeEditor.portPositions = new Dictionary();
+
+ //Get node position
+ Vector2 nodePos = GridToWindowPositionNoClipped(node.position);
+
+ GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000)));
+
+ GUIStyle style = NodeEditorResources.styles.nodeBody;
+ GUILayout.BeginVertical(new GUIStyle(style));
+ EditorGUI.BeginChangeCheck();
+
+ //Draw node contents
+ nodeEditor.OnNodeGUI();
+
+ //Apply
+ nodeEditor.serializedObject.ApplyModifiedProperties();
+
+ //If user changed a value, notify other scripts through onUpdateNode
+ if (EditorGUI.EndChangeCheck()) {
+ if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
}
- //Check all output ports
- foreach (NodePort output in node.Outputs) {
- //Check if port rect is available
- if (!portConnectionPoints.ContainsKey(output)) continue;
- Rect r = GridToWindowRect(portConnectionPoints[output]);
- if (r.Contains(mousePos)) hoveredPort = output;
+
+ if (e.type == EventType.Repaint) {
+ nodeWidths.Add(node, nodeEditor.GetWidth());
+
+ foreach (var kvp in NodeEditor.portPositions) {
+ Vector2 portHandlePos = kvp.Value;
+ portHandlePos += node.position;
+ Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16);
+ portConnectionPoints.Add(kvp.Key, rect);
+ }
}
+
+ GUILayout.EndVertical();
+
+ if (e.type != EventType.Layout) {
+ //Check if we are hovering this node
+ Vector2 nodeSize = GUILayoutUtility.GetLastRect().size;
+ Rect windowRect = new Rect(nodePos, nodeSize);
+ if (windowRect.Contains(mousePos)) hoveredNode = node;
+
+ //Check if we are hovering any of this nodes ports
+ //Check input ports
+ foreach (NodePort input in node.Inputs) {
+ //Check if port rect is available
+ if (!portConnectionPoints.ContainsKey(input)) continue;
+ Rect r = GridToWindowRect(portConnectionPoints[input]);
+ if (r.Contains(mousePos)) hoveredPort = input;
+ }
+ //Check all output ports
+ foreach (NodePort output in node.Outputs) {
+ //Check if port rect is available
+ if (!portConnectionPoints.ContainsKey(output)) continue;
+ Rect r = GridToWindowRect(portConnectionPoints[output]);
+ if (r.Contains(mousePos)) hoveredPort = output;
+ }
+ }
+
+ GUILayout.EndArea();
}
- GUILayout.EndArea();
+ EndZoomed(position, zoom);
+
+ //If a change in hash is detected in the selected node, call OnValidate method.
+ //This is done through reflection because OnValidate is only relevant in editor,
+ //and thus, the code should not be included in build.
+ if (selectedNode != null) {
+ if (onValidate != null && nodeHash != selectedNode.GetHashCode()) onValidate.Invoke(selectedNode, null);
+ }
}
- EndZoomed(position, zoom);
-
- //If a change in hash is detected in the selected node, call OnValidate method.
- //This is done through reflection because OnValidate is only relevant in editor,
- //and thus, the code should not be included in build.
- if (selectedNode != null) {
- if (onValidate != null && nodeHash != selectedNode.GetHashCode()) onValidate.Invoke(selectedNode, null);
+ private void DrawTooltip() {
+ if (hoveredPort != null) {
+ Type type = hoveredPort.ValueType;
+ GUIContent content = new GUIContent();
+ content.text = TypeToString(type);
+ Vector2 size = NodeEditorResources.styles.tooltip.CalcSize(content);
+ Rect rect = new Rect(Event.current.mousePosition - (size), size);
+ EditorGUI.LabelField(rect, content, NodeEditorResources.styles.tooltip);
+ Repaint();
+ }
}
- }
- private void DrawTooltip() {
- if (hoveredPort != null) {
- Type type = hoveredPort.ValueType;
- GUIContent content = new GUIContent();
- content.text = TypeToString(type);
- Vector2 size = NodeEditorResources.styles.tooltip.CalcSize(content);
- Rect rect = new Rect(Event.current.mousePosition - (size), size);
- EditorGUI.LabelField(rect, content, NodeEditorResources.styles.tooltip);
- Repaint();
+ private string TypeToString(Type type) {
+ if (type == null) return "null";
+ if (type == typeof(float)) return "float";
+ else if (type == typeof(int)) return "int";
+ else if (type == typeof(long)) return "long";
+ else if (type == typeof(double)) return "double";
+ else if (type == typeof(string)) return "string";
+ else if (type == typeof(bool)) return "bool";
+ else if (type.IsGenericType) {
+ string s = "";
+ Type genericType = type.GetGenericTypeDefinition();
+ if (genericType == typeof(List<>)) s = "List";
+ else s = type.GetGenericTypeDefinition().ToString();
+
+ Type[] types = type.GetGenericArguments();
+ string[] stypes = new string[types.Length];
+ for (int i = 0; i < types.Length; i++) {
+ stypes[i] = TypeToString(types[i]);
+ }
+ return s + "<" + string.Join(", ", stypes) + ">";
+ } else if (type.IsArray) {
+ string rank = "";
+ for (int i = 1; i < type.GetArrayRank(); i++) {
+ rank += ",";
+ }
+ Type elementType = type.GetElementType();
+ if (!elementType.IsArray) return TypeToString(elementType) + "[" + rank + "]";
+ else {
+ string s = TypeToString(elementType);
+ int i = s.IndexOf('[');
+ return s.Substring(0, i) + "[" + rank + "]" + s.Substring(i);
+ }
+ } else return hoveredPort.ValueType.ToString();
}
}
-
- private string TypeToString(Type type) {
- if (type == null) return "null";
- if (type == typeof(float)) return "float";
- else if (type == typeof(int)) return "int";
- else if (type == typeof(long)) return "long";
- else if (type == typeof(double)) return "double";
- else if (type == typeof(string)) return "string";
- else if (type == typeof(bool)) return "bool";
- else if (type.IsGenericType) {
- string s = "";
- Type genericType = type.GetGenericTypeDefinition();
- if (genericType == typeof(List<>)) s = "List";
- else s = type.GetGenericTypeDefinition().ToString();
-
- Type[] types = type.GetGenericArguments();
- string[] stypes = new string[types.Length];
- for (int i = 0; i < types.Length; i++) {
- stypes[i] = TypeToString(types[i]);
- }
- return s + "<" + string.Join(", ", stypes) + ">";
- } else if (type.IsArray) {
- string rank = "";
- for (int i = 1; i < type.GetArrayRank(); i++) {
- rank += ",";
- }
- Type elementType = type.GetElementType();
- if (!elementType.IsArray) return TypeToString(elementType) + "[" + rank + "]";
- else {
- string s = TypeToString(elementType);
- int i = s.IndexOf('[');
- return s.Substring(0, i) + "[" + rank + "]" + s.Substring(i);
- }
- } else return hoveredPort.ValueType.ToString();
- }
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs
index 8040942..37f0f75 100644
--- a/Scripts/Editor/NodeEditorGUILayout.cs
+++ b/Scripts/Editor/NodeEditorGUILayout.cs
@@ -1,39 +1,55 @@
using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
using UnityEditor;
using UnityEngine;
+using XNode;
-/// UNEC-specific version of
-public static class NodeEditorGUILayout {
+namespace XNodeEditor {
+ /// UNEC-specific version of
+ public static class NodeEditorGUILayout {
- public static void PropertyField(SerializedProperty property, bool includeChildren = true) {
- if (property == null) throw new NullReferenceException();
- Node node = property.serializedObject.targetObject as Node;
- NodePort port = node.GetPort(property.name);
+ public static void PropertyField(SerializedProperty property, bool includeChildren = true) {
+ if (property == null) throw new NullReferenceException();
+ Node node = property.serializedObject.targetObject as Node;
+ NodePort port = node.GetPort(property.name);
- // If property is not a port, display a regular property field
- if (port == null) EditorGUILayout.PropertyField(property, includeChildren, GUILayout.MinWidth(30));
- else {
- Rect rect = new Rect();
+ // If property is not a port, display a regular property field
+ if (port == null) EditorGUILayout.PropertyField(property, includeChildren, GUILayout.MinWidth(30));
+ else {
+ Rect rect = new Rect();
- // If property is an input, display a regular property field and put a port handle on the left side
- if (port.direction == NodePort.IO.Input) {
- // Display a label if port is connected
- if (port.IsConnected) EditorGUILayout.LabelField(property.displayName);
- // Display an editable property field if port is not connected
- else EditorGUILayout.PropertyField(property, includeChildren, GUILayout.MinWidth(30));
- rect = GUILayoutUtility.GetLastRect();
- rect.position = rect.position - new Vector2(16, 0);
- // If property is an output, display a text label and put a port handle on the right side
- } else if (port.direction == NodePort.IO.Output) {
- EditorGUILayout.LabelField(property.displayName, NodeEditorResources.styles.outputPort, GUILayout.MinWidth(30));
- rect = GUILayoutUtility.GetLastRect();
- rect.position = rect.position + new Vector2(rect.width, 0);
+ // If property is an input, display a regular property field and put a port handle on the left side
+ if (port.direction == NodePort.IO.Input) {
+ // Display a label if port is connected
+ if (port.IsConnected) EditorGUILayout.LabelField(property.displayName);
+ // Display an editable property field if port is not connected
+ else EditorGUILayout.PropertyField(property, includeChildren, GUILayout.MinWidth(30));
+ rect = GUILayoutUtility.GetLastRect();
+ rect.position = rect.position - new Vector2(16, 0);
+ // If property is an output, display a text label and put a port handle on the right side
+ } else if (port.direction == NodePort.IO.Output) {
+ EditorGUILayout.LabelField(property.displayName, NodeEditorResources.styles.outputPort, GUILayout.MinWidth(30));
+ rect = GUILayoutUtility.GetLastRect();
+ rect.position = rect.position + new Vector2(rect.width, 0);
+ }
+
+ rect.size = new Vector2(16, 16);
+
+ DrawPortHandle(rect, port.ValueType);
+
+ // Register the handle position
+ Vector2 portPos = rect.center;
+ if (NodeEditor.portPositions.ContainsKey(port)) NodeEditor.portPositions[port] = portPos;
+ else NodeEditor.portPositions.Add(port, portPos);
}
+ }
+ public static void PortField(NodePort port) {
+ if (port == null) return;
+ EditorGUILayout.LabelField(port.fieldName.PrettifyCamelCase(), GUILayout.MinWidth(30));
+
+ Rect rect = GUILayoutUtility.GetLastRect();
+ if (port.direction == NodePort.IO.Input) rect.position = rect.position - new Vector2(16, 0);
+ else if (port.direction == NodePort.IO.Output) rect.position = rect.position + new Vector2(rect.width, 0);
rect.size = new Vector2(16, 16);
DrawPortHandle(rect, port.ValueType);
@@ -43,31 +59,14 @@ public static class NodeEditorGUILayout {
if (NodeEditor.portPositions.ContainsKey(port)) NodeEditor.portPositions[port] = portPos;
else NodeEditor.portPositions.Add(port, portPos);
}
- }
- public static void PortField(NodePort port) {
- if (port == null) return;
- EditorGUILayout.LabelField(port.fieldName.PrettifyCamelCase(), GUILayout.MinWidth(30));
-
- Rect rect = GUILayoutUtility.GetLastRect();
- if (port.direction == NodePort.IO.Input) rect.position = rect.position - new Vector2(16, 0);
- else if (port.direction == NodePort.IO.Output) rect.position = rect.position + new Vector2(rect.width, 0);
- rect.size = new Vector2(16, 16);
-
- DrawPortHandle(rect, port.ValueType);
-
- // Register the handle position
- Vector2 portPos = rect.center;
- if (NodeEditor.portPositions.ContainsKey(port)) NodeEditor.portPositions[port] = portPos;
- else NodeEditor.portPositions.Add(port, portPos);
- }
-
- private static void DrawPortHandle(Rect rect, Type type) {
- Color col = GUI.color;
- GUI.color = new Color32(90, 97, 105, 255);
- GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
- GUI.color = NodeEditorPreferences.GetTypeColor(type);
- GUI.DrawTexture(rect, NodeEditorResources.dot);
- GUI.color = col;
+ private static void DrawPortHandle(Rect rect, Type type) {
+ Color col = GUI.color;
+ GUI.color = new Color32(90, 97, 105, 255);
+ GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
+ GUI.color = NodeEditorPreferences.GetTypeColor(type);
+ GUI.DrawTexture(rect, NodeEditorResources.dot);
+ GUI.color = col;
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs
index 9d392b3..80412fc 100644
--- a/Scripts/Editor/NodeEditorPreferences.cs
+++ b/Scripts/Editor/NodeEditorPreferences.cs
@@ -1,98 +1,99 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
-public static class NodeEditorPreferences {
+namespace XNodeEditor {
+ public static class NodeEditorPreferences {
- /// Have we loaded the prefs yet
- private static bool prefsLoaded = false;
+ /// Have we loaded the prefs yet
+ private static bool prefsLoaded = false;
- private static Dictionary typeColors;
- private static Dictionary generatedTypeColors;
+ private static Dictionary typeColors;
+ private static Dictionary generatedTypeColors;
- [PreferenceItem("Node Editor")]
- private static void PreferencesGUI() {
- if (!prefsLoaded) LoadPrefs();
- EditorGUILayout.LabelField("Type colors", EditorStyles.boldLabel);
+ [PreferenceItem("Node Editor")]
+ private static void PreferencesGUI() {
+ if (!prefsLoaded) LoadPrefs();
+ EditorGUILayout.LabelField("Type colors", EditorStyles.boldLabel);
- string[] typeKeys = new string[typeColors.Count];
- typeColors.Keys.CopyTo(typeKeys, 0);
+ string[] typeKeys = new string[typeColors.Count];
+ typeColors.Keys.CopyTo(typeKeys, 0);
- foreach (var key in typeKeys) {
- EditorGUILayout.BeginHorizontal();
- Color col = typeColors[key];
- col = EditorGUILayout.ColorField(key, col);
- typeColors[key] = col;
- if (!GUILayout.Toggle(true, "")) {
- typeColors.Remove(key);
+ foreach (var key in typeKeys) {
+ EditorGUILayout.BeginHorizontal();
+ Color col = typeColors[key];
+ col = EditorGUILayout.ColorField(key, col);
+ typeColors[key] = col;
+ if (!GUILayout.Toggle(true, "")) {
+ typeColors.Remove(key);
+ SavePrefs();
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+ if (GUI.changed) {
SavePrefs();
}
- EditorGUILayout.EndHorizontal();
- }
- if (GUI.changed) {
- SavePrefs();
- }
- string[] generatedTypeKeys = new string[generatedTypeColors.Count];
- generatedTypeColors.Keys.CopyTo(generatedTypeKeys, 0);
- foreach (var key in generatedTypeKeys) {
- EditorGUILayout.BeginHorizontal();
- Color col = generatedTypeColors[key];
- EditorGUI.BeginDisabledGroup(true);
- col = EditorGUILayout.ColorField(key, col);
- EditorGUI.EndDisabledGroup();
- if (GUILayout.Toggle(false, "")) {
- typeColors.Add(key, generatedTypeColors[key]);
- generatedTypeColors.Remove(key);
- SavePrefs();
- }
- EditorGUILayout.EndHorizontal();
- }
- }
-
- private static void LoadPrefs() {
- generatedTypeColors = new Dictionary();
- typeColors = GetTypeColors();
- prefsLoaded = true;
- }
-
- private static void SavePrefs() {
- if (!prefsLoaded) return;
- string s = "";
- foreach (var item in typeColors) {
- s += item.Key + "," + ColorUtility.ToHtmlStringRGB(item.Value) + ",";
- }
- EditorPrefs.SetString("unec_typecolors", s);
- }
-
- public static void SetDefaultTypeColors() {
- EditorPrefs.SetString("unec_typecolors", "int,2568CA,string,CE743A,bool,00FF00");
- }
-
- public static Dictionary GetTypeColors() {
- if (prefsLoaded) return typeColors;
- if (!EditorPrefs.HasKey("unec_typecolors")) SetDefaultTypeColors();
- string[] data = EditorPrefs.GetString("unec_typecolors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- Dictionary dict = new Dictionary();
- for (int i = 0; i < data.Length; i += 2) {
- Color col;
- if (ColorUtility.TryParseHtmlString("#" + data[i + 1], out col)) {
- dict.Add(data[i], col);
+ string[] generatedTypeKeys = new string[generatedTypeColors.Count];
+ generatedTypeColors.Keys.CopyTo(generatedTypeKeys, 0);
+ foreach (var key in generatedTypeKeys) {
+ EditorGUILayout.BeginHorizontal();
+ Color col = generatedTypeColors[key];
+ EditorGUI.BeginDisabledGroup(true);
+ col = EditorGUILayout.ColorField(key, col);
+ EditorGUI.EndDisabledGroup();
+ if (GUILayout.Toggle(false, "")) {
+ typeColors.Add(key, generatedTypeColors[key]);
+ generatedTypeColors.Remove(key);
+ SavePrefs();
+ }
+ EditorGUILayout.EndHorizontal();
}
}
- return dict;
- }
- /// Return color based on type
- public static Color GetTypeColor(System.Type type) {
- if (!prefsLoaded) LoadPrefs();
- if (type == null) return Color.gray;
- if (typeColors.ContainsKey(type.Name)) return typeColors[type.Name];
- if (generatedTypeColors.ContainsKey(type.Name)) return generatedTypeColors[type.Name];
- UnityEngine.Random.InitState(type.Name.GetHashCode());
- generatedTypeColors.Add(type.Name, new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value));
- return generatedTypeColors[type.Name];
+ private static void LoadPrefs() {
+ generatedTypeColors = new Dictionary();
+ typeColors = GetTypeColors();
+ prefsLoaded = true;
+ }
+
+ private static void SavePrefs() {
+ if (!prefsLoaded) return;
+ string s = "";
+ foreach (var item in typeColors) {
+ s += item.Key + "," + ColorUtility.ToHtmlStringRGB(item.Value) + ",";
+ }
+ EditorPrefs.SetString("unec_typecolors", s);
+ }
+
+ public static void SetDefaultTypeColors() {
+ EditorPrefs.SetString("unec_typecolors", "int,2568CA,string,CE743A,bool,00FF00");
+ }
+
+ public static Dictionary GetTypeColors() {
+ if (prefsLoaded) return typeColors;
+ if (!EditorPrefs.HasKey("unec_typecolors")) SetDefaultTypeColors();
+ string[] data = EditorPrefs.GetString("unec_typecolors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ Dictionary dict = new Dictionary();
+ for (int i = 0; i < data.Length; i += 2) {
+ Color col;
+ if (ColorUtility.TryParseHtmlString("#" + data[i + 1], out col)) {
+ dict.Add(data[i], col);
+ }
+ }
+ return dict;
+ }
+
+ /// Return color based on type
+ public static Color GetTypeColor(System.Type type) {
+ if (!prefsLoaded) LoadPrefs();
+ if (type == null) return Color.gray;
+ if (typeColors.ContainsKey(type.Name)) return typeColors[type.Name];
+ if (generatedTypeColors.ContainsKey(type.Name)) return generatedTypeColors[type.Name];
+ UnityEngine.Random.InitState(type.Name.GetHashCode());
+ generatedTypeColors.Add(type.Name, new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value));
+ return generatedTypeColors[type.Name];
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs
index e6a18e6..316ef94 100644
--- a/Scripts/Editor/NodeEditorReflection.cs
+++ b/Scripts/Editor/NodeEditorReflection.cs
@@ -2,49 +2,51 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using UnityEngine;
+using XNode;
-/// Contains reflection-related info
-public partial class NodeEditorWindow {
- [NonSerialized] private static Dictionary customNodeEditor;
- public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
- [NonSerialized] private static Type[] _nodeTypes = null;
+namespace XNodeEditor {
+ /// Contains reflection-related info
+ public partial class NodeEditorWindow {
+ [NonSerialized] private static Dictionary customNodeEditor;
+ public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
+ [NonSerialized] private static Type[] _nodeTypes = null;
- public static NodeEditor GetNodeEditor(Type node) {
- if (customNodeEditor == null) CacheCustomNodeEditors();
- if (customNodeEditor.ContainsKey(node)) return customNodeEditor[node];
- return customNodeEditor[typeof(Node)];
- }
+ public static NodeEditor GetNodeEditor(Type node) {
+ if (customNodeEditor == null) CacheCustomNodeEditors();
+ if (customNodeEditor.ContainsKey(node)) return customNodeEditor[node];
+ return customNodeEditor[typeof(Node)];
+ }
- public static Type[] GetNodeTypes() {
- //Get all classes deriving from Node via reflection
- return GetDerivedTypes(typeof(Node));
- }
+ public static Type[] GetNodeTypes() {
+ //Get all classes deriving from Node via reflection
+ return GetDerivedTypes(typeof(Node));
+ }
- public static void CacheCustomNodeEditors() {
- customNodeEditor = new Dictionary();
- customNodeEditor.Add(typeof(Node), new NodeEditor());
- //Get all classes deriving from NodeEditor via reflection
- Type[] nodeEditors = GetDerivedTypes(typeof(NodeEditor));
- for (int i = 0; i < nodeEditors.Length; i++) {
- var attribs = nodeEditors[i].GetCustomAttributes(typeof(CustomNodeEditorAttribute), false);
- if (attribs == null || attribs.Length == 0) continue;
- if (nodeEditors[i].IsAbstract) continue;
- CustomNodeEditorAttribute attrib = attribs[0] as CustomNodeEditorAttribute;
- customNodeEditor.Add(attrib.inspectedType, Activator.CreateInstance(nodeEditors[i]) as NodeEditor);
+ public static void CacheCustomNodeEditors() {
+ customNodeEditor = new Dictionary();
+ customNodeEditor.Add(typeof(Node), new NodeEditor());
+ //Get all classes deriving from NodeEditor via reflection
+ Type[] nodeEditors = GetDerivedTypes(typeof(NodeEditor));
+ for (int i = 0; i < nodeEditors.Length; i++) {
+ var attribs = nodeEditors[i].GetCustomAttributes(typeof(CustomNodeEditorAttribute), false);
+ if (attribs == null || attribs.Length == 0) continue;
+ if (nodeEditors[i].IsAbstract) continue;
+ CustomNodeEditorAttribute attrib = attribs[0] as CustomNodeEditorAttribute;
+ customNodeEditor.Add(attrib.inspectedType, Activator.CreateInstance(nodeEditors[i]) as NodeEditor);
+ }
+ }
+
+ public static Type[] GetDerivedTypes(Type baseType) {
+ //Get all classes deriving from baseType via reflection
+ Assembly assembly = Assembly.GetAssembly(baseType);
+ return assembly.GetTypes().Where(t =>
+ !t.IsAbstract &&
+ baseType.IsAssignableFrom(t)
+ ).ToArray();
+ }
+
+ public static object ObjectFromType(Type type) {
+ return Activator.CreateInstance(type);
}
}
-
- public static Type[] GetDerivedTypes(Type baseType) {
- //Get all classes deriving from baseType via reflection
- Assembly assembly = Assembly.GetAssembly(baseType);
- return assembly.GetTypes().Where(t =>
- !t.IsAbstract &&
- baseType.IsAssignableFrom(t)
- ).ToArray();
- }
-
- public static object ObjectFromType(Type type) {
- return Activator.CreateInstance(type);
- }
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs
index cc9320e..1e1b004 100644
--- a/Scripts/Editor/NodeEditorResources.cs
+++ b/Scripts/Editor/NodeEditorResources.cs
@@ -1,94 +1,94 @@
-using System;
-using UnityEditor;
-using UnityEngine;
+using UnityEngine;
-public static class NodeEditorResources {
- //Unec textures
- 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 dot { get { return _dot != null ? _dot : _dot = Resources.Load("unec_dot"); } }
- private static Texture2D _dot;
- public static Texture2D dotOuter { get { return _dotOuter != null ? _dotOuter : _dotOuter = Resources.Load("unec_dot_outer"); } }
- private static Texture2D _dotOuter;
- public static Texture2D nodeBody { get { return _nodeBody != null ? _nodeBody : _nodeBody = Resources.Load("unec_node"); } }
- private static Texture2D _nodeBody;
+namespace XNodeEditor {
+ public static class NodeEditorResources {
+ //Unec textures
+ 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 dot { get { return _dot != null ? _dot : _dot = Resources.Load("unec_dot"); } }
+ private static Texture2D _dot;
+ public static Texture2D dotOuter { get { return _dotOuter != null ? _dotOuter : _dotOuter = Resources.Load("unec_dot_outer"); } }
+ private static Texture2D _dotOuter;
+ public static Texture2D nodeBody { get { return _nodeBody != null ? _nodeBody : _nodeBody = Resources.Load("unec_node"); } }
+ private static Texture2D _nodeBody;
- //Grid colors
- 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);
+ //Grid colors
+ 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);
- //Unec styles
- public static Styles styles { get { return _styles != null ? _styles : _styles = new Styles(); } }
- public static Styles _styles = null;
+ //Unec styles
+ public static Styles styles { get { return _styles != null ? _styles : _styles = new Styles(); } }
+ public static Styles _styles = null;
- public class Styles {
- public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip;
+ public class Styles {
+ public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip;
- public Styles() {
- GUIStyle baseStyle = new GUIStyle("Label");
- baseStyle.fixedHeight = 18;
+ public Styles() {
+ GUIStyle baseStyle = new GUIStyle("Label");
+ baseStyle.fixedHeight = 18;
- inputPort = new GUIStyle(baseStyle);
- inputPort.alignment = TextAnchor.UpperLeft;
- inputPort.padding.left = 10;
+ inputPort = new GUIStyle(baseStyle);
+ inputPort.alignment = TextAnchor.UpperLeft;
+ inputPort.padding.left = 10;
- outputPort = new GUIStyle(baseStyle);
- outputPort.alignment = TextAnchor.UpperRight;
- outputPort.padding.right = 10;
+ outputPort = new GUIStyle(baseStyle);
+ outputPort.alignment = TextAnchor.UpperRight;
+ outputPort.padding.right = 10;
- nodeHeader = new GUIStyle();
- nodeHeader.alignment = TextAnchor.MiddleCenter;
- nodeHeader.fontStyle = FontStyle.Bold;
- nodeHeader.normal.textColor = Color.white;
+ nodeHeader = new GUIStyle();
+ nodeHeader.alignment = TextAnchor.MiddleCenter;
+ nodeHeader.fontStyle = FontStyle.Bold;
+ nodeHeader.normal.textColor = Color.white;
- nodeBody = new GUIStyle();
- nodeBody.normal.background = NodeEditorResources.nodeBody;
- nodeBody.border = new RectOffset(32, 32, 32, 32);
- nodeBody.padding = new RectOffset(16, 16, 4, 16);
+ nodeBody = new GUIStyle();
+ nodeBody.normal.background = NodeEditorResources.nodeBody;
+ nodeBody.border = new RectOffset(32, 32, 32, 32);
+ nodeBody.padding = new RectOffset(16, 16, 4, 16);
- tooltip = new GUIStyle("helpBox");
- tooltip.alignment = TextAnchor.MiddleCenter;
- }
- }
-
- 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;
+ tooltip = new GUIStyle("helpBox");
+ tooltip.alignment = TextAnchor.MiddleCenter;
}
}
- 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;
+ 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;
}
- tex.SetPixels(cols);
- tex.wrapMode = TextureWrapMode.Clamp;
- tex.filterMode = FilterMode.Bilinear;
- tex.name = "Grid";
- tex.Apply();
- return tex;
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs
index 755f87f..12e641a 100644
--- a/Scripts/Editor/NodeEditorUtilities.cs
+++ b/Scripts/Editor/NodeEditorUtilities.cs
@@ -1,54 +1,53 @@
using System;
-using System.Collections;
-using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using UnityEngine;
-/// A set of editor-only utilities and extensions for UnityNodeEditorBase
-public static class NodeEditorUtilities {
+namespace XNodeEditor {
+ /// A set of editor-only utilities and extensions for UnityNodeEditorBase
+ public static class NodeEditorUtilities {
- public static bool GetAttrib(Type classType, out T attribOut) where T : Attribute {
- object[] attribs = classType.GetCustomAttributes(typeof(T), false);
- return GetAttrib(attribs, out attribOut);
- }
-
- public static bool GetAttrib(object[] attribs, out T attribOut) where T : Attribute {
- for (int i = 0; i < attribs.Length; i++) {
- if (attribs[i].GetType() == typeof(T)) {
- attribOut = attribs[i] as T;
- return true;
- }
+ public static bool GetAttrib(Type classType, out T attribOut) where T : Attribute {
+ object[] attribs = classType.GetCustomAttributes(typeof(T), false);
+ return GetAttrib(attribs, out attribOut);
}
- attribOut = null;
- return false;
- }
- public static bool HasAttrib(object[] attribs) where T : Attribute {
- for (int i = 0; i < attribs.Length; i++) {
- if (attribs[i].GetType() == typeof(T)) {
- return true;
+ public static bool GetAttrib(object[] attribs, out T attribOut) where T : Attribute {
+ for (int i = 0; i < attribs.Length; i++) {
+ if (attribs[i].GetType() == typeof(T)) {
+ attribOut = attribs[i] as T;
+ return true;
+ }
}
+ attribOut = null;
+ return false;
}
- return false;
- }
- /// Turns camelCaseString into Camel Case String
- public static string PrettifyCamelCase(this string camelCase) {
- if (string.IsNullOrEmpty(camelCase)) return "";
- string s = System.Text.RegularExpressions.Regex.Replace(camelCase, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
- return char.ToUpper(s[0]) + s.Substring(1);
- }
+ public static bool HasAttrib(object[] attribs) where T : Attribute {
+ for (int i = 0; i < attribs.Length; i++) {
+ if (attribs[i].GetType() == typeof(T)) {
+ return true;
+ }
+ }
+ return false;
+ }
- /// Returns true if this can be casted to
- public static bool IsCastableTo(this Type from, Type to) {
- if (to.IsAssignableFrom(from)) return true;
- var methods = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Where(
- m => m.ReturnType == to &&
- (m.Name == "op_Implicit" ||
- m.Name == "op_Explicit")
- );
- return methods.Count() > 0;
+ /// Turns camelCaseString into Camel Case String
+ public static string PrettifyCamelCase(this string camelCase) {
+ if (string.IsNullOrEmpty(camelCase)) return "";
+ string s = System.Text.RegularExpressions.Regex.Replace(camelCase, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
+ return char.ToUpper(s[0]) + s.Substring(1);
+ }
+
+ /// Returns true if this can be casted to
+ public static bool IsCastableTo(this Type from, Type to) {
+ if (to.IsAssignableFrom(from)) return true;
+ var methods = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
+ .Where(
+ m => m.ReturnType == to &&
+ (m.Name == "op_Implicit" ||
+ m.Name == "op_Explicit")
+ );
+ return methods.Count() > 0;
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs
index 3cc305e..45df18a 100644
--- a/Scripts/Editor/NodeEditorWindow.cs
+++ b/Scripts/Editor/NodeEditorWindow.cs
@@ -1,97 +1,97 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
+using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
+using XNode;
-[InitializeOnLoad]
-public partial class NodeEditorWindow : EditorWindow {
- public static NodeEditorWindow current;
+namespace XNodeEditor {
+ [InitializeOnLoad]
+ public partial class NodeEditorWindow : EditorWindow {
+ public static NodeEditorWindow current;
- /// Stores node positions for all nodePorts.
- public Dictionary portConnectionPoints { get { return _portConnectionPoints; } }
- private Dictionary _portConnectionPoints = new Dictionary();
- public Dictionary nodeWidths { get { return _nodeWidths; } }
- private Dictionary _nodeWidths = new Dictionary();
- public NodeGraph graph;
- 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;
+ /// Stores node positions for all nodePorts.
+ public Dictionary portConnectionPoints { get { return _portConnectionPoints; } }
+ private Dictionary _portConnectionPoints = new Dictionary();
+ public Dictionary nodeWidths { get { return _nodeWidths; } }
+ private Dictionary _nodeWidths = new Dictionary();
+ public NodeGraph graph;
+ 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;
- void OnFocus() {
- AssetDatabase.SaveAssets();
- current = this;
- }
-
- partial void OnEnable();
- /// Create editor window
- //[MenuItem("Window/UNEC")]
- public static NodeEditorWindow Init() {
- NodeEditorWindow w = CreateInstance();
- w.titleContent = new GUIContent("UNEC");
- w.wantsMouseMove = true;
- w.Show();
- return w;
- }
-
- public void Save() {
- if (AssetDatabase.Contains(graph)) {
- EditorUtility.SetDirty(graph);
- AssetDatabase.SaveAssets();
- } else SaveAs();
- }
-
- public void SaveAs() {
- string path = EditorUtility.SaveFilePanelInProject("Save NodeGraph", "NewNodeGraph", "asset", "");
- if (string.IsNullOrEmpty(path)) return;
- else {
- NodeGraph existingGraph = AssetDatabase.LoadAssetAtPath(path);
- if (existingGraph != null) AssetDatabase.DeleteAsset(path);
- AssetDatabase.CreateAsset(graph, path);
- EditorUtility.SetDirty(graph);
+ void OnFocus() {
AssetDatabase.SaveAssets();
+ current = this;
}
- }
- private void DraggableWindow(int windowID) {
- GUI.DragWindow();
- }
-
- public Vector2 WindowToGridPosition(Vector2 windowPosition) {
- return (windowPosition - (position.size * 0.5f) - (panOffset / zoom)) * zoom;
- }
-
- public Vector2 GridToWindowPosition(Vector2 gridPosition) {
- return (position.size * 0.5f) + (panOffset / zoom) + (gridPosition / zoom);
- }
-
- public Rect GridToWindowRect(Rect gridRect) {
- gridRect.position = GridToWindowPositionNoClipped(gridRect.position);
- return gridRect;
- }
-
- public Vector2 GridToWindowPositionNoClipped(Vector2 gridPosition) {
- Vector2 center = position.size * 0.5f;
- float xOffset = (center.x * zoom + (panOffset.x + gridPosition.x));
- float yOffset = (center.y * zoom + (panOffset.y + gridPosition.y));
- return new Vector2(xOffset, yOffset);
- }
-
- public void SelectNode(Node node) {
- selectedNode = node;
- }
-
- [OnOpenAsset(0)]
- public static bool OnOpen(int instanceID, int line) {
- NodeGraph nodeGraph = EditorUtility.InstanceIDToObject(instanceID) as NodeGraph;
- if (nodeGraph != null) {
- NodeEditorWindow w = Init();
- w.graph = nodeGraph;
- return true;
+ partial void OnEnable();
+ /// Create editor window
+ //[MenuItem("Window/UNEC")]
+ public static NodeEditorWindow Init() {
+ NodeEditorWindow w = CreateInstance();
+ w.titleContent = new GUIContent("UNEC");
+ w.wantsMouseMove = true;
+ w.Show();
+ return w;
+ }
+
+ public void Save() {
+ if (AssetDatabase.Contains(graph)) {
+ EditorUtility.SetDirty(graph);
+ AssetDatabase.SaveAssets();
+ } else SaveAs();
+ }
+
+ public void SaveAs() {
+ string path = EditorUtility.SaveFilePanelInProject("Save NodeGraph", "NewNodeGraph", "asset", "");
+ if (string.IsNullOrEmpty(path)) return;
+ else {
+ NodeGraph existingGraph = AssetDatabase.LoadAssetAtPath(path);
+ if (existingGraph != null) AssetDatabase.DeleteAsset(path);
+ AssetDatabase.CreateAsset(graph, path);
+ EditorUtility.SetDirty(graph);
+ AssetDatabase.SaveAssets();
+ }
+ }
+
+ private void DraggableWindow(int windowID) {
+ GUI.DragWindow();
+ }
+
+ public Vector2 WindowToGridPosition(Vector2 windowPosition) {
+ return (windowPosition - (position.size * 0.5f) - (panOffset / zoom)) * zoom;
+ }
+
+ public Vector2 GridToWindowPosition(Vector2 gridPosition) {
+ return (position.size * 0.5f) + (panOffset / zoom) + (gridPosition / zoom);
+ }
+
+ public Rect GridToWindowRect(Rect gridRect) {
+ gridRect.position = GridToWindowPositionNoClipped(gridRect.position);
+ return gridRect;
+ }
+
+ public Vector2 GridToWindowPositionNoClipped(Vector2 gridPosition) {
+ Vector2 center = position.size * 0.5f;
+ float xOffset = (center.x * zoom + (panOffset.x + gridPosition.x));
+ float yOffset = (center.y * zoom + (panOffset.y + gridPosition.y));
+ return new Vector2(xOffset, yOffset);
+ }
+
+ public void SelectNode(Node node) {
+ selectedNode = node;
+ }
+
+ [OnOpenAsset(0)]
+ public static bool OnOpen(int instanceID, int line) {
+ NodeGraph nodeGraph = EditorUtility.InstanceIDToObject(instanceID) as NodeGraph;
+ if (nodeGraph != null) {
+ NodeEditorWindow w = Init();
+ w.graph = nodeGraph;
+ return true;
+ }
+ return false;
}
- return false;
}
}
\ No newline at end of file
diff --git a/Scripts/Node.cs b/Scripts/Node.cs
index eab0c09..d7592a3 100644
--- a/Scripts/Node.cs
+++ b/Scripts/Node.cs
@@ -2,198 +2,200 @@
using System.Collections.Generic;
using UnityEngine;
-/// Base class for all nodes
-[Serializable]
-public abstract class Node : ScriptableObject {
- public enum ShowBackingValue {
- /// Never show the backing value
- Never,
- /// Show the backing value only when the port does not have any active connections
- Unconnected,
- /// Always show the backing value
- Always
- }
-
- /// Iterate over all ports on this node.
- public IEnumerable Ports { get { foreach (NodePort port in ports.Values) yield return port; } }
- /// Iterate over all outputs on this node.
- public IEnumerable Outputs { get { foreach (NodePort port in Ports) { if (port.IsOutput) yield return port; } } }
- /// Iterate over all inputs on this node.
- public IEnumerable Inputs { get { foreach (NodePort port in Ports) { if (port.IsInput) yield return port; } } }
- /// Iterate over all instane ports on this node.
- public IEnumerable InstancePorts { get { foreach (NodePort port in Ports) { if (port.IsDynamic) yield return port; } } }
- /// Iterate over all instance outputs on this node.
- public IEnumerable InstanceOutputs { get { foreach (NodePort port in Ports) { if (port.IsDynamic && port.IsOutput) yield return port; } } }
- /// Iterate over all instance inputs on this node.
- public IEnumerable InstanceInputs { get { foreach (NodePort port in Ports) { if (port.IsDynamic && port.IsInput) yield return port; } } }
- /// Parent
- [SerializeField] public NodeGraph graph;
- /// Position on the
- [SerializeField] public Vector2 position;
- /// Input s. It is recommended not to modify these at hand. Instead, see
- [SerializeField] private NodePortDictionary ports = new NodePortDictionary();
-
- protected void OnEnable() {
- NodeDataCache.UpdatePorts(this, ports);
- Init();
- }
-
- /// Initialize node. Called on creation.
- protected virtual void Init() { name = GetType().Name; }
-
- /// Checks all connections for invalid references, and removes them.
- public void VerifyConnections() {
- foreach (NodePort port in Ports) port.VerifyConnections();
- }
-
- #region Instance Ports
- /// Returns input port at index
- public NodePort AddInstanceInput(Type type, string fieldName = null) {
- return AddInstancePort(type, NodePort.IO.Input, fieldName);
- }
-
- /// Returns input port at index
- public NodePort AddInstanceOutput(Type type, string fieldName = null) {
- return AddInstancePort(type, NodePort.IO.Output, fieldName);
- }
-
- private NodePort AddInstancePort(Type type, NodePort.IO direction, string fieldName = null) {
- if (fieldName == null) {
- fieldName = "instanceInput_0";
- int i = 0;
- while (HasPort(fieldName)) fieldName = "instanceInput_" + (++i);
- } else if (HasPort(fieldName)) {
- Debug.LogWarning("Port '" + fieldName + "' already exists in " + name, this);
- return ports[fieldName];
+namespace XNode {
+ /// Base class for all nodes
+ [Serializable]
+ public abstract class Node : ScriptableObject {
+ public enum ShowBackingValue {
+ /// Never show the backing value
+ Never,
+ /// Show the backing value only when the port does not have any active connections
+ Unconnected,
+ /// Always show the backing value
+ Always
}
- NodePort port = new NodePort(fieldName, type, direction, this);
- ports.Add(fieldName, port);
- return port;
- }
- public bool RemoveInstancePort(string fieldName) {
- NodePort port = GetPort(fieldName);
- if (port == null || port.IsStatic) return false;
- port.ClearConnections();
- ports.Remove(fieldName);
- return true;
- }
- #endregion
+ /// Iterate over all ports on this node.
+ public IEnumerable Ports { get { foreach (NodePort port in ports.Values) yield return port; } }
+ /// Iterate over all outputs on this node.
+ public IEnumerable Outputs { get { foreach (NodePort port in Ports) { if (port.IsOutput) yield return port; } } }
+ /// Iterate over all inputs on this node.
+ public IEnumerable Inputs { get { foreach (NodePort port in Ports) { if (port.IsInput) yield return port; } } }
+ /// Iterate over all instane ports on this node.
+ public IEnumerable InstancePorts { get { foreach (NodePort port in Ports) { if (port.IsDynamic) yield return port; } } }
+ /// Iterate over all instance outputs on this node.
+ public IEnumerable InstanceOutputs { get { foreach (NodePort port in Ports) { if (port.IsDynamic && port.IsOutput) yield return port; } } }
+ /// Iterate over all instance inputs on this node.
+ public IEnumerable InstanceInputs { get { foreach (NodePort port in Ports) { if (port.IsDynamic && port.IsInput) yield return port; } } }
+ /// Parent
+ [SerializeField] public NodeGraph graph;
+ /// Position on the
+ [SerializeField] public Vector2 position;
+ /// Input s. It is recommended not to modify these at hand. Instead, see
+ [SerializeField] private NodePortDictionary ports = new NodePortDictionary();
- #region Ports
- /// Returns output port which matches fieldName
- public NodePort GetOutputPort(string fieldName) {
- NodePort port = GetPort(fieldName);
- if (port == null || port.direction != NodePort.IO.Output) return null;
- else return port;
- }
+ protected void OnEnable() {
+ NodeDataCache.UpdatePorts(this, ports);
+ Init();
+ }
- /// Returns input port which matches fieldName
- public NodePort GetInputPort(string fieldName) {
- NodePort port = GetPort(fieldName);
- if (port == null || port.direction != NodePort.IO.Input) return null;
- else return port;
- }
+ /// Initialize node. Called on creation.
+ protected virtual void Init() { name = GetType().Name; }
- /// Returns port which matches fieldName
- public NodePort GetPort(string fieldName) {
- if (ports.ContainsKey(fieldName)) return ports[fieldName];
- else return null;
- }
+ /// Checks all connections for invalid references, and removes them.
+ public void VerifyConnections() {
+ foreach (NodePort port in Ports) port.VerifyConnections();
+ }
- public bool HasPort(string fieldName) {
- return ports.ContainsKey(fieldName);
- }
- #endregion
+ #region Instance Ports
+ /// Returns input port at index
+ public NodePort AddInstanceInput(Type type, string fieldName = null) {
+ return AddInstancePort(type, NodePort.IO.Input, fieldName);
+ }
- #region Inputs/Outputs
- /// Return input value for a specified port. Returns fallback value if no ports are connected
- /// Field name of requested input port
- /// If no ports are connected, this value will be returned
- public T GetInputValue(string fieldName, T fallback = default(T)) {
- NodePort port = GetPort(fieldName);
- if (port != null && port.IsConnected) return port.GetInputValue();
- else return fallback;
- }
+ /// Returns input port at index
+ public NodePort AddInstanceOutput(Type type, string fieldName = null) {
+ return AddInstancePort(type, NodePort.IO.Output, fieldName);
+ }
- /// Return all input values for a specified port. Returns fallback value if no ports are connected
- /// Field name of requested input port
- /// If no ports are connected, this value will be returned
- public T[] GetInputValues(string fieldName, params T[] fallback) {
- NodePort port = GetPort(fieldName);
- if (port != null && port.IsConnected) return port.GetInputValues();
- else return fallback;
- }
+ private NodePort AddInstancePort(Type type, NodePort.IO direction, string fieldName = null) {
+ if (fieldName == null) {
+ fieldName = "instanceInput_0";
+ int i = 0;
+ while (HasPort(fieldName)) fieldName = "instanceInput_" + (++i);
+ } else if (HasPort(fieldName)) {
+ Debug.LogWarning("Port '" + fieldName + "' already exists in " + name, this);
+ return ports[fieldName];
+ }
+ NodePort port = new NodePort(fieldName, type, direction, this);
+ ports.Add(fieldName, port);
+ return port;
+ }
- /// Returns a value based on requested port output. Should be overridden before used.
- /// The requested port.
- public virtual object GetValue(NodePort port) {
- Debug.LogWarning("No GetValue(NodePort port) override defined for " + GetType());
- return null;
- }
- #endregion
+ public bool RemoveInstancePort(string fieldName) {
+ NodePort port = GetPort(fieldName);
+ if (port == null || port.IsStatic) return false;
+ port.ClearConnections();
+ ports.Remove(fieldName);
+ return true;
+ }
+ #endregion
- /// Called whenever a connection is being made between two s
- /// Output Input
- public virtual void OnCreateConnection(NodePort from, NodePort to) { }
+ #region Ports
+ /// Returns output port which matches fieldName
+ public NodePort GetOutputPort(string fieldName) {
+ NodePort port = GetPort(fieldName);
+ if (port == null || port.direction != NodePort.IO.Output) return null;
+ else return port;
+ }
- /// Disconnect everything from this node
- public void ClearConnections() {
- foreach (NodePort port in Ports) port.ClearConnections();
- }
+ /// Returns input port which matches fieldName
+ public NodePort GetInputPort(string fieldName) {
+ NodePort port = GetPort(fieldName);
+ if (port == null || port.direction != NodePort.IO.Input) return null;
+ else return port;
+ }
- public override int GetHashCode() {
- return JsonUtility.ToJson(this).GetHashCode();
- }
+ /// Returns port which matches fieldName
+ public NodePort GetPort(string fieldName) {
+ if (ports.ContainsKey(fieldName)) return ports[fieldName];
+ else return null;
+ }
- /// Mark a serializable field as an input port. You can access this through
- [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
- public class InputAttribute : Attribute {
- public ShowBackingValue backingValue;
+ public bool HasPort(string fieldName) {
+ return ports.ContainsKey(fieldName);
+ }
+ #endregion
+
+ #region Inputs/Outputs
+ /// Return input value for a specified port. Returns fallback value if no ports are connected
+ /// Field name of requested input port
+ /// If no ports are connected, this value will be returned
+ public T GetInputValue(string fieldName, T fallback = default(T)) {
+ NodePort port = GetPort(fieldName);
+ if (port != null && port.IsConnected) return port.GetInputValue();
+ else return fallback;
+ }
+
+ /// Return all input values for a specified port. Returns fallback value if no ports are connected
+ /// Field name of requested input port
+ /// If no ports are connected, this value will be returned
+ public T[] GetInputValues(string fieldName, params T[] fallback) {
+ NodePort port = GetPort(fieldName);
+ if (port != null && port.IsConnected) return port.GetInputValues();
+ else return fallback;
+ }
+
+ /// Returns a value based on requested port output. Should be overridden before used.
+ /// The requested port.
+ public virtual object GetValue(NodePort port) {
+ Debug.LogWarning("No GetValue(NodePort port) override defined for " + GetType());
+ return null;
+ }
+ #endregion
+
+ /// Called whenever a connection is being made between two s
+ /// Output Input
+ public virtual void OnCreateConnection(NodePort from, NodePort to) { }
+
+ /// Disconnect everything from this node
+ public void ClearConnections() {
+ foreach (NodePort port in Ports) port.ClearConnections();
+ }
+
+ public override int GetHashCode() {
+ return JsonUtility.ToJson(this).GetHashCode();
+ }
/// Mark a serializable field as an input port. You can access this through
- /// Should we display the backing value for this port as an editor field?
- public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected) { this.backingValue = backingValue; }
- }
+ [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
+ public class InputAttribute : Attribute {
+ public ShowBackingValue backingValue;
- /// Mark a serializable field as an output port. You can access this through
- [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
- public class OutputAttribute : Attribute {
- /// Mark a serializable field as an output port. You can access this through
- public OutputAttribute() { }
- }
-
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
- public class CreateNodeMenuAttribute : Attribute {
- public string menuName;
- /// Manually supply node class with a context menu path
- /// Path to this node in the context menu
- public CreateNodeMenuAttribute(string menuName) {
- this.menuName = menuName;
+ /// Mark a serializable field as an input port. You can access this through
+ /// Should we display the backing value for this port as an editor field?
+ public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected) { this.backingValue = backingValue; }
}
- }
- [Serializable] private class NodePortDictionary : Dictionary, ISerializationCallbackReceiver {
- [SerializeField] private List keys = new List();
- [SerializeField] private List values = new List();
+ /// Mark a serializable field as an output port. You can access this through
+ [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
+ public class OutputAttribute : Attribute {
+ /// Mark a serializable field as an output port. You can access this through
+ public OutputAttribute() { }
+ }
- public void OnBeforeSerialize() {
- keys.Clear();
- values.Clear();
- foreach (KeyValuePair pair in this) {
- keys.Add(pair.Key);
- values.Add(pair.Value);
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public class CreateNodeMenuAttribute : Attribute {
+ public string menuName;
+ /// Manually supply node class with a context menu path
+ /// Path to this node in the context menu
+ public CreateNodeMenuAttribute(string menuName) {
+ this.menuName = menuName;
}
}
- public void OnAfterDeserialize() {
- this.Clear();
+ [Serializable] private class NodePortDictionary : Dictionary, ISerializationCallbackReceiver {
+ [SerializeField] private List keys = new List();
+ [SerializeField] private List values = new List();
- if (keys.Count != values.Count)
- throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
+ public void OnBeforeSerialize() {
+ keys.Clear();
+ values.Clear();
+ foreach (KeyValuePair pair in this) {
+ keys.Add(pair.Key);
+ values.Add(pair.Value);
+ }
+ }
- for (int i = 0; i < keys.Count; i++)
- this.Add(keys[i], values[i]);
+ public void OnAfterDeserialize() {
+ this.Clear();
+
+ if (keys.Count != values.Count)
+ throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
+
+ for (int i = 0; i < keys.Count; i++)
+ this.Add(keys[i], values[i]);
+ }
}
}
}
\ No newline at end of file
diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs
index 30d98ab..ec9cc8f 100644
--- a/Scripts/NodeDataCache.cs
+++ b/Scripts/NodeDataCache.cs
@@ -1,100 +1,100 @@
-using System.Collections;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using UnityEditor;
using UnityEngine;
-/// Precaches reflection data in editor so we won't have to do it runtime
-public static class NodeDataCache {
- private static PortDataCache portDataCache;
- private static bool Initialized { get { return portDataCache != null; } }
+namespace XNode {
+ /// Precaches reflection data in editor so we won't have to do it runtime
+ public static class NodeDataCache {
+ private static PortDataCache portDataCache;
+ private static bool Initialized { get { return portDataCache != null; } }
- /// Update static ports to reflect class fields.
- public static void UpdatePorts(Node node, Dictionary ports) {
- if (!Initialized) BuildCache();
+ /// Update static ports to reflect class fields.
+ public static void UpdatePorts(Node node, Dictionary ports) {
+ if (!Initialized) BuildCache();
- Dictionary staticPorts = new Dictionary();
- System.Type nodeType = node.GetType();
+ Dictionary staticPorts = new Dictionary();
+ System.Type nodeType = node.GetType();
- if (!portDataCache.ContainsKey(nodeType)) return;
- for (int i = 0; i < portDataCache[nodeType].Count; i++) {
- staticPorts.Add(portDataCache[nodeType][i].fieldName, portDataCache[nodeType][i]);
- }
-
- // Cleanup port dict - Remove nonexisting static ports - update static port types
- foreach (NodePort port in ports.Values.ToList()) {
- if (staticPorts.ContainsKey(port.fieldName)) {
- NodePort staticPort = staticPorts[port.fieldName];
- if (port.IsDynamic || port.direction != staticPort.direction) ports.Remove(port.fieldName);
- else port.ValueType = staticPort.ValueType;
- } else if (port.IsStatic) ports.Remove(port.fieldName);
- }
- // Add missing ports
- foreach (NodePort staticPort in staticPorts.Values) {
- if (!ports.ContainsKey(staticPort.fieldName)) {
- ports.Add(staticPort.fieldName, new NodePort(staticPort, node));
+ if (!portDataCache.ContainsKey(nodeType)) return;
+ for (int i = 0; i < portDataCache[nodeType].Count; i++) {
+ staticPorts.Add(portDataCache[nodeType][i].fieldName, portDataCache[nodeType][i]);
}
- }
- }
- private static void BuildCache() {
- portDataCache = new PortDataCache();
- System.Type baseType = typeof(Node);
- Assembly assembly = Assembly.GetAssembly(baseType);
- System.Type[] nodeTypes = assembly.GetTypes().Where(t =>
- !t.IsAbstract &&
- baseType.IsAssignableFrom(t)
- ).ToArray();
-
- for (int i = 0; i < nodeTypes.Length; i++) {
- CachePorts(nodeTypes[i]);
- }
- }
-
- private static void CachePorts(System.Type nodeType) {
- System.Reflection.FieldInfo[] fieldInfo = nodeType.GetFields();
- for (int i = 0; i < fieldInfo.Length; i++) {
-
- //Get InputAttribute and OutputAttribute
- object[] attribs = fieldInfo[i].GetCustomAttributes(false);
- Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute;
- Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute;
-
- if (inputAttrib == null && outputAttrib == null) continue;
-
- if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output.");
- else {
- if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List());
- portDataCache[nodeType].Add(new NodePort(fieldInfo[i]));
+ // Cleanup port dict - Remove nonexisting static ports - update static port types
+ foreach (NodePort port in ports.Values.ToList()) {
+ if (staticPorts.ContainsKey(port.fieldName)) {
+ NodePort staticPort = staticPorts[port.fieldName];
+ if (port.IsDynamic || port.direction != staticPort.direction) ports.Remove(port.fieldName);
+ else port.ValueType = staticPort.ValueType;
+ } else if (port.IsStatic) ports.Remove(port.fieldName);
}
- }
- }
-
- [System.Serializable]
- private class PortDataCache : Dictionary>, ISerializationCallbackReceiver {
- [SerializeField] private List keys = new List();
- [SerializeField] private List> values = new List>();
-
- // save the dictionary to lists
- public void OnBeforeSerialize() {
- keys.Clear();
- values.Clear();
- foreach (var pair in this) {
- keys.Add(pair.Key);
- values.Add(pair.Value);
+ // Add missing ports
+ foreach (NodePort staticPort in staticPorts.Values) {
+ if (!ports.ContainsKey(staticPort.fieldName)) {
+ ports.Add(staticPort.fieldName, new NodePort(staticPort, node));
+ }
}
}
- // load dictionary from lists
- public void OnAfterDeserialize() {
- this.Clear();
+ private static void BuildCache() {
+ portDataCache = new PortDataCache();
+ System.Type baseType = typeof(Node);
+ Assembly assembly = Assembly.GetAssembly(baseType);
+ System.Type[] nodeTypes = assembly.GetTypes().Where(t =>
+ !t.IsAbstract &&
+ baseType.IsAssignableFrom(t)
+ ).ToArray();
- if (keys.Count != values.Count)
- throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
+ for (int i = 0; i < nodeTypes.Length; i++) {
+ CachePorts(nodeTypes[i]);
+ }
+ }
- for (int i = 0; i < keys.Count; i++)
- this.Add(keys[i], values[i]);
+ private static void CachePorts(System.Type nodeType) {
+ System.Reflection.FieldInfo[] fieldInfo = nodeType.GetFields();
+ for (int i = 0; i < fieldInfo.Length; i++) {
+
+ //Get InputAttribute and OutputAttribute
+ object[] attribs = fieldInfo[i].GetCustomAttributes(false);
+ Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute;
+ Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute;
+
+ if (inputAttrib == null && outputAttrib == null) continue;
+
+ if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output.");
+ else {
+ if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List());
+ portDataCache[nodeType].Add(new NodePort(fieldInfo[i]));
+ }
+ }
+ }
+
+ [System.Serializable]
+ private class PortDataCache : Dictionary>, ISerializationCallbackReceiver {
+ [SerializeField] private List keys = new List();
+ [SerializeField] private List> values = new List>();
+
+ // save the dictionary to lists
+ public void OnBeforeSerialize() {
+ keys.Clear();
+ values.Clear();
+ foreach (var pair in this) {
+ keys.Add(pair.Key);
+ values.Add(pair.Value);
+ }
+ }
+
+ // load dictionary from lists
+ public void OnAfterDeserialize() {
+ this.Clear();
+
+ if (keys.Count != values.Count)
+ throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
+
+ for (int i = 0; i < keys.Count; i++)
+ this.Add(keys[i], values[i]);
+ }
}
}
}
\ No newline at end of file
diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs
index 2fa569b..8253a42 100644
--- a/Scripts/NodeGraph.cs
+++ b/Scripts/NodeGraph.cs
@@ -2,46 +2,48 @@
using System.Collections.Generic;
using UnityEngine;
-/// Base class for all node graphs
-[Serializable]
-public abstract class NodeGraph : ScriptableObject {
+namespace XNode {
+ /// Base class for all node graphs
+ [Serializable]
+ public abstract class NodeGraph : ScriptableObject {
- /// All nodes in the graph.
- /// See:
- [SerializeField] public List nodes = new List();
+ /// All nodes in the graph.
+ /// See:
+ [SerializeField] public List nodes = new List();
- public T AddNode() where T : Node {
- return AddNode(typeof(T)) as T;
- }
-
- public virtual Node AddNode(Type type) {
- Node node = ScriptableObject.CreateInstance(type) as Node;
-#if UNITY_EDITOR
- if (!Application.isPlaying) {
- UnityEditor.AssetDatabase.AddObjectToAsset(node, this);
- UnityEditor.AssetDatabase.SaveAssets();
+ public T AddNode() where T : Node {
+ return AddNode(typeof(T)) as T;
}
-#endif
- nodes.Add(node);
- node.graph = this;
- return node;
- }
- /// Safely remove a node and all its connections
- ///
- public void RemoveNode(Node node) {
- node.ClearConnections();
+ public virtual Node AddNode(Type type) {
+ Node node = ScriptableObject.CreateInstance(type) as Node;
#if UNITY_EDITOR
- if (!Application.isPlaying) {
- DestroyImmediate(node, true);
- UnityEditor.AssetDatabase.SaveAssets();
- }
+ if (!Application.isPlaying) {
+ UnityEditor.AssetDatabase.AddObjectToAsset(node, this);
+ UnityEditor.AssetDatabase.SaveAssets();
+ }
#endif
- nodes.Remove(node);
- }
+ nodes.Add(node);
+ node.graph = this;
+ return node;
+ }
- /// Remove all nodes and connections from the graph
- public void Clear() {
- nodes.Clear();
+ /// Safely remove a node and all its connections
+ ///
+ public void RemoveNode(Node node) {
+ node.ClearConnections();
+#if UNITY_EDITOR
+ if (!Application.isPlaying) {
+ DestroyImmediate(node, true);
+ UnityEditor.AssetDatabase.SaveAssets();
+ }
+#endif
+ nodes.Remove(node);
+ }
+
+ /// Remove all nodes and connections from the graph
+ public void Clear() {
+ nodes.Clear();
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs
index 6a85b78..c26ad90 100644
--- a/Scripts/NodePort.cs
+++ b/Scripts/NodePort.cs
@@ -1,246 +1,247 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
-[Serializable]
-public class NodePort {
- public enum IO { Input, Output }
+namespace XNode {
+ [Serializable]
+ public class NodePort {
+ public enum IO { Input, Output }
- public int ConnectionCount { get { return connections.Count; } }
- /// Return the first connection
- public NodePort Connection { get { return connections.Count > 0 ? connections[0].Port : null; } }
+ public int ConnectionCount { get { return connections.Count; } }
+ /// Return the first connection
+ public NodePort Connection { get { return connections.Count > 0 ? connections[0].Port : null; } }
- 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 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 string fieldName { get { return _fieldName; } }
- public Node node { get { return _node; } }
- public bool IsDynamic { get { return _dynamic; } }
- public bool IsStatic { get { return !_dynamic; } }
- public Type ValueType {
- get {
- if (valueType == null && !string.IsNullOrEmpty(_typeQualifiedName)) valueType = Type.GetType(_typeQualifiedName, false);
- return valueType;
- }
- set {
- valueType = value;
- if (value != null) _typeQualifiedName = value.AssemblyQualifiedName;
- }
- }
- private Type valueType;
-
- [SerializeField] private string _fieldName;
- [SerializeField] private Node _node;
- [SerializeField] private string _typeQualifiedName;
- [SerializeField] private List connections = new List();
- [SerializeField] private IO _direction;
- [SerializeField] private bool _dynamic;
-
- /// Construct a static targetless nodeport. Used as a template.
- public NodePort(FieldInfo fieldInfo) {
- _fieldName = fieldInfo.Name;
- ValueType = fieldInfo.FieldType;
- _dynamic = false;
- var attribs = fieldInfo.GetCustomAttributes(false);
- for (int i = 0; i < attribs.Length; i++) {
- if (attribs[i] is Node.InputAttribute) _direction = IO.Input;
- else if (attribs[i] is Node.OutputAttribute) _direction = IO.Output;
- }
- }
-
- /// Copy a nodePort but assign it to another node.
- public NodePort(NodePort nodePort, Node node) {
- _fieldName = nodePort._fieldName;
- ValueType = nodePort.valueType;
- _direction = nodePort.direction;
- _dynamic = nodePort._dynamic;
- _node = node;
- }
-
- /// Construct a dynamic port. Dynamic ports are not forgotten on reimport, and is ideal for runtime-created ports.
- public NodePort(string fieldName, Type type, IO direction, Node node) {
- _fieldName = fieldName;
- this.ValueType = type;
- _direction = direction;
- _node = node;
- _dynamic = true;
- }
-
- /// Checks all connections for invalid references, and removes them.
- public void VerifyConnections() {
- for (int i = connections.Count - 1; i >= 0; i--) {
- if (connections[i].node != null &&
- !string.IsNullOrEmpty(connections[i].fieldName) &&
- connections[i].node.GetPort(connections[i].fieldName) != null)
- continue;
- connections.RemoveAt(i);
- }
- }
-
- /// Return the output value of this node through its parent nodes GetValue override method.
- ///
- public object GetOutputValue() {
- if (direction == IO.Input) return null;
- return node.GetValue(this);
- }
-
- /// Return the output value of the first connected port. Returns null if none found or invalid.
- ///
- public object GetInputValue() {
- NodePort connectedPort = Connection;
- if (connectedPort == null) return null;
- return connectedPort.GetOutputValue();
- }
-
- /// Return the output values of all connected ports.
- ///
- public object[] GetInputValues() {
- object[] objs = new object[ConnectionCount];
- for (int i = 0; i < ConnectionCount; i++) {
- NodePort connectedPort = connections[i].Port;
- if (connectedPort == null) { // if we happen to find a null port, remove it and look again
- connections.RemoveAt(i);
- i--;
- continue;
+ public string fieldName { get { return _fieldName; } }
+ public Node node { get { return _node; } }
+ public bool IsDynamic { get { return _dynamic; } }
+ public bool IsStatic { get { return !_dynamic; } }
+ public Type ValueType {
+ get {
+ if (valueType == null && !string.IsNullOrEmpty(_typeQualifiedName)) valueType = Type.GetType(_typeQualifiedName, false);
+ return valueType;
+ }
+ set {
+ valueType = value;
+ if (value != null) _typeQualifiedName = value.AssemblyQualifiedName;
}
- objs[i] = connectedPort.GetOutputValue();
}
- return objs;
- }
+ private Type valueType;
- /// Return the output value of the first connected port. Returns null if none found or invalid.
- ///
- public T GetInputValue() {
- object obj = GetInputValue();
- return obj is T ? (T) obj : default(T);
- }
+ [SerializeField] private string _fieldName;
+ [SerializeField] private Node _node;
+ [SerializeField] private string _typeQualifiedName;
+ [SerializeField] private List connections = new List();
+ [SerializeField] private IO _direction;
+ [SerializeField] private bool _dynamic;
- /// Return the output values of all connected ports.
- ///
- public T[] GetInputValues() {
- object[] objs = GetInputValues();
- T[] ts = new T[objs.Length];
- for (int i = 0; i < objs.Length; i++) {
- if (objs[i] is T) ts[i] = (T) objs[i];
+ /// Construct a static targetless nodeport. Used as a template.
+ public NodePort(FieldInfo fieldInfo) {
+ _fieldName = fieldInfo.Name;
+ ValueType = fieldInfo.FieldType;
+ _dynamic = false;
+ var attribs = fieldInfo.GetCustomAttributes(false);
+ for (int i = 0; i < attribs.Length; i++) {
+ if (attribs[i] is Node.InputAttribute) _direction = IO.Input;
+ else if (attribs[i] is Node.OutputAttribute) _direction = IO.Output;
+ }
}
- return ts;
- }
- /// Return true if port is connected and has a valid input.
- ///
- public bool TryGetInputValue(out T value) {
- object obj = GetInputValue();
- if (obj is T) {
- value = (T) obj;
- return true;
- } else {
- value = default(T);
+ /// Copy a nodePort but assign it to another node.
+ public NodePort(NodePort nodePort, Node node) {
+ _fieldName = nodePort._fieldName;
+ ValueType = nodePort.valueType;
+ _direction = nodePort.direction;
+ _dynamic = nodePort._dynamic;
+ _node = node;
+ }
+
+ /// Construct a dynamic port. Dynamic ports are not forgotten on reimport, and is ideal for runtime-created ports.
+ public NodePort(string fieldName, Type type, IO direction, Node node) {
+ _fieldName = fieldName;
+ this.ValueType = type;
+ _direction = direction;
+ _node = node;
+ _dynamic = true;
+ }
+
+ /// Checks all connections for invalid references, and removes them.
+ public void VerifyConnections() {
+ for (int i = connections.Count - 1; i >= 0; i--) {
+ if (connections[i].node != null &&
+ !string.IsNullOrEmpty(connections[i].fieldName) &&
+ connections[i].node.GetPort(connections[i].fieldName) != null)
+ continue;
+ connections.RemoveAt(i);
+ }
+ }
+
+ /// Return the output value of this node through its parent nodes GetValue override method.
+ ///
+ public object GetOutputValue() {
+ if (direction == IO.Input) return null;
+ return node.GetValue(this);
+ }
+
+ /// Return the output value of the first connected port. Returns null if none found or invalid.
+ ///
+ public object GetInputValue() {
+ NodePort connectedPort = Connection;
+ if (connectedPort == null) return null;
+ return connectedPort.GetOutputValue();
+ }
+
+ /// Return the output values of all connected ports.
+ ///
+ public object[] GetInputValues() {
+ object[] objs = new object[ConnectionCount];
+ for (int i = 0; i < ConnectionCount; i++) {
+ NodePort connectedPort = connections[i].Port;
+ if (connectedPort == null) { // if we happen to find a null port, remove it and look again
+ connections.RemoveAt(i);
+ i--;
+ continue;
+ }
+ objs[i] = connectedPort.GetOutputValue();
+ }
+ return objs;
+ }
+
+ /// Return the output value of the first connected port. Returns null if none found or invalid.
+ ///
+ public T GetInputValue() {
+ object obj = GetInputValue();
+ return obj is T ? (T) obj : default(T);
+ }
+
+ /// Return the output values of all connected ports.
+ ///
+ public T[] GetInputValues() {
+ object[] objs = GetInputValues();
+ T[] ts = new T[objs.Length];
+ for (int i = 0; i < objs.Length; i++) {
+ if (objs[i] is T) ts[i] = (T) objs[i];
+ }
+ return ts;
+ }
+
+ /// Return true if port is connected and has a valid input.
+ ///
+ public bool TryGetInputValue(out T value) {
+ object obj = GetInputValue();
+ if (obj is T) {
+ value = (T) obj;
+ return true;
+ } else {
+ value = default(T);
+ return false;
+ }
+ }
+
+ /// Return the sum of all inputs.
+ ///
+ public float GetInputSum(float fallback) {
+ object[] objs = GetInputValues();
+ if (objs.Length == 0) return fallback;
+ float result = 0;
+ for (int i = 0; i < objs.Length; i++) {
+ if (objs[i] is float) result += (float) objs[i];
+ }
+ return result;
+ }
+
+ /// Return the sum of all inputs.
+ ///
+ public int GetInputSum(int fallback) {
+ object[] objs = GetInputValues();
+ if (objs.Length == 0) return fallback;
+ int result = 0;
+ for (int i = 0; i < objs.Length; i++) {
+ if (objs[i] is int) result += (int) objs[i];
+ }
+ return result;
+ }
+
+ /// Connect this to another
+ /// The to connect to
+ public void Connect(NodePort port) {
+ if (connections == null) connections = new List();
+ if (port == null) { Debug.LogWarning("Cannot connect to null port"); return; }
+ if (port == this) { Debug.LogWarning("Attempting to connect port to self."); return; }
+ if (IsConnectedTo(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(new PortConnection(port));
+ if (port.connections == null) port.connections = new List();
+ if (!port.IsConnectedTo(this)) port.connections.Add(new PortConnection(this));
+ node.OnCreateConnection(this, port);
+ port.node.OnCreateConnection(this, port);
+ }
+
+ public NodePort GetConnection(int i) {
+ //If the connection is broken for some reason, remove it.
+ if (connections[i].node == null || string.IsNullOrEmpty(connections[i].fieldName)) {
+ connections.RemoveAt(i);
+ return null;
+ }
+ NodePort port = connections[i].node.GetPort(connections[i].fieldName);
+ if (port == null) {
+ connections.RemoveAt(i);
+ return null;
+ }
+ return port;
+ }
+
+ public bool IsConnectedTo(NodePort port) {
+ for (int i = 0; i < connections.Count; i++) {
+ if (connections[i].Port == port) return true;
+ }
return false;
}
- }
- /// Return the sum of all inputs.
- ///
- public float GetInputSum(float fallback) {
- object[] objs = GetInputValues();
- if (objs.Length == 0) return fallback;
- float result = 0;
- for (int i = 0; i < objs.Length; i++) {
- if (objs[i] is float) result += (float) objs[i];
- }
- return result;
- }
-
- /// Return the sum of all inputs.
- ///
- public int GetInputSum(int fallback) {
- object[] objs = GetInputValues();
- if (objs.Length == 0) return fallback;
- int result = 0;
- for (int i = 0; i < objs.Length; i++) {
- if (objs[i] is int) result += (int) objs[i];
- }
- return result;
- }
-
- /// Connect this to another
- /// The to connect to
- public void Connect(NodePort port) {
- if (connections == null) connections = new List();
- if (port == null) { Debug.LogWarning("Cannot connect to null port"); return; }
- if (port == this) { Debug.LogWarning("Attempting to connect port to self."); return; }
- if (IsConnectedTo(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(new PortConnection(port));
- if (port.connections == null) port.connections = new List();
- if (!port.IsConnectedTo(this)) port.connections.Add(new PortConnection(this));
- node.OnCreateConnection(this, port);
- port.node.OnCreateConnection(this, port);
- }
-
- public NodePort GetConnection(int i) {
- //If the connection is broken for some reason, remove it.
- if (connections[i].node == null || string.IsNullOrEmpty(connections[i].fieldName)) {
- connections.RemoveAt(i);
- return null;
- }
- NodePort port = connections[i].node.GetPort(connections[i].fieldName);
- if (port == null) {
- connections.RemoveAt(i);
- return null;
- }
- return port;
- }
-
- public bool IsConnectedTo(NodePort port) {
- for (int i = 0; i < connections.Count; i++) {
- if (connections[i].Port == port) return true;
- }
- return false;
- }
-
- public void Disconnect(NodePort port) {
- for (int i = connections.Count - 1; i >= 0; i--) {
- //Remove matching ports.
- if (connections[i].Port == port) {
- connections.RemoveAt(i);
+ public void Disconnect(NodePort port) {
+ for (int i = connections.Count - 1; i >= 0; i--) {
+ //Remove matching ports.
+ if (connections[i].Port == port) {
+ connections.RemoveAt(i);
+ }
+ }
+ for (int i = 0; i < port.connections.Count; i++) {
+ if (port.connections[i].Port == this) {
+ port.connections.RemoveAt(i);
+ }
}
}
- for (int i = 0; i < port.connections.Count; i++) {
- if (port.connections[i].Port == this) {
- port.connections.RemoveAt(i);
+
+ public void ClearConnections() {
+ while (connections.Count > 0) {
+ Disconnect(connections[0].Port);
}
}
- }
- public void ClearConnections() {
- while (connections.Count > 0) {
- Disconnect(connections[0].Port);
- }
- }
+ [Serializable]
+ private class PortConnection {
+ [SerializeField] public string fieldName;
+ [SerializeField] public Node node;
+ public NodePort Port { get { return port != null ? port : port = GetPort(); } }
+ [NonSerialized] private NodePort port;
- [Serializable]
- private class PortConnection {
- [SerializeField] public string fieldName;
- [SerializeField] public Node node;
- public NodePort Port { get { return port != null ? port : port = GetPort(); } }
- [NonSerialized] private NodePort port;
+ public PortConnection(NodePort port) {
+ this.port = port;
+ node = port.node;
+ fieldName = port.fieldName;
+ }
- public PortConnection(NodePort port) {
- this.port = port;
- node = port.node;
- fieldName = port.fieldName;
- }
-
- /// Returns the port that this points to
- private NodePort GetPort() {
- if (node == null || string.IsNullOrEmpty(fieldName)) return null;
- return node.GetPort(fieldName);
+ /// Returns the port that this points to
+ private NodePort GetPort() {
+ if (node == null || string.IsNullOrEmpty(fieldName)) return null;
+ return node.GetPort(fieldName);
+ }
}
}
}
\ No newline at end of file