mirror of
https://github.com/Siccity/xNode.git
synced 2025-12-20 01:06:01 +08:00
WIP
This commit is contained in:
parent
7070081d25
commit
aeeeb74290
13
Scripts/Attributes/CreateNodeMenuAttribute.cs
Normal file
13
Scripts/Attributes/CreateNodeMenuAttribute.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XNode {
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||||
|
public class CreateNodeMenuAttribute : Attribute {
|
||||||
|
public string menuName;
|
||||||
|
/// <summary> Manually supply node class with a context menu path </summary>
|
||||||
|
/// <param name="menuName"> Path to this node in the context menu. Null or empty hides it. </param>
|
||||||
|
public CreateNodeMenuAttribute(string menuName) {
|
||||||
|
this.menuName = menuName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 5085db84cab1cba42ad78c9310b87f36
|
guid: 5fbc123731a209b47a4ada9ac41875f2
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
24
Scripts/Attributes/InputAttribute.cs
Normal file
24
Scripts/Attributes/InputAttribute.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XNode {
|
||||||
|
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||||
|
public class InputAttribute : Attribute {
|
||||||
|
public ShowBackingValue backingValue;
|
||||||
|
public ConnectionType connectionType;
|
||||||
|
public bool dynamicPortList;
|
||||||
|
public TypeConstraint typeConstraint;
|
||||||
|
|
||||||
|
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
|
||||||
|
/// <param name="backingValue">Should we display the backing value for this port as an editor field? </param>
|
||||||
|
/// <param name="connectionType">Should we allow multiple connections? </param>
|
||||||
|
/// <param name="typeConstraint">Constrains which input connections can be made to this port </param>
|
||||||
|
/// <param name="dynamicPortList">If true, will display a reorderable list of inputs instead of a single port. Will automatically add and display values for lists and arrays </param>
|
||||||
|
public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected, ConnectionType connectionType = ConnectionType.Multiple, TypeConstraint typeConstraint = TypeConstraint.None, bool dynamicPortList = false) {
|
||||||
|
this.backingValue = backingValue;
|
||||||
|
this.connectionType = connectionType;
|
||||||
|
this.dynamicPortList = dynamicPortList;
|
||||||
|
this.typeConstraint = typeConstraint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Scripts/Attributes/InputAttribute.cs.meta
Normal file
11
Scripts/Attributes/InputAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6d1e987ec989e04684a50162742a10b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
30
Scripts/Attributes/NodeTintAttribute.cs
Normal file
30
Scripts/Attributes/NodeTintAttribute.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace XNode {
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||||
|
public class NodeTintAttribute : Attribute {
|
||||||
|
public Color color;
|
||||||
|
/// <summary> Specify a color for this node type </summary>
|
||||||
|
/// <param name="r"> Red [0.0f .. 1.0f] </param>
|
||||||
|
/// <param name="g"> Green [0.0f .. 1.0f] </param>
|
||||||
|
/// <param name="b"> Blue [0.0f .. 1.0f] </param>
|
||||||
|
public NodeTintAttribute(float r, float g, float b) {
|
||||||
|
color = new Color(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Specify a color for this node type </summary>
|
||||||
|
/// <param name="hex"> HEX color value </param>
|
||||||
|
public NodeTintAttribute(string hex) {
|
||||||
|
ColorUtility.TryParseHtmlString(hex, out color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Specify a color for this node type </summary>
|
||||||
|
/// <param name="r"> Red [0 .. 255] </param>
|
||||||
|
/// <param name="g"> Green [0 .. 255] </param>
|
||||||
|
/// <param name="b"> Blue [0 .. 255] </param>
|
||||||
|
public NodeTintAttribute(byte r, byte g, byte b) {
|
||||||
|
color = new Color32(r, g, b, byte.MaxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Scripts/Attributes/NodeTintAttribute.cs.meta
Normal file
11
Scripts/Attributes/NodeTintAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 69fbc77b18c1fe3418cf19a02d0d273b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
13
Scripts/Attributes/NodeWidthAttribute.cs
Normal file
13
Scripts/Attributes/NodeWidthAttribute.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XNode {
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||||
|
public class NodeWidthAttribute : Attribute {
|
||||||
|
public int width;
|
||||||
|
/// <summary> Specify a width for this node type </summary>
|
||||||
|
/// <param name="width"> Width </param>
|
||||||
|
public NodeWidthAttribute(int width) {
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Scripts/Attributes/NodeWidthAttribute.cs.meta
Normal file
11
Scripts/Attributes/NodeWidthAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 068cc89d1268616429d0a01ad64e2959
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
21
Scripts/Attributes/OutputAttribute.cs
Normal file
21
Scripts/Attributes/OutputAttribute.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XNode {
|
||||||
|
/// <summary> Mark a serializable field as an output port. You can access this through <see cref="GetOutputPort(string)"/> </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||||
|
public class OutputAttribute : Attribute {
|
||||||
|
public ShowBackingValue backingValue;
|
||||||
|
public ConnectionType connectionType;
|
||||||
|
public bool dynamicPortList;
|
||||||
|
|
||||||
|
/// <summary> Mark a serializable field as an output port. You can access this through <see cref="GetOutputPort(string)"/> </summary>
|
||||||
|
/// <param name="backingValue">Should we display the backing value for this port as an editor field? </param>
|
||||||
|
/// <param name="connectionType">Should we allow multiple connections? </param>
|
||||||
|
/// <param name="dynamicPortList">If true, will display a reorderable list of outputs instead of a single port. Will automatically add and display values for lists and arrays </param>
|
||||||
|
public OutputAttribute(ShowBackingValue backingValue = ShowBackingValue.Never, ConnectionType connectionType = ConnectionType.Multiple, bool dynamicPortList = false) {
|
||||||
|
this.backingValue = backingValue;
|
||||||
|
this.connectionType = connectionType;
|
||||||
|
this.dynamicPortList = dynamicPortList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Scripts/Attributes/OutputAttribute.cs.meta
Normal file
11
Scripts/Attributes/OutputAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e79589a039fb6f94a9215b71335c5641
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -1,14 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace XNodeEditor {
|
|
||||||
/// <summary>
|
|
||||||
/// Workaround since c# doesn't support nested interfaces.
|
|
||||||
///
|
|
||||||
/// Used with INodeEditor and INodeGraphEditor.
|
|
||||||
/// </summary>
|
|
||||||
public interface ICustomEditor<T> {
|
|
||||||
T Target { get; }
|
|
||||||
SerializedObject SerializedObject { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,43 +6,25 @@ using UnityEngine;
|
|||||||
using XNode;
|
using XNode;
|
||||||
|
|
||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
public interface INodeGraphEditor {
|
public interface INodeEditor {
|
||||||
INodeGraph target { get; }
|
Editor editor { get; }
|
||||||
void OnGUI();
|
void OnBodyGUI();
|
||||||
void OnOpen();
|
void OnHeaderGUI();
|
||||||
Texture2D GetGridTexture();
|
|
||||||
Texture2D GetSecondaryGridTexture();
|
|
||||||
/// <summary> Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. </summary>
|
|
||||||
NodeEditorPreferences.Settings GetDefaultPreferences();
|
|
||||||
/// <summary> Returns context node menu path. Null or empty strings for hidden nodes. </summary>
|
|
||||||
string GetNodeMenuName(Type type);
|
|
||||||
/// <summary> Add items for the context menu when right-clicking this node. Override to add custom menu items. </summary>
|
/// <summary> Add items for the context menu when right-clicking this node. Override to add custom menu items. </summary>
|
||||||
void AddContextMenuItems(GenericMenu menu);
|
void AddContextMenuItems(GenericMenu menu);
|
||||||
Color GetPortColor(XNode.NodePort port);
|
|
||||||
Color GetTypeColor(Type type);
|
|
||||||
/// <summary> Create a node and save it in the graph asset </summary>
|
|
||||||
XNode.INode CreateNode(Type type, Vector2 position);
|
|
||||||
/// <summary> Creates a copy of the original node in the graph </summary>
|
|
||||||
XNode.INode CopyNode(XNode.INode original);
|
|
||||||
/// <summary> Safely remove a node and all its connections. </summary>
|
|
||||||
void RemoveNode(XNode.INode node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class CustomNodeGraphEditorAttribute : Attribute,
|
public class CustomNodeEditorAttribute : Attribute, XNodeEditor.Internal.INodeEditorAttrib {
|
||||||
XNodeEditor.Internal.INodeEditorAttrib {
|
private Type inspectedType;
|
||||||
private Type inspectedType;
|
/// <summary> Tells a NodeEditor which Node type it is an editor for </summary>
|
||||||
public string editorPrefsKey;
|
/// <param name="inspectedType">Type that this editor can edit</param>
|
||||||
/// <summary> Tells a NodeGraphEditor which Graph type it is an editor for </summary>
|
public CustomNodeEditorAttribute(Type inspectedType) {
|
||||||
/// <param name="inspectedType">Type that this editor can edit</param>
|
this.inspectedType = inspectedType;
|
||||||
/// <param name="editorPrefsKey">Define unique key for unique layout settings instance</param>
|
|
||||||
public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") {
|
|
||||||
this.inspectedType = inspectedType;
|
|
||||||
this.editorPrefsKey = editorPrefsKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type GetInspectedType() {
|
|
||||||
return inspectedType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Type GetInspectedType() {
|
||||||
|
return inspectedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,16 +1,48 @@
|
|||||||
using System.Collections;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using XNode;
|
||||||
|
|
||||||
public class INodeGraphEditor : MonoBehaviour {
|
namespace XNodeEditor {
|
||||||
|
public interface INodeGraphEditor {
|
||||||
|
NodeEditorWindow window { get; set; }
|
||||||
|
Editor editor { get; }
|
||||||
|
void OnGUI();
|
||||||
|
void OnOpen();
|
||||||
|
Texture2D GetGridTexture();
|
||||||
|
Texture2D GetSecondaryGridTexture();
|
||||||
|
/// <summary> Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. </summary>
|
||||||
|
NodeEditorPreferences.Settings GetDefaultPreferences();
|
||||||
|
/// <summary> Returns context node menu path. Null or empty strings for hidden nodes. </summary>
|
||||||
|
string GetNodeMenuName(Type type);
|
||||||
|
/// <summary> Add items for the context menu when right-clicking this node. Override to add custom menu items. </summary>
|
||||||
|
void AddContextMenuItems(GenericMenu menu);
|
||||||
|
Color GetPortColor(XNode.NodePort port);
|
||||||
|
Color GetTypeColor(Type type);
|
||||||
|
/// <summary> Create a node and save it in the graph asset </summary>
|
||||||
|
XNode.INode CreateNode(Type type, Vector2 position);
|
||||||
|
/// <summary> Creates a copy of the original node in the graph </summary>
|
||||||
|
XNode.INode CopyNode(XNode.INode original);
|
||||||
|
/// <summary> Safely remove a node and all its connections. </summary>
|
||||||
|
void RemoveNode(XNode.INode node);
|
||||||
|
}
|
||||||
|
|
||||||
// Use this for initialization
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
void Start () {
|
public class CustomNodeGraphEditorAttribute : Attribute, XNodeEditor.Internal.INodeEditorAttrib {
|
||||||
|
private Type inspectedType;
|
||||||
|
public string editorPrefsKey;
|
||||||
|
/// <summary> Tells a NodeGraphEditor which Graph type it is an editor for </summary>
|
||||||
|
/// <param name="inspectedType">Type that this editor can edit</param>
|
||||||
|
/// <param name="editorPrefsKey">Define unique key for unique layout settings instance</param>
|
||||||
|
public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") {
|
||||||
|
this.inspectedType = inspectedType;
|
||||||
|
this.editorPrefsKey = editorPrefsKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type GetInspectedType() {
|
||||||
|
return inspectedType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Update is called once per frame
|
|
||||||
void Update () {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,14 +6,18 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
/// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary>
|
/// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary>
|
||||||
|
[CustomNodeEditor(typeof(XNode.Node))]
|
||||||
|
public class NodeEditor : Editor, INodeEditor {
|
||||||
|
public new XNode.Node target { get { return _target != null? _target : _target = base.target as XNode.Node; } }
|
||||||
|
private XNode.Node _target;
|
||||||
|
|
||||||
[CustomNodeEditor(typeof(XNode.INode))]
|
/// <summary> Fires whenever a node was modified through the editor </summary>
|
||||||
public class NodeEditor : XNodeEditor.Internal.NodeEditorBase<NodeEditor, NodeEditor.CustomNodeEditorAttribute, XNode.Node> {
|
|
||||||
|
|
||||||
/// <summary> Fires every whenever a node was modified through the editor </summary>
|
|
||||||
public static Action<XNode.INode> onUpdateNode;
|
|
||||||
public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>();
|
public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>();
|
||||||
|
|
||||||
|
#region Interface implementation
|
||||||
|
Editor INodeEditor.editor { get { return this; } }
|
||||||
|
#endregion
|
||||||
|
|
||||||
public new virtual void OnHeaderGUI() {
|
public new virtual void OnHeaderGUI() {
|
||||||
GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
|
GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
|
||||||
}
|
}
|
||||||
@ -89,20 +93,5 @@ namespace XNodeEditor {
|
|||||||
target.name = newName;
|
target.name = newName;
|
||||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public class CustomNodeEditorAttribute : Attribute,
|
|
||||||
XNodeEditor.Internal.INodeEditorAttrib {
|
|
||||||
private Type inspectedType;
|
|
||||||
/// <summary> Tells a NodeEditor which Node type it is an editor for </summary>
|
|
||||||
/// <param name="inspectedType">Type that this editor can edit</param>
|
|
||||||
public CustomNodeEditorAttribute(Type inspectedType) {
|
|
||||||
this.inspectedType = inspectedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type GetInspectedType() {
|
|
||||||
return inspectedType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using XNodeEditor.Internal;
|
||||||
|
|
||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
public partial class NodeEditorWindow {
|
public partial class NodeEditorWindow {
|
||||||
@ -160,7 +161,6 @@ namespace XNodeEditor {
|
|||||||
hoveredPort.Disconnect(output);
|
hoveredPort.Disconnect(output);
|
||||||
draggedOutput = output;
|
draggedOutput = output;
|
||||||
draggedOutputTarget = hoveredPort;
|
draggedOutputTarget = hoveredPort;
|
||||||
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
||||||
@ -215,7 +215,6 @@ namespace XNodeEditor {
|
|||||||
int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget);
|
int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget);
|
||||||
if (connectionIndex != -1) {
|
if (connectionIndex != -1) {
|
||||||
draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
|
draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
|
||||||
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
|
|
||||||
EditorUtility.SetDirty((UnityEngine.Object) graph);
|
EditorUtility.SetDirty((UnityEngine.Object) graph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,7 +270,7 @@ namespace XNodeEditor {
|
|||||||
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
} else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
|
||||||
if (!Selection.Contains((UnityEngine.Object) hoveredNode)) SelectNode(hoveredNode, false);
|
if (!Selection.Contains((UnityEngine.Object) hoveredNode)) SelectNode(hoveredNode, false);
|
||||||
GenericMenu menu = new GenericMenu();
|
GenericMenu menu = new GenericMenu();
|
||||||
NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu);
|
hoveredNode.GetNodeEditor().AddContextMenuItems(menu);
|
||||||
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||||
e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places.
|
e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places.
|
||||||
} else if (!IsHoveringNode) {
|
} else if (!IsHoveringNode) {
|
||||||
@ -387,8 +386,8 @@ namespace XNodeEditor {
|
|||||||
if (srcNode.Graph != graph) continue; // ignore nodes selected in another graph
|
if (srcNode.Graph != graph) continue; // ignore nodes selected in another graph
|
||||||
XNode.INode newNode = graphEditor.CopyNode(srcNode);
|
XNode.INode newNode = graphEditor.CopyNode(srcNode);
|
||||||
substitutes.Add(srcNode, newNode);
|
substitutes.Add(srcNode, newNode);
|
||||||
newnode.Position = srcnode.Position + new Vector2(30, 30);
|
newNode.Position = srcNode.Position + new Vector2(30, 30);
|
||||||
newNodes[i] = newNode;
|
newNodes[i] = newNode as UnityEngine.Object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +395,7 @@ namespace XNodeEditor {
|
|||||||
for (int i = 0; i < Selection.objects.Length; i++) {
|
for (int i = 0; i < Selection.objects.Length; i++) {
|
||||||
if (Selection.objects[i] is XNode.INode) {
|
if (Selection.objects[i] is XNode.INode) {
|
||||||
XNode.INode srcNode = Selection.objects[i] as XNode.INode;
|
XNode.INode srcNode = Selection.objects[i] as XNode.INode;
|
||||||
if (srcNode.graph != graph) continue; // ignore nodes selected in another graph
|
if (srcNode.Graph != graph) continue; // ignore nodes selected in another graph
|
||||||
foreach (XNode.NodePort port in srcNode.Ports) {
|
foreach (XNode.NodePort port in srcNode.Ports) {
|
||||||
for (int c = 0; c < port.ConnectionCount; c++) {
|
for (int c = 0; c < port.ConnectionCount; c++) {
|
||||||
XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c);
|
XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c);
|
||||||
|
|||||||
@ -27,9 +27,9 @@ namespace XNodeEditor {
|
|||||||
for (int k = 0; k < objs.Length; k++) {
|
for (int k = 0; k < objs.Length; k++) {
|
||||||
XNode.INode node = objs[k] as XNode.INode;
|
XNode.INode node = objs[k] as XNode.INode;
|
||||||
if (node.GetType () == scriptType) {
|
if (node.GetType () == scriptType) {
|
||||||
if (node != null && node.graph != null) {
|
if (node != null && node.Graph != null) {
|
||||||
// Delete the node and notify the user
|
// Delete the node and notify the user
|
||||||
Debug.LogWarning (node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
|
Debug.LogWarning (node.Name + " of " + node.Graph + " depended on deleted script and has been removed automatically.", node.Graph);
|
||||||
node.graph.RemoveNode (node);
|
node.graph.RemoveNode (node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace XNodeEditor.Internal {
|
|
||||||
/// <summary> Handles caching of custom editor classes and their target types. Accessible with GetEditor(Type type) </summary>
|
|
||||||
/// <typeparam name="T">Editor Type. Should be the type of the deriving script itself (eg. NodeEditor) </typeparam>
|
|
||||||
/// <typeparam name="A">Attribute Type. The attribute used to connect with the runtime type (eg. CustomNodeEditorAttribute) </typeparam>
|
|
||||||
/// <typeparam name="K">Runtime Type. The Object this can be an editor for (eg. INode ) </typeparam>
|
|
||||||
public abstract class NodeEditorBase<T, A, K> : Editor where T : NodeEditorBase<T, A, K>, ICustomEditor<K> where A : Attribute, INodeEditorAttrib where K : class {
|
|
||||||
/// <summary> Custom editors defined with [CustomNodeEditor] </summary>
|
|
||||||
private static Dictionary<Type, Type> editorTypes;
|
|
||||||
private static Dictionary<K, T> editors = new Dictionary<K, T>();
|
|
||||||
public NodeEditorWindow window;
|
|
||||||
public new K target { get { return _target as UnityEngine.Object == base.target ? _target : _target = base.target as K; } set { base.target = value as UnityEngine.Object; } }
|
|
||||||
private K _target;
|
|
||||||
|
|
||||||
public static T GetEditor<Q>(Q target, NodeEditorWindow window) where Q : class {
|
|
||||||
if ((target as UnityEngine.Object) == null) return default(T);
|
|
||||||
T editor;
|
|
||||||
if (!editors.TryGetValue(target as K, out editor)) {
|
|
||||||
Type type = target.GetType();
|
|
||||||
Type editorType = GetEditorType(type);
|
|
||||||
editor = (T) Editor.CreateEditor(target as UnityEngine.Object, editorType);
|
|
||||||
editor.window = window;
|
|
||||||
editors.Add(target as K, editor);
|
|
||||||
}
|
|
||||||
if (editor.target == null) editor.Initialize(new UnityEngine.Object[] { target as UnityEngine.Object });
|
|
||||||
if (editor.window != window) editor.window = window;
|
|
||||||
return editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type GetEditorType(Type type) {
|
|
||||||
if (type == null) return null;
|
|
||||||
if (editorTypes == null) CacheCustomEditors();
|
|
||||||
Type result;
|
|
||||||
if (editorTypes.TryGetValue(type, out result)) return result;
|
|
||||||
//If type isn't found, try base type
|
|
||||||
return GetEditorType(type.BaseType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CacheCustomEditors() {
|
|
||||||
editorTypes = new Dictionary<Type, Type>();
|
|
||||||
|
|
||||||
//Get all classes deriving from NodeEditor via reflection
|
|
||||||
Type[] nodeEditors = XNodeEditor.NodeEditorWindow.GetDerivedTypes(typeof(T));
|
|
||||||
for (int i = 0; i < nodeEditors.Length; i++) {
|
|
||||||
if (nodeEditors[i].IsAbstract) continue;
|
|
||||||
var attribs = nodeEditors[i].GetCustomAttributes(typeof(A), false);
|
|
||||||
if (attribs == null || attribs.Length == 0) continue;
|
|
||||||
A attrib = attribs[0] as A;
|
|
||||||
editorTypes.Add(attrib.GetInspectedType(), nodeEditors[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface INodeEditorAttrib {
|
|
||||||
Type GetInspectedType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
73
Scripts/Editor/NodeEditorExtensions.cs
Normal file
73
Scripts/Editor/NodeEditorExtensions.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
using XNode;
|
||||||
|
|
||||||
|
namespace XNodeEditor.Internal {
|
||||||
|
/// <summary> Handles caching of custom editor classes and their target types. Accessible with GetEditor(Type type) </summary>
|
||||||
|
public static class NodeEditorExtensions {
|
||||||
|
/// <summary> Custom editors defined with [CustomNodeEditor] </summary>
|
||||||
|
private static Dictionary<Type, Type> nodeEditorTypes;
|
||||||
|
/// <summary> Custom editors defined with [CustomGraphEditor] </summary>
|
||||||
|
private static Dictionary<Type, Type> graphEditorTypes;
|
||||||
|
private static Dictionary<Object, INodeEditor> nodeEditors = new Dictionary<Object, INodeEditor>();
|
||||||
|
private static Dictionary<Object, INodeGraphEditor> graphEditors = new Dictionary<Object, INodeGraphEditor>();
|
||||||
|
|
||||||
|
public static INodeGraphEditor GetGraphEditor(this INodeGraph target, NodeEditorWindow window) {
|
||||||
|
INodeGraphEditor graphEditor = GetEditor(target.Object, graphEditors);
|
||||||
|
if (graphEditor.window != window) graphEditor.window = window;
|
||||||
|
return graphEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static INodeEditor GetNodeEditor(this INode target) {
|
||||||
|
INodeEditor nodeEditor = GetEditor(target.Object, nodeEditors);
|
||||||
|
return nodeEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T GetEditor<T>(UnityEngine.Object target, Dictionary<Object, T> editors) where T : class {
|
||||||
|
if (target == null) return null;
|
||||||
|
T tEditor;
|
||||||
|
if (!editors.TryGetValue(target, out tEditor)) {
|
||||||
|
Type editorType = GetEditorType(target.GetType());
|
||||||
|
tEditor = Editor.CreateEditor(target, editorType) as T;
|
||||||
|
editors.Add(target, tEditor);
|
||||||
|
}
|
||||||
|
Editor editor = tEditor as Editor;
|
||||||
|
if (editor.target == null) editor.Initialize(new UnityEngine.Object[] { target });
|
||||||
|
return tEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type GetEditorType(Type type) {
|
||||||
|
if (type == null) return null;
|
||||||
|
if (graphEditorTypes == null) graphEditorTypes = CacheCustomEditors<CustomNodeGraphEditorAttribute>(typeof(INodeGraphEditor));
|
||||||
|
if (nodeEditorTypes == null) nodeEditorTypes = CacheCustomEditors<CustomNodeEditorAttribute>(typeof(INodeEditor));
|
||||||
|
Type result;
|
||||||
|
if (graphEditorTypes.TryGetValue(type, out result)) return result;
|
||||||
|
//If type isn't found, try base type
|
||||||
|
return GetEditorType(type.BaseType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<Type, Type> CacheCustomEditors<A>(Type editorInterface) where A : Attribute, INodeEditorAttrib {
|
||||||
|
Dictionary<Type, Type> dict = new Dictionary<Type, Type>();
|
||||||
|
|
||||||
|
//Get all classes deriving from editorInterface via reflection
|
||||||
|
Type[] editors = XNodeEditor.NodeEditorWindow.GetDerivedTypes(editorInterface);
|
||||||
|
for (int i = 0; i < editors.Length; i++) {
|
||||||
|
if (editors[i].IsAbstract) continue;
|
||||||
|
object[] attribs = editors[i].GetCustomAttributes(typeof(A), false);
|
||||||
|
if (attribs == null || attribs.Length == 0) continue;
|
||||||
|
A attrib = attribs[0] as A;
|
||||||
|
dict.Add(attrib.GetInspectedType(), editors[i]);
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface INodeEditorAttrib {
|
||||||
|
Type GetInspectedType();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,13 +3,14 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using XNodeEditor.Internal;
|
||||||
|
|
||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
/// <summary> Contains GUI methods </summary>
|
/// <summary> Contains GUI methods </summary>
|
||||||
public partial class NodeEditorWindow {
|
public partial class NodeEditorWindow {
|
||||||
public NodeGraphEditor graphEditor;
|
public INodeGraphEditor graphEditor;
|
||||||
private List<UnityEngine.Object> selectionCache;
|
private List<UnityEngine.Object> selectionCache;
|
||||||
private List<XNode.Node> culledNodes;
|
private List<XNode.INode> culledNodes;
|
||||||
private int topPadding { get { return isDocked() ? 19 : 22; } }
|
private int topPadding { get { return isDocked() ? 19 : 22; } }
|
||||||
/// <summary> Executed after all other window GUI. Useful if Zoom is ruining your day. Automatically resets after being run.</summary>
|
/// <summary> Executed after all other window GUI. Useful if Zoom is ruining your day. Automatically resets after being run.</summary>
|
||||||
public event Action onLateGUI;
|
public event Action onLateGUI;
|
||||||
@ -136,8 +137,7 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
p = new Vector2(-p.y, p.x) * Mathf.Sign(side) * tangentLength;
|
p = new Vector2(-p.y, p.x) * Mathf.Sign(side) * tangentLength;
|
||||||
inputTangent = p;
|
inputTangent = p;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
inputTangent = Vector2.left * Vector2.Distance(windowPoints[i], windowPoints[i + 1]) * 0.01f * zoom;
|
inputTangent = Vector2.left * Vector2.Distance(windowPoints[i], windowPoints[i + 1]) * 0.01f * zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ namespace XNodeEditor {
|
|||||||
hoveredReroute = new RerouteReference();
|
hoveredReroute = new RerouteReference();
|
||||||
|
|
||||||
Color col = GUI.color;
|
Color col = GUI.color;
|
||||||
foreach (XNode.Node node in graph.nodes) {
|
foreach (XNode.Node node in graph.Nodes) {
|
||||||
//If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset.
|
//If 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;
|
if (node == null) continue;
|
||||||
|
|
||||||
@ -279,17 +279,18 @@ namespace XNodeEditor {
|
|||||||
//Save guiColor so we can revert it
|
//Save guiColor so we can revert it
|
||||||
Color guiColor = GUI.color;
|
Color guiColor = GUI.color;
|
||||||
|
|
||||||
if (e.type == EventType.Layout) culledNodes = new List<XNode.Node>();
|
if (e.type == EventType.Layout) culledNodes = new List<XNode.INode>();
|
||||||
for (int n = 0; n < graph.nodes.Count; n++) {
|
|
||||||
|
for (int n = 0; n < graph.Nodes.Count(); n++) {
|
||||||
|
if (n >= graph.Nodes.Count()) return;
|
||||||
|
XNode.INode node = graph.Nodes.ElementAt(n);
|
||||||
// Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable.
|
// Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable.
|
||||||
if (graph.nodes[n] == null) continue;
|
if (node == null) continue;
|
||||||
if (n >= graph.nodes.Count) return;
|
|
||||||
XNode.Node node = graph.nodes[n];
|
|
||||||
|
|
||||||
// Culling
|
// Culling
|
||||||
if (e.type == EventType.Layout) {
|
if (e.type == EventType.Layout) {
|
||||||
// Cull unselected nodes outside view
|
// Cull unselected nodes outside view
|
||||||
if (!Selection.Contains(node) && ShouldBeCulled(node)) {
|
if (!Selection.Contains(node.Object) && ShouldBeCulled(node)) {
|
||||||
culledNodes.Add(node);
|
culledNodes.Add(node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -299,7 +300,7 @@ namespace XNodeEditor {
|
|||||||
_portConnectionPoints = _portConnectionPoints.Where(x => x.Key.node != node).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
_portConnectionPoints = _portConnectionPoints.Where(x => x.Key.node != node).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeEditor nodeEditor = NodeEditor.GetEditor(node, this);
|
INodeEditor nodeEditor = node.GetNodeEditor();
|
||||||
|
|
||||||
NodeEditor.portPositions.Clear();
|
NodeEditor.portPositions.Clear();
|
||||||
|
|
||||||
@ -308,7 +309,7 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000)));
|
GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000)));
|
||||||
|
|
||||||
bool selected = selectionCache.Contains(graph.nodes[n]);
|
bool selected = selectionCache.Contains(graph.Nodes.ElementAt(n).Object);
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
GUIStyle style = new GUIStyle(nodeEditor.GetBodyStyle());
|
GUIStyle style = new GUIStyle(nodeEditor.GetBodyStyle());
|
||||||
@ -335,7 +336,7 @@ namespace XNodeEditor {
|
|||||||
//If user changed a value, notify other scripts through onUpdateNode
|
//If user changed a value, notify other scripts through onUpdateNode
|
||||||
if (EditorGUI.EndChangeCheck()) {
|
if (EditorGUI.EndChangeCheck()) {
|
||||||
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
|
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
|
||||||
EditorUtility.SetDirty(node);
|
EditorUtility.SetDirty(node.Object);
|
||||||
nodeEditor.serializedObject.ApplyModifiedProperties();
|
nodeEditor.serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +366,7 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
//If dragging a selection box, add nodes inside to selection
|
//If dragging a selection box, add nodes inside to selection
|
||||||
if (currentActivity == NodeActivity.DragGrid) {
|
if (currentActivity == NodeActivity.DragGrid) {
|
||||||
if (windowRect.Overlaps(selectionBox)) preSelection.Add(node);
|
if (windowRect.Overlaps(selectionBox)) preSelection.Add(node.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if we are hovering any of this nodes ports
|
//Check if we are hovering any of this nodes ports
|
||||||
@ -397,8 +398,7 @@ namespace XNodeEditor {
|
|||||||
if (onValidate != null && EditorGUI.EndChangeCheck()) onValidate.Invoke(Selection.activeObject, null);
|
if (onValidate != null && EditorGUI.EndChangeCheck()) onValidate.Invoke(Selection.activeObject, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldBeCulled(XNode.Node node) {
|
private bool ShouldBeCulled(XNode.INode node) {
|
||||||
|
|
||||||
Vector2 nodePos = GridToWindowPositionNoClipped(node.Position);
|
Vector2 nodePos = GridToWindowPositionNoClipped(node.Position);
|
||||||
if (nodePos.x / _zoom > position.width) return true; // Right
|
if (nodePos.x / _zoom > position.width) return true; // Right
|
||||||
else if (nodePos.y / _zoom > position.height) return true; // Bottom
|
else if (nodePos.y / _zoom > position.height) return true; // Bottom
|
||||||
|
|||||||
@ -48,8 +48,8 @@ namespace XNodeEditor {
|
|||||||
// If property is an input, display a regular property field and put a port handle on the left side
|
// If property is an input, display a regular property field and put a port handle on the left side
|
||||||
if (port.direction == XNode.NodePort.IO.Input) {
|
if (port.direction == XNode.NodePort.IO.Input) {
|
||||||
// Get data from [Input] attribute
|
// Get data from [Input] attribute
|
||||||
XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected;
|
XNode.ShowBackingValue showBacking = XNode.ShowBackingValue.Unconnected;
|
||||||
XNode.Node.InputAttribute inputAttribute;
|
XNode.InputAttribute inputAttribute;
|
||||||
bool dynamicPortList = false;
|
bool dynamicPortList = false;
|
||||||
if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) {
|
if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) {
|
||||||
dynamicPortList = inputAttribute.dynamicPortList;
|
dynamicPortList = inputAttribute.dynamicPortList;
|
||||||
@ -58,8 +58,8 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
//Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField
|
//Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField
|
||||||
bool useLayoutSpace = dynamicPortList ||
|
bool useLayoutSpace = dynamicPortList ||
|
||||||
showBacking == XNode.Node.ShowBackingValue.Never ||
|
showBacking == XNode.ShowBackingValue.Never ||
|
||||||
(showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected);
|
(showBacking == XNode.ShowBackingValue.Unconnected && port.IsConnected);
|
||||||
if (spacePadding > 0 && useLayoutSpace) {
|
if (spacePadding > 0 && useLayoutSpace) {
|
||||||
GUILayout.Space(spacePadding);
|
GUILayout.Space(spacePadding);
|
||||||
spacePadding = 0;
|
spacePadding = 0;
|
||||||
@ -67,22 +67,22 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
if (dynamicPortList) {
|
if (dynamicPortList) {
|
||||||
Type type = GetType(property);
|
Type type = GetType(property);
|
||||||
XNode.Node.ConnectionType connectionType = inputAttribute != null ? inputAttribute.connectionType : XNode.Node.ConnectionType.Multiple;
|
XNode.ConnectionType connectionType = inputAttribute != null ? inputAttribute.connectionType : XNode.ConnectionType.Multiple;
|
||||||
DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType);
|
DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (showBacking) {
|
switch (showBacking) {
|
||||||
case XNode.Node.ShowBackingValue.Unconnected:
|
case XNode.ShowBackingValue.Unconnected:
|
||||||
// Display a label if port is connected
|
// Display a label if port is connected
|
||||||
if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName));
|
if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName));
|
||||||
// Display an editable property field if port is not connected
|
// Display an editable property field if port is not connected
|
||||||
else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
||||||
break;
|
break;
|
||||||
case XNode.Node.ShowBackingValue.Never:
|
case XNode.ShowBackingValue.Never:
|
||||||
// Display a label
|
// Display a label
|
||||||
EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName));
|
EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName));
|
||||||
break;
|
break;
|
||||||
case XNode.Node.ShowBackingValue.Always:
|
case XNode.ShowBackingValue.Always:
|
||||||
// Display an editable property field
|
// Display an editable property field
|
||||||
EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
||||||
break;
|
break;
|
||||||
@ -93,8 +93,8 @@ namespace XNodeEditor {
|
|||||||
// If property is an output, display a text label and put a port handle on the right side
|
// If property is an output, display a text label and put a port handle on the right side
|
||||||
} else if (port.direction == XNode.NodePort.IO.Output) {
|
} else if (port.direction == XNode.NodePort.IO.Output) {
|
||||||
// Get data from [Output] attribute
|
// Get data from [Output] attribute
|
||||||
XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected;
|
XNode.ShowBackingValue showBacking = XNode.ShowBackingValue.Unconnected;
|
||||||
XNode.Node.OutputAttribute outputAttribute;
|
XNode.OutputAttribute outputAttribute;
|
||||||
bool dynamicPortList = false;
|
bool dynamicPortList = false;
|
||||||
if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) {
|
if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) {
|
||||||
dynamicPortList = outputAttribute.dynamicPortList;
|
dynamicPortList = outputAttribute.dynamicPortList;
|
||||||
@ -103,8 +103,8 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
//Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField
|
//Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField
|
||||||
bool useLayoutSpace = dynamicPortList ||
|
bool useLayoutSpace = dynamicPortList ||
|
||||||
showBacking == XNode.Node.ShowBackingValue.Never ||
|
showBacking == XNode.ShowBackingValue.Never ||
|
||||||
(showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected);
|
(showBacking == XNode.ShowBackingValue.Unconnected && port.IsConnected);
|
||||||
if (spacePadding > 0 && useLayoutSpace) {
|
if (spacePadding > 0 && useLayoutSpace) {
|
||||||
GUILayout.Space(spacePadding);
|
GUILayout.Space(spacePadding);
|
||||||
spacePadding = 0;
|
spacePadding = 0;
|
||||||
@ -112,22 +112,22 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
if (dynamicPortList) {
|
if (dynamicPortList) {
|
||||||
Type type = GetType(property);
|
Type type = GetType(property);
|
||||||
XNode.Node.ConnectionType connectionType = outputAttribute != null ? outputAttribute.connectionType : XNode.Node.ConnectionType.Multiple;
|
XNode.ConnectionType connectionType = outputAttribute != null ? outputAttribute.connectionType : XNode.ConnectionType.Multiple;
|
||||||
DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType);
|
DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (showBacking) {
|
switch (showBacking) {
|
||||||
case XNode.Node.ShowBackingValue.Unconnected:
|
case XNode.ShowBackingValue.Unconnected:
|
||||||
// Display a label if port is connected
|
// Display a label if port is connected
|
||||||
if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30));
|
if (port.IsConnected) EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30));
|
||||||
// Display an editable property field if port is not connected
|
// Display an editable property field if port is not connected
|
||||||
else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
else EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
||||||
break;
|
break;
|
||||||
case XNode.Node.ShowBackingValue.Never:
|
case XNode.ShowBackingValue.Never:
|
||||||
// Display a label
|
// Display a label
|
||||||
EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30));
|
EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30));
|
||||||
break;
|
break;
|
||||||
case XNode.Node.ShowBackingValue.Always:
|
case XNode.ShowBackingValue.Always:
|
||||||
// Display an editable property field
|
// Display an editable property field
|
||||||
EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30));
|
||||||
break;
|
break;
|
||||||
@ -258,7 +258,7 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Use DynamicPortList instead")]
|
[Obsolete("Use DynamicPortList instead")]
|
||||||
public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action<ReorderableList> onCreation = null) {
|
public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.ConnectionType connectionType = XNode.ConnectionType.Multiple, XNode.TypeConstraint typeConstraint = XNode.TypeConstraint.None, Action<ReorderableList> onCreation = null) {
|
||||||
DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation);
|
DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@ -281,7 +281,7 @@ namespace XNodeEditor {
|
|||||||
/// <param name="serializedObject">The serializedObject of the node</param>
|
/// <param name="serializedObject">The serializedObject of the node</param>
|
||||||
/// <param name="connectionType">Connection type of added dynamic ports</param>
|
/// <param name="connectionType">Connection type of added dynamic ports</param>
|
||||||
/// <param name="onCreation">Called on the list on creation. Use this if you want to customize the created ReorderableList</param>
|
/// <param name="onCreation">Called on the list on creation. Use this if you want to customize the created ReorderableList</param>
|
||||||
public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action<ReorderableList> onCreation = null) {
|
public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.ConnectionType connectionType = XNode.ConnectionType.Multiple, XNode.TypeConstraint typeConstraint = XNode.TypeConstraint.None, Action<ReorderableList> onCreation = null) {
|
||||||
XNode.INode node = serializedObject.targetObject as XNode.INode;
|
XNode.INode node = serializedObject.targetObject as XNode.INode;
|
||||||
|
|
||||||
var indexedPorts = node.DynamicPorts.Select(x => {
|
var indexedPorts = node.DynamicPorts.Select(x => {
|
||||||
@ -312,7 +312,7 @@ namespace XNodeEditor {
|
|||||||
list.DoLayoutList();
|
list.DoLayoutList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReorderableList CreateReorderableList(string fieldName, List<XNode.NodePort> dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action<ReorderableList> onCreation) {
|
private static ReorderableList CreateReorderableList(string fieldName, List<XNode.NodePort> dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.ConnectionType connectionType, XNode.TypeConstraint typeConstraint, Action<ReorderableList> onCreation) {
|
||||||
bool hasArrayData = arrayData != null && arrayData.isArray;
|
bool hasArrayData = arrayData != null && arrayData.isArray;
|
||||||
XNode.INode node = serializedObject.targetObject as XNode.INode;
|
XNode.INode node = serializedObject.targetObject as XNode.INode;
|
||||||
ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true);
|
ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true);
|
||||||
@ -402,7 +402,7 @@ namespace XNodeEditor {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
while (node.HasPort(newName)) newName = fieldName + " " + (++i);
|
while (node.HasPort(newName)) newName = fieldName + " " + (++i);
|
||||||
|
|
||||||
if (io == XNode.NodePort.IO.Output) node.AddDynamicPort(type, XNode.NodePort.IO.Output, connectionType, XNode.Node.TypeConstraint.None, newName);
|
if (io == XNode.NodePort.IO.Output) node.AddDynamicPort(type, XNode.NodePort.IO.Output, connectionType, XNode.TypeConstraint.None, newName);
|
||||||
else node.AddDynamicPort(type, XNode.NodePort.IO.Input, connectionType, typeConstraint, newName);
|
else node.AddDynamicPort(type, XNode.NodePort.IO.Input, connectionType, typeConstraint, newName);
|
||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
EditorUtility.SetDirty((UnityEngine.Object) node);
|
EditorUtility.SetDirty((UnityEngine.Object) node);
|
||||||
|
|||||||
@ -61,9 +61,9 @@ namespace XNodeEditor {
|
|||||||
public static Dictionary<Type, Color> GetNodeTint() {
|
public static Dictionary<Type, Color> GetNodeTint() {
|
||||||
Dictionary<Type, Color> tints = new Dictionary<Type, Color>();
|
Dictionary<Type, Color> tints = new Dictionary<Type, Color>();
|
||||||
for (int i = 0; i < nodeTypes.Length; i++) {
|
for (int i = 0; i < nodeTypes.Length; i++) {
|
||||||
var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.INode.NodeTintAttribute), true);
|
var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.NodeTintAttribute), true);
|
||||||
if (attribs == null || attribs.Length == 0) continue;
|
if (attribs == null || attribs.Length == 0) continue;
|
||||||
XNode.INode.NodeTintAttribute attrib = attribs[0] as XNode.INode.NodeTintAttribute;
|
XNode.NodeTintAttribute attrib = attribs[0] as XNode.NodeTintAttribute;
|
||||||
tints.Add(nodeTypes[i], attrib.color);
|
tints.Add(nodeTypes[i], attrib.color);
|
||||||
}
|
}
|
||||||
return tints;
|
return tints;
|
||||||
@ -72,9 +72,9 @@ namespace XNodeEditor {
|
|||||||
public static Dictionary<Type, int> GetNodeWidth() {
|
public static Dictionary<Type, int> GetNodeWidth() {
|
||||||
Dictionary<Type, int> widths = new Dictionary<Type, int>();
|
Dictionary<Type, int> widths = new Dictionary<Type, int>();
|
||||||
for (int i = 0; i < nodeTypes.Length; i++) {
|
for (int i = 0; i < nodeTypes.Length; i++) {
|
||||||
var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.INode.NodeWidthAttribute), true);
|
var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.NodeWidthAttribute), true);
|
||||||
if (attribs == null || attribs.Length == 0) continue;
|
if (attribs == null || attribs.Length == 0) continue;
|
||||||
XNode.INode.NodeWidthAttribute attrib = attribs[0] as XNode.INode.NodeWidthAttribute;
|
XNode.NodeWidthAttribute attrib = attribs[0] as XNode.NodeWidthAttribute;
|
||||||
widths.Add(nodeTypes[i], attrib.width);
|
widths.Add(nodeTypes[i], attrib.width);
|
||||||
}
|
}
|
||||||
return widths;
|
return widths;
|
||||||
@ -96,7 +96,7 @@ namespace XNodeEditor {
|
|||||||
foreach (Assembly assembly in assemblies) {
|
foreach (Assembly assembly in assemblies) {
|
||||||
try {
|
try {
|
||||||
types.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
|
types.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
|
||||||
} catch(ReflectionTypeLoadException) {}
|
} catch (ReflectionTypeLoadException) { }
|
||||||
}
|
}
|
||||||
return types.ToArray();
|
return types.ToArray();
|
||||||
}
|
}
|
||||||
@ -183,4 +183,4 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Callbacks;
|
using UnityEditor.Callbacks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using XNodeEditor.Internal;
|
||||||
|
|
||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
[InitializeOnLoad]
|
[InitializeOnLoad]
|
||||||
@ -79,14 +80,15 @@ namespace XNodeEditor {
|
|||||||
/// <summary> Handle Selection Change events</summary>
|
/// <summary> Handle Selection Change events</summary>
|
||||||
private static void OnSelectionChanged() {
|
private static void OnSelectionChanged() {
|
||||||
XNode.INodeGraph nodeGraph = Selection.activeObject as XNode.INodeGraph;
|
XNode.INodeGraph nodeGraph = Selection.activeObject as XNode.INodeGraph;
|
||||||
if (nodeGraph && !AssetDatabase.Contains(nodeGraph)) {
|
// Open if double clicked non-asset graph (eg a runtime cloned graph)
|
||||||
|
if (nodeGraph != null && !AssetDatabase.Contains(nodeGraph.Object)) {
|
||||||
Open(nodeGraph);
|
Open(nodeGraph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Make sure the graph editor is assigned and to the right object </summary>
|
/// <summary> Make sure the graph editor is assigned and to the right object </summary>
|
||||||
private void ValidateGraphEditor() {
|
private void ValidateGraphEditor() {
|
||||||
NodeGraphEditor graphEditor = NodeGraphEditor.GetEditor(graph, this);
|
INodeGraphEditor graphEditor = graph.GetGraphEditor(this);
|
||||||
if (this.graphEditor != graphEditor) {
|
if (this.graphEditor != graphEditor) {
|
||||||
this.graphEditor = graphEditor;
|
this.graphEditor = graphEditor;
|
||||||
graphEditor.OnOpen();
|
graphEditor.OnOpen();
|
||||||
@ -102,25 +104,6 @@ namespace XNodeEditor {
|
|||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save() {
|
|
||||||
if (AssetDatabase.Contains(graph)) {
|
|
||||||
EditorUtility.SetDirty(graph);
|
|
||||||
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
|
||||||
} else SaveAs();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveAs() {
|
|
||||||
string path = EditorUtility.SaveFilePanelInProject("Save NodeGraph", "NewNodeGraph", "asset", "");
|
|
||||||
if (string.IsNullOrEmpty(path)) return;
|
|
||||||
else {
|
|
||||||
XNode.INodeGraph existingGraph = AssetDatabase.LoadAssetAtPath<XNode.INodeGraph>(path);
|
|
||||||
if (existingGraph != null) AssetDatabase.DeleteAsset(path);
|
|
||||||
AssetDatabase.CreateAsset(graph, path);
|
|
||||||
EditorUtility.SetDirty(graph);
|
|
||||||
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DraggableWindow(int windowID) {
|
private void DraggableWindow(int windowID) {
|
||||||
GUI.DragWindow();
|
GUI.DragWindow();
|
||||||
}
|
}
|
||||||
@ -155,14 +138,14 @@ namespace XNodeEditor {
|
|||||||
public void SelectNode(XNode.INode node, bool add) {
|
public void SelectNode(XNode.INode node, bool add) {
|
||||||
if (add) {
|
if (add) {
|
||||||
List<Object> selection = new List<Object>(Selection.objects);
|
List<Object> selection = new List<Object>(Selection.objects);
|
||||||
selection.Add(node);
|
selection.Add(node.Object);
|
||||||
Selection.objects = selection.ToArray();
|
Selection.objects = selection.ToArray();
|
||||||
} else Selection.objects = new Object[] { node };
|
} else Selection.objects = new Object[] { node.Object };
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeselectNode(XNode.INode node) {
|
public void DeselectNode(XNode.INode node) {
|
||||||
List<Object> selection = new List<Object>(Selection.objects);
|
List<Object> selection = new List<Object>(Selection.objects);
|
||||||
selection.Remove(node);
|
selection.Remove(node.Object);
|
||||||
Selection.objects = selection.ToArray();
|
Selection.objects = selection.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,15 +8,17 @@ using XNode;
|
|||||||
namespace XNodeEditor {
|
namespace XNodeEditor {
|
||||||
/// <summary> Base class to derive custom NodeGraph editors from. Use this to override how graphs are drawn in the editor. </summary>
|
/// <summary> Base class to derive custom NodeGraph editors from. Use this to override how graphs are drawn in the editor. </summary>
|
||||||
[CustomNodeGraphEditor(typeof(XNode.NodeGraph))]
|
[CustomNodeGraphEditor(typeof(XNode.NodeGraph))]
|
||||||
public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase<NodeGraphEditor, NodeGraphEditor.CustomNodeGraphEditorAttribute, XNode.INodeGraph>, ICustomEditor<XNode.INodeGraph>, INodeGraphEditor {
|
public class NodeGraphEditor : Editor, INodeGraphEditor {
|
||||||
|
|
||||||
INodeGraph INodeGraphEditor.target { get { return base.target; } }
|
public NodeEditorWindow window;
|
||||||
|
|
||||||
[Obsolete("Use window.position instead")]
|
[Obsolete("Use window.position instead")]
|
||||||
public Rect position { get { return window.position; } set { window.position = value; } }
|
public Rect position { get { return window.position; } set { window.position = value; } }
|
||||||
|
|
||||||
INodeGraph ICustomEditor<INodeGraph>.Target { get { return target as INodeGraph; } }
|
#region Interface implementation
|
||||||
SerializedObject ICustomEditor<INodeGraph>.SerializedObject { get { return serializedObject; } }
|
NodeEditorWindow INodeGraphEditor.window { get { return window; } set { window = value; } }
|
||||||
|
Editor INodeGraphEditor.editor { get { return this; } }
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary> Are we currently renaming a node? </summary>
|
/// <summary> Are we currently renaming a node? </summary>
|
||||||
protected bool isRenaming;
|
protected bool isRenaming;
|
||||||
@ -42,7 +44,7 @@ namespace XNodeEditor {
|
|||||||
/// <summary> Returns context node menu path. Null or empty strings for hidden nodes. </summary>
|
/// <summary> Returns context node menu path. Null or empty strings for hidden nodes. </summary>
|
||||||
public virtual string GetNodeMenuName(Type type) {
|
public virtual string GetNodeMenuName(Type type) {
|
||||||
//Check if type has the CreateNodeMenuAttribute
|
//Check if type has the CreateNodeMenuAttribute
|
||||||
XNode.Node.CreateNodeMenuAttribute attrib;
|
XNode.CreateNodeMenuAttribute attrib;
|
||||||
if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path
|
if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path
|
||||||
return attrib.menuName;
|
return attrib.menuName;
|
||||||
else // Return generated path
|
else // Return generated path
|
||||||
@ -85,7 +87,7 @@ namespace XNodeEditor {
|
|||||||
if (typeName.EndsWith("Node")) typeName = typeName.Substring(0, typeName.LastIndexOf("Node"));
|
if (typeName.EndsWith("Node")) typeName = typeName.Substring(0, typeName.LastIndexOf("Node"));
|
||||||
node.Name = UnityEditor.ObjectNames.NicifyVariableName(typeName);
|
node.Name = UnityEditor.ObjectNames.NicifyVariableName(typeName);
|
||||||
}
|
}
|
||||||
if (node is ScriptableObject) AssetDatabase.AddObjectToAsset(node as ScriptableObject, target);
|
if (node is ScriptableObject) AssetDatabase.AddObjectToAsset(node as UnityEngine.Object, target as UnityEngine.Object);
|
||||||
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
NodeEditorWindow.RepaintAll();
|
NodeEditorWindow.RepaintAll();
|
||||||
return node;
|
return node;
|
||||||
@ -95,7 +97,7 @@ namespace XNodeEditor {
|
|||||||
public virtual XNode.INode CopyNode(XNode.INode original) {
|
public virtual XNode.INode CopyNode(XNode.INode original) {
|
||||||
XNode.INode node = ((INodeGraph) target).CopyNode(original);
|
XNode.INode node = ((INodeGraph) target).CopyNode(original);
|
||||||
node.Name = original.Name;
|
node.Name = original.Name;
|
||||||
if (node is ScriptableObject) AssetDatabase.AddObjectToAsset(node as ScriptableObject, target);
|
if (node is ScriptableObject) AssetDatabase.AddObjectToAsset(node as UnityEngine.Object, target as UnityEngine.Object);
|
||||||
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|||||||
28
Scripts/Enums.cs
Normal file
28
Scripts/Enums.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
namespace XNode {
|
||||||
|
/// <summary> Used by <see cref="InputAttribute"/> and <see cref="OutputAttribute"/> to determine when to display the field value associated with a <see cref="NodePort"/> </summary>
|
||||||
|
public enum ShowBackingValue {
|
||||||
|
/// <summary> Never show the backing value </summary>
|
||||||
|
Never,
|
||||||
|
/// <summary> Show the backing value only when the port does not have any active connections </summary>
|
||||||
|
Unconnected,
|
||||||
|
/// <summary> Always show the backing value </summary>
|
||||||
|
Always
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ConnectionType {
|
||||||
|
/// <summary> Allow multiple connections</summary>
|
||||||
|
Multiple,
|
||||||
|
/// <summary> always override the current connection </summary>
|
||||||
|
Override
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Tells which types of input to allow </summary>
|
||||||
|
public enum TypeConstraint {
|
||||||
|
/// <summary> Allow all types of input</summary>
|
||||||
|
None,
|
||||||
|
/// <summary> Allow similar and inherited types </summary>
|
||||||
|
Inherited,
|
||||||
|
/// <summary> Allow only similar types </summary>
|
||||||
|
Strict
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Scripts/Enums.cs.meta
Normal file
11
Scripts/Enums.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5624052acb7b18847bca77648c13ab44
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -7,20 +7,21 @@ namespace XNode {
|
|||||||
string Name { get; set; }
|
string Name { get; set; }
|
||||||
INodeGraph Graph { get; }
|
INodeGraph Graph { get; }
|
||||||
Vector2 Position { get; set; }
|
Vector2 Position { get; set; }
|
||||||
object GetValue(NodePort port);
|
UnityEngine.Object Object { get; }
|
||||||
|
object GetValue(INodePort port);
|
||||||
bool HasPort(string fieldName);
|
bool HasPort(string fieldName);
|
||||||
NodePort GetPort(string fieldName);
|
INodePort GetPort(string fieldName);
|
||||||
void UpdateStaticPorts();
|
void UpdateStaticPorts();
|
||||||
IEnumerable<NodePort> Ports { get; }
|
IEnumerable<INodePort> Ports { get; }
|
||||||
IEnumerable<NodePort> Outputs { get; }
|
IEnumerable<INodePort> Outputs { get; }
|
||||||
IEnumerable<NodePort> Inputs { get; }
|
IEnumerable<INodePort> Inputs { get; }
|
||||||
IEnumerable<NodePort> DynamicPorts { get; }
|
IEnumerable<INodePort> DynamicPorts { get; }
|
||||||
NodePort AddDynamicPort(Type type, NodePort.IO direction, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = Node.TypeConstraint.None, string fieldName = null);
|
INodePort AddDynamicPort(Type type, XNode.IO direction, XNode.ConnectionType connectionType, XNode.TypeConstraint typeConstraint, string fieldName);
|
||||||
void RemoveDynamicPort(string fieldName);
|
void RemoveDynamicPort(string fieldName);
|
||||||
NodePort GetInputPort(string fieldName);
|
INodePort GetInputPort(string fieldName);
|
||||||
NodePort GetOutputPort(string fieldName);
|
INodePort GetOutputPort(string fieldName);
|
||||||
void OnCreateConnection(NodePort from, NodePort to);
|
void OnCreateConnection(INodePort from, INodePort to);
|
||||||
void OnRemoveConnection(NodePort port);
|
void OnRemoveConnection(INodePort port);
|
||||||
void ClearConnections();
|
void ClearConnections();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,8 +4,9 @@ using System.Collections.Generic;
|
|||||||
namespace XNode {
|
namespace XNode {
|
||||||
/// <summary> Used by advanced extensions that need to alter the base classes of NodeGraphs </summary>
|
/// <summary> Used by advanced extensions that need to alter the base classes of NodeGraphs </summary>
|
||||||
public interface INodeGraph {
|
public interface INodeGraph {
|
||||||
void MoveNodeToTop(INode node);
|
|
||||||
IEnumerable<INode> Nodes { get; }
|
IEnumerable<INode> Nodes { get; }
|
||||||
|
UnityEngine.Object Object { get; }
|
||||||
|
void MoveNodeToTop(INode node);
|
||||||
INode AddNode(Type type);
|
INode AddNode(Type type);
|
||||||
INode CopyNode(INode original);
|
INode CopyNode(INode original);
|
||||||
void RemoveNode(INode node);
|
void RemoveNode(INode node);
|
||||||
|
|||||||
17
Scripts/Interfaces/INodePort.cs
Normal file
17
Scripts/Interfaces/INodePort.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace XNode {
|
||||||
|
public enum IO { Input, Output }
|
||||||
|
|
||||||
|
public interface INodePort {
|
||||||
|
string fieldName { get; }
|
||||||
|
INode node { get; }
|
||||||
|
List<INodePort> GetConnections();
|
||||||
|
IO direction { get; }
|
||||||
|
ConnectionType connectionType { get; }
|
||||||
|
TypeConstraint typeConstraint { get; }
|
||||||
|
bool dynamic { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Scripts/Interfaces/INodePort.cs.meta
Normal file
11
Scripts/Interfaces/INodePort.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d6d96535423e9e8458c40e17f8d4b70f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
174
Scripts/Node.cs
174
Scripts/Node.cs
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace XNode {
|
namespace XNode {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -24,39 +25,6 @@ namespace XNode {
|
|||||||
/// </example>
|
/// </example>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public abstract class Node : ScriptableObject, XNode.INode {
|
public abstract class Node : ScriptableObject, XNode.INode {
|
||||||
/// <summary> Used by <see cref="InputAttribute"/> and <see cref="OutputAttribute"/> to determine when to display the field value associated with a <see cref="NodePort"/> </summary>
|
|
||||||
public enum ShowBackingValue {
|
|
||||||
/// <summary> Never show the backing value </summary>
|
|
||||||
Never,
|
|
||||||
/// <summary> Show the backing value only when the port does not have any active connections </summary>
|
|
||||||
Unconnected,
|
|
||||||
/// <summary> Always show the backing value </summary>
|
|
||||||
Always
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ConnectionType {
|
|
||||||
/// <summary> Allow multiple connections</summary>
|
|
||||||
Multiple,
|
|
||||||
/// <summary> always override the current connection </summary>
|
|
||||||
Override,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Tells which types of input to allow </summary>
|
|
||||||
public enum TypeConstraint {
|
|
||||||
/// <summary> Allow all types of input</summary>
|
|
||||||
None,
|
|
||||||
/// <summary> Allow similar and inherited types </summary>
|
|
||||||
Inherited,
|
|
||||||
/// <summary> Allow only similar types </summary>
|
|
||||||
Strict,
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface implementation
|
|
||||||
string INode.Name { get { return name; } set { name = value; } }
|
|
||||||
INodeGraph INode.Graph { get { return graph; } }
|
|
||||||
Vector2 INode.Position { get { return position; } set { position = value; } }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Iterate over all ports on this node. </summary>
|
/// <summary> Iterate over all ports on this node. </summary>
|
||||||
public IEnumerable<NodePort> Ports { get { foreach (NodePort port in ports.Values) yield return port; } }
|
public IEnumerable<NodePort> Ports { get { foreach (NodePort port in ports.Values) yield return port; } }
|
||||||
/// <summary> Iterate over all outputs on this node. </summary>
|
/// <summary> Iterate over all outputs on this node. </summary>
|
||||||
@ -98,32 +66,15 @@ namespace XNode {
|
|||||||
/// <summary> Convenience function. </summary>
|
/// <summary> Convenience function. </summary>
|
||||||
/// <seealso cref="AddInstancePort"/>
|
/// <seealso cref="AddInstancePort"/>
|
||||||
/// <seealso cref="AddInstanceOutput"/>
|
/// <seealso cref="AddInstanceOutput"/>
|
||||||
public NodePort AddDynamicInput(Type type, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) {
|
public NodePort AddDynamicInput(Type type, ConnectionType connectionType = ConnectionType.Multiple, TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) {
|
||||||
return ((INode) this).AddDynamicPort(type, NodePort.IO.Input, connectionType, typeConstraint, fieldName);
|
return (NodePort) ((INode) this).AddDynamicPort(type, IO.Input, connectionType, typeConstraint, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Convenience function. </summary>
|
/// <summary> Convenience function. </summary>
|
||||||
/// <seealso cref="AddInstancePort"/>
|
/// <seealso cref="AddInstancePort"/>
|
||||||
/// <seealso cref="AddInstanceInput"/>
|
/// <seealso cref="AddInstanceInput"/>
|
||||||
public NodePort AddDynamicOutput(Type type, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) {
|
public NodePort AddDynamicOutput(Type type, ConnectionType connectionType = ConnectionType.Multiple, TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) {
|
||||||
return ((INode) this).AddDynamicPort(type, NodePort.IO.Output, connectionType, typeConstraint, fieldName);
|
return (NodePort) ((INode) this).AddDynamicPort(type, IO.Output, connectionType, typeConstraint, fieldName);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Add a dynamic, serialized port to this node. </summary>
|
|
||||||
/// <seealso cref="AddDynamicInput"/>
|
|
||||||
/// <seealso cref="AddDynamicOutput"/>
|
|
||||||
NodePort INode.AddDynamicPort(Type type, NodePort.IO direction, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) {
|
|
||||||
if (fieldName == null) {
|
|
||||||
fieldName = "dynamicInput_0";
|
|
||||||
int i = 0;
|
|
||||||
while (HasPort(fieldName)) fieldName = "dynamicInput_" + (++i);
|
|
||||||
} else if (HasPort(fieldName)) {
|
|
||||||
Debug.LogWarning("Port '" + fieldName + "' already exists in " + name, this);
|
|
||||||
return ports[fieldName];
|
|
||||||
}
|
|
||||||
NodePort port = new NodePort(fieldName, type, direction, connectionType, typeConstraint, this);
|
|
||||||
ports.Add(fieldName, port);
|
|
||||||
return port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Remove an dynamic port from the node </summary>
|
/// <summary> Remove an dynamic port from the node </summary>
|
||||||
@ -155,14 +106,14 @@ namespace XNode {
|
|||||||
/// <summary> Returns output port which matches fieldName </summary>
|
/// <summary> Returns output port which matches fieldName </summary>
|
||||||
public NodePort GetOutputPort(string fieldName) {
|
public NodePort GetOutputPort(string fieldName) {
|
||||||
NodePort port = GetPort(fieldName);
|
NodePort port = GetPort(fieldName);
|
||||||
if (port == null || port.direction != NodePort.IO.Output) return null;
|
if (port == null || port.direction != IO.Output) return null;
|
||||||
else return port;
|
else return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Returns input port which matches fieldName </summary>
|
/// <summary> Returns input port which matches fieldName </summary>
|
||||||
public NodePort GetInputPort(string fieldName) {
|
public NodePort GetInputPort(string fieldName) {
|
||||||
NodePort port = GetPort(fieldName);
|
NodePort port = GetPort(fieldName);
|
||||||
if (port == null || port.direction != NodePort.IO.Input) return null;
|
if (port == null || port.direction != IO.Input) return null;
|
||||||
else return port;
|
else return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,93 +169,6 @@ namespace XNode {
|
|||||||
foreach (NodePort port in Ports) port.ClearConnections();
|
foreach (NodePort port in Ports) port.ClearConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Attributes
|
|
||||||
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
|
||||||
public class InputAttribute : Attribute {
|
|
||||||
public ShowBackingValue backingValue;
|
|
||||||
public ConnectionType connectionType;
|
|
||||||
public bool dynamicPortList;
|
|
||||||
public TypeConstraint typeConstraint;
|
|
||||||
|
|
||||||
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
|
|
||||||
/// <param name="backingValue">Should we display the backing value for this port as an editor field? </param>
|
|
||||||
/// <param name="connectionType">Should we allow multiple connections? </param>
|
|
||||||
/// <param name="typeConstraint">Constrains which input connections can be made to this port </param>
|
|
||||||
/// <param name="dynamicPortList">If true, will display a reorderable list of inputs instead of a single port. Will automatically add and display values for lists and arrays </param>
|
|
||||||
public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected, ConnectionType connectionType = ConnectionType.Multiple, TypeConstraint typeConstraint = TypeConstraint.None, bool dynamicPortList = false) {
|
|
||||||
this.backingValue = backingValue;
|
|
||||||
this.connectionType = connectionType;
|
|
||||||
this.dynamicPortList = dynamicPortList;
|
|
||||||
this.typeConstraint = typeConstraint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Mark a serializable field as an output port. You can access this through <see cref="GetOutputPort(string)"/> </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
|
||||||
public class OutputAttribute : Attribute {
|
|
||||||
public ShowBackingValue backingValue;
|
|
||||||
public ConnectionType connectionType;
|
|
||||||
public bool dynamicPortList;
|
|
||||||
|
|
||||||
/// <summary> Mark a serializable field as an output port. You can access this through <see cref="GetOutputPort(string)"/> </summary>
|
|
||||||
/// <param name="backingValue">Should we display the backing value for this port as an editor field? </param>
|
|
||||||
/// <param name="connectionType">Should we allow multiple connections? </param>
|
|
||||||
/// <param name="dynamicPortList">If true, will display a reorderable list of outputs instead of a single port. Will automatically add and display values for lists and arrays </param>
|
|
||||||
public OutputAttribute(ShowBackingValue backingValue = ShowBackingValue.Never, ConnectionType connectionType = ConnectionType.Multiple, bool dynamicPortList = false) {
|
|
||||||
this.backingValue = backingValue;
|
|
||||||
this.connectionType = connectionType;
|
|
||||||
this.dynamicPortList = dynamicPortList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
|
||||||
public class CreateNodeMenuAttribute : Attribute {
|
|
||||||
public string menuName;
|
|
||||||
/// <summary> Manually supply node class with a context menu path </summary>
|
|
||||||
/// <param name="menuName"> Path to this node in the context menu. Null or empty hides it. </param>
|
|
||||||
public CreateNodeMenuAttribute(string menuName) {
|
|
||||||
this.menuName = menuName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
|
||||||
public class NodeTintAttribute : Attribute {
|
|
||||||
public Color color;
|
|
||||||
/// <summary> Specify a color for this node type </summary>
|
|
||||||
/// <param name="r"> Red [0.0f .. 1.0f] </param>
|
|
||||||
/// <param name="g"> Green [0.0f .. 1.0f] </param>
|
|
||||||
/// <param name="b"> Blue [0.0f .. 1.0f] </param>
|
|
||||||
public NodeTintAttribute(float r, float g, float b) {
|
|
||||||
color = new Color(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Specify a color for this node type </summary>
|
|
||||||
/// <param name="hex"> HEX color value </param>
|
|
||||||
public NodeTintAttribute(string hex) {
|
|
||||||
ColorUtility.TryParseHtmlString(hex, out color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Specify a color for this node type </summary>
|
|
||||||
/// <param name="r"> Red [0 .. 255] </param>
|
|
||||||
/// <param name="g"> Green [0 .. 255] </param>
|
|
||||||
/// <param name="b"> Blue [0 .. 255] </param>
|
|
||||||
public NodeTintAttribute(byte r, byte g, byte b) {
|
|
||||||
color = new Color32(r, g, b, byte.MaxValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
|
||||||
public class NodeWidthAttribute : Attribute {
|
|
||||||
public int width;
|
|
||||||
/// <summary> Specify a width for this node type </summary>
|
|
||||||
/// <param name="width"> Width </param>
|
|
||||||
public NodeWidthAttribute(int width) {
|
|
||||||
this.width = width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
[Serializable] private class NodePortDictionary : Dictionary<string, NodePort>, ISerializationCallbackReceiver {
|
[Serializable] private class NodePortDictionary : Dictionary<string, NodePort>, ISerializationCallbackReceiver {
|
||||||
[SerializeField] private List<string> keys = new List<string>();
|
[SerializeField] private List<string> keys = new List<string>();
|
||||||
[SerializeField] private List<NodePort> values = new List<NodePort>();
|
[SerializeField] private List<NodePort> values = new List<NodePort>();
|
||||||
@ -328,5 +192,29 @@ namespace XNode {
|
|||||||
this.Add(keys[i], values[i]);
|
this.Add(keys[i], values[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Interface implementation
|
||||||
|
string INode.Name { get { return name; } set { name = value; } }
|
||||||
|
INodeGraph INode.Graph { get { return graph; } }
|
||||||
|
Vector2 INode.Position { get { return position; } set { position = value; } }
|
||||||
|
Object INode.Object { get { return this; } }
|
||||||
|
|
||||||
|
/// <summary> Add a dynamic, serialized port to this node. </summary>
|
||||||
|
/// <seealso cref="AddDynamicInput"/>
|
||||||
|
/// <seealso cref="AddDynamicOutput"/>
|
||||||
|
INodePort INode.AddDynamicPort(Type type, IO direction, ConnectionType connectionType, TypeConstraint typeConstraint, string fieldName) {
|
||||||
|
if (fieldName == null) {
|
||||||
|
fieldName = "dynamicInput_0";
|
||||||
|
int i = 0;
|
||||||
|
while (HasPort(fieldName)) fieldName = "dynamicInput_" + (++i);
|
||||||
|
} else if (HasPort(fieldName)) {
|
||||||
|
Debug.LogWarning("Port '" + fieldName + "' already exists in " + name, this);
|
||||||
|
return ports[fieldName];
|
||||||
|
}
|
||||||
|
NodePort port = new NodePort(fieldName, type, direction, connectionType, typeConstraint, this);
|
||||||
|
ports.Add(fieldName, port);
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,8 +86,8 @@ namespace XNode {
|
|||||||
|
|
||||||
//Get InputAttribute and OutputAttribute
|
//Get InputAttribute and OutputAttribute
|
||||||
object[] attribs = fieldInfo[i].GetCustomAttributes(true);
|
object[] attribs = fieldInfo[i].GetCustomAttributes(true);
|
||||||
Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute;
|
InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is InputAttribute) as InputAttribute;
|
||||||
Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute;
|
OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is OutputAttribute) as OutputAttribute;
|
||||||
|
|
||||||
if (inputAttrib == null && outputAttrib == null) continue;
|
if (inputAttrib == null && outputAttrib == null) continue;
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ namespace XNode {
|
|||||||
|
|
||||||
#region Interface implementation
|
#region Interface implementation
|
||||||
IEnumerable<INode> INodeGraph.Nodes { get { foreach (Node node in nodes) yield return node; } }
|
IEnumerable<INode> INodeGraph.Nodes { get { foreach (Node node in nodes) yield return node; } }
|
||||||
|
UnityEngine.Object INodeGraph.Object { get { return this; } }
|
||||||
INode INodeGraph.AddNode(Type type) { return AddNode(type); }
|
INode INodeGraph.AddNode(Type type) { return AddNode(type); }
|
||||||
void INodeGraph.MoveNodeToTop(INode node) { MoveNodeToTop(node as Node); }
|
void INodeGraph.MoveNodeToTop(INode node) { MoveNodeToTop(node as Node); }
|
||||||
INode INodeGraph.CopyNode(INode original) { return CopyNode(original as Node); }
|
INode INodeGraph.CopyNode(INode original) { return CopyNode(original as Node); }
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace XNode {
|
namespace XNode {
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class NodePort {
|
public class NodePort : INodePort {
|
||||||
public enum IO { Input, Output }
|
|
||||||
|
|
||||||
public int ConnectionCount { get { return connections.Count; } }
|
public int ConnectionCount { get { return connections.Count; } }
|
||||||
/// <summary> Return the first non-null connection </summary>
|
/// <summary> Return the first non-null connection </summary>
|
||||||
@ -20,8 +20,8 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IO direction { get { return _direction; } }
|
public IO direction { get { return _direction; } }
|
||||||
public Node.ConnectionType connectionType { get { return _connectionType; } }
|
public ConnectionType connectionType { get { return _connectionType; } }
|
||||||
public Node.TypeConstraint typeConstraint { get { return _typeConstraint; } }
|
public TypeConstraint typeConstraint { get { return _typeConstraint; } }
|
||||||
|
|
||||||
/// <summary> Is this port connected to anytihng? </summary>
|
/// <summary> Is this port connected to anytihng? </summary>
|
||||||
public bool IsConnected { get { return connections.Count != 0; } }
|
public bool IsConnected { get { return connections.Count != 0; } }
|
||||||
@ -49,10 +49,23 @@ namespace XNode {
|
|||||||
[SerializeField] private string _typeQualifiedName;
|
[SerializeField] private string _typeQualifiedName;
|
||||||
[SerializeField] private List<PortConnection> connections = new List<PortConnection>();
|
[SerializeField] private List<PortConnection> connections = new List<PortConnection>();
|
||||||
[SerializeField] private IO _direction;
|
[SerializeField] private IO _direction;
|
||||||
[SerializeField] private Node.ConnectionType _connectionType;
|
[SerializeField] private ConnectionType _connectionType;
|
||||||
[SerializeField] private Node.TypeConstraint _typeConstraint;
|
[SerializeField] private TypeConstraint _typeConstraint;
|
||||||
[SerializeField] private bool _dynamic;
|
[SerializeField] private bool _dynamic;
|
||||||
|
|
||||||
|
#region Interface implementation
|
||||||
|
INode INodePort.node { get { return _node; } }
|
||||||
|
List<INodePort> INodePort.GetConnections() {
|
||||||
|
List<INodePort> result = new List<INodePort>();
|
||||||
|
for (int i = 0; i < connections.Count; i++) {
|
||||||
|
NodePort port = GetConnection(i);
|
||||||
|
if (port != null) result.Add(port);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
bool INodePort.dynamic { get { return _dynamic; } }
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary> Construct a static targetless nodeport. Used as a template. </summary>
|
/// <summary> Construct a static targetless nodeport. Used as a template. </summary>
|
||||||
public NodePort(FieldInfo fieldInfo) {
|
public NodePort(FieldInfo fieldInfo) {
|
||||||
_fieldName = fieldInfo.Name;
|
_fieldName = fieldInfo.Name;
|
||||||
@ -60,13 +73,13 @@ namespace XNode {
|
|||||||
_dynamic = false;
|
_dynamic = false;
|
||||||
var attribs = fieldInfo.GetCustomAttributes(false);
|
var attribs = fieldInfo.GetCustomAttributes(false);
|
||||||
for (int i = 0; i < attribs.Length; i++) {
|
for (int i = 0; i < attribs.Length; i++) {
|
||||||
if (attribs[i] is Node.InputAttribute) {
|
if (attribs[i] is InputAttribute) {
|
||||||
_direction = IO.Input;
|
_direction = IO.Input;
|
||||||
_connectionType = (attribs[i] as Node.InputAttribute).connectionType;
|
_connectionType = (attribs[i] as InputAttribute).connectionType;
|
||||||
_typeConstraint = (attribs[i] as Node.InputAttribute).typeConstraint;
|
_typeConstraint = (attribs[i] as InputAttribute).typeConstraint;
|
||||||
} else if (attribs[i] is Node.OutputAttribute) {
|
} else if (attribs[i] is OutputAttribute) {
|
||||||
_direction = IO.Output;
|
_direction = IO.Output;
|
||||||
_connectionType = (attribs[i] as Node.OutputAttribute).connectionType;
|
_connectionType = (attribs[i] as OutputAttribute).connectionType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,7 +96,7 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Construct a dynamic port. Dynamic ports are not forgotten on reimport, and is ideal for runtime-created ports. </summary>
|
/// <summary> Construct a dynamic port. Dynamic ports are not forgotten on reimport, and is ideal for runtime-created ports. </summary>
|
||||||
public NodePort(string fieldName, Type type, IO direction, Node.ConnectionType connectionType, Node.TypeConstraint typeConstraint, Node node) {
|
public NodePort(string fieldName, Type type, IO direction, ConnectionType connectionType, TypeConstraint typeConstraint, Node node) {
|
||||||
_fieldName = fieldName;
|
_fieldName = fieldName;
|
||||||
this.ValueType = type;
|
this.ValueType = type;
|
||||||
_direction = direction;
|
_direction = direction;
|
||||||
@ -198,8 +211,8 @@ namespace XNode {
|
|||||||
if (port == this) { Debug.LogWarning("Cannot connect port to self."); return; }
|
if (port == this) { Debug.LogWarning("Cannot connect port to self."); return; }
|
||||||
if (IsConnectedTo(port)) { Debug.LogWarning("Port already connected. "); 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; }
|
if (direction == port.direction) { Debug.LogWarning("Cannot connect two " + (direction == IO.Input ? "input" : "output") + " connections"); return; }
|
||||||
if (port.connectionType == Node.ConnectionType.Override && port.ConnectionCount != 0) { port.ClearConnections(); }
|
if (port.connectionType == ConnectionType.Override && port.ConnectionCount != 0) { port.ClearConnections(); }
|
||||||
if (connectionType == Node.ConnectionType.Override && ConnectionCount != 0) { ClearConnections(); }
|
if (connectionType == ConnectionType.Override && ConnectionCount != 0) { ClearConnections(); }
|
||||||
connections.Add(new PortConnection(port));
|
connections.Add(new PortConnection(port));
|
||||||
if (port.connections == null) port.connections = new List<PortConnection>();
|
if (port.connections == null) port.connections = new List<PortConnection>();
|
||||||
if (!port.IsConnectedTo(this)) port.connections.Add(new PortConnection(this));
|
if (!port.IsConnectedTo(this)) port.connections.Add(new PortConnection(this));
|
||||||
@ -256,8 +269,8 @@ namespace XNode {
|
|||||||
// If there isn't one of each, they can't connect
|
// If there isn't one of each, they can't connect
|
||||||
if (input == null || output == null) return false;
|
if (input == null || output == null) return false;
|
||||||
// Check type constraints
|
// Check type constraints
|
||||||
if (input.typeConstraint == XNode.Node.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false;
|
if (input.typeConstraint == XNode.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false;
|
||||||
if (input.typeConstraint == XNode.Node.TypeConstraint.Strict && input.ValueType != output.ValueType) return false;
|
if (input.typeConstraint == XNode.TypeConstraint.Strict && input.ValueType != output.ValueType) return false;
|
||||||
// Success
|
// Success
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user