diff --git a/Resources.meta b/Resources.meta
new file mode 100644
index 0000000..f101f36
--- /dev/null
+++ b/Resources.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 65ed9b337b7f0b74ab007799bb407a5e
+folderAsset: yes
+timeCreated: 1507567488
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/NodeDataCache.asset b/Resources/NodeDataCache.asset
new file mode 100644
index 0000000..d118730
Binary files /dev/null and b/Resources/NodeDataCache.asset differ
diff --git a/Resources/NodeDataCache.asset.meta b/Resources/NodeDataCache.asset.meta
new file mode 100644
index 0000000..cd7469f
--- /dev/null
+++ b/Resources/NodeDataCache.asset.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a6799b9b87b05a14f9053390f4500d7b
+timeCreated: 1507567505
+licenseType: Free
+NativeFormatImporter:
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs
index 901feb5..1bde7d3 100644
--- a/Scripts/Editor/NodeEditor.cs
+++ b/Scripts/Editor/NodeEditor.cs
@@ -63,14 +63,14 @@ public class NodeEditor {
/// Draw node port GUI using automatic layouting. Returns port handle position.
protected Vector2 DrawNodePortGUI(NodePort port) {
GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputPort : NodeEditorResources.styles.outputPort;
- Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.name.PrettifyCamelCase()), style);
+ Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.fieldName.PrettifyCamelCase()), style);
return DrawNodePortGUI(rect, port);
}
/// Draw node port GUI in rect. Returns port handle position.
protected Vector2 DrawNodePortGUI(Rect rect, NodePort port) {
GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputPort : NodeEditorResources.styles.outputPort;
- GUI.Label(rect, new GUIContent(port.name.PrettifyCamelCase()), style);
+ GUI.Label(rect, new GUIContent(port.fieldName.PrettifyCamelCase()), style);
Vector2 handlePoint = rect.center;
diff --git a/Scripts/Editor/NodeEditorToolbar.cs b/Scripts/Editor/NodeEditorToolbar.cs
index eb89b6f..8d7900b 100644
--- a/Scripts/Editor/NodeEditorToolbar.cs
+++ b/Scripts/Editor/NodeEditorToolbar.cs
@@ -19,7 +19,7 @@ public partial class NodeEditorWindow {
if (IsHoveringNode) {
GUILayout.Space(20);
string hoverInfo = hoveredNode.GetType().ToString();
- if (IsHoveringPort) hoverInfo += " > " + hoveredPort.name;
+ if (IsHoveringPort) hoverInfo += " > " + hoveredPort.fieldName;
GUILayout.Label(hoverInfo);
}
}
diff --git a/Scripts/Node.cs b/Scripts/Node.cs
index 49f62d0..ea0d485 100644
--- a/Scripts/Node.cs
+++ b/Scripts/Node.cs
@@ -20,12 +20,13 @@ public abstract class Node : ScriptableObject {
public int OutputCount { get { return outputs.Count; } }
protected Node() {
- CachePorts(); //Cache the ports at creation time so we don't have to use reflection at runtime
+
+ GetPorts(); //Cache the ports at creation time so we don't have to use reflection at runtime
}
protected void OnEnable() {
VerifyConnections();
- CachePorts();
+ GetPorts();
Init();
}
@@ -117,45 +118,7 @@ public abstract class Node : ScriptableObject {
}
}
- /// Use reflection to find all fields with or , and write to and
- private void CachePorts() {
- List inputPorts = new List();
- List outputPorts = new List();
-
- System.Reflection.FieldInfo[] fieldInfo = GetType().GetFields();
- for (int i = 0; i < fieldInfo.Length; i++) {
-
- //Get InputAttribute and OutputAttribute
- object[] attribs = fieldInfo[i].GetCustomAttributes(false);
- InputAttribute inputAttrib = null;
- OutputAttribute outputAttrib = null;
- for (int k = 0; k < attribs.Length; k++) {
- if (attribs[k] is InputAttribute) inputAttrib = attribs[k] as InputAttribute;
- else if (attribs[k] is OutputAttribute) outputAttrib = attribs[k] as OutputAttribute;
- }
-
- if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output.");
- else if (inputAttrib != null) inputPorts.Add(new NodePort(fieldInfo[i], this));
- else if (outputAttrib != null) outputPorts.Add(new NodePort(fieldInfo[i], this));
- }
-
- //Remove
- for (int i = inputs.Count-1; i >= 0; i--) {
- //If input nodeport does not exist, remove it
- if (!inputPorts.Any(x => inputs[i].fieldName == x.fieldName)) inputs.RemoveAt(i);
- }
- for (int i = outputs.Count - 1; i >= 0; i--) {
- //If output nodeport does not exist, remove it
- if (!outputPorts.Any(x => outputs[i].fieldName == x.fieldName)) outputs.RemoveAt(i);
- }
- //Add
- for (int i = 0; i < inputPorts.Count; i++) {
- //If inputports contains a new port, add it
- if (!inputs.Any(x => x.fieldName == inputPorts[i].fieldName)) inputs.Add(inputPorts[i]);
- }
- for (int i = 0; i < outputPorts.Count; i++) {
- //If inputports contains a new port, add it
- if (!outputs.Any(x => x.fieldName == outputPorts[i].fieldName)) outputs.Add(outputPorts[i]);
- }
+ private void GetPorts() {
+ NodeDataCache.GetPorts(this, out inputs, out outputs);
}
}
diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs
new file mode 100644
index 0000000..8608d21
--- /dev/null
+++ b/Scripts/NodeDataCache.cs
@@ -0,0 +1,113 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using System.Reflection;
+using System.Linq;
+
+/// Precaches reflection data in editor so we won't have to do it runtime
+public sealed class NodeDataCache : ScriptableObject {
+ public static NodeDataCache instance { get { return _instance; } }
+ private static NodeDataCache _instance;
+
+ [SerializeField]
+ private PortDataCache portDataCache = new PortDataCache();
+
+ [RuntimeInitializeOnLoadMethod]
+ private static void InitializeInstance() {
+ Debug.Log("INIT");
+ NodeDataCache[] ndc = Resources.FindObjectsOfTypeAll();
+ if (ndc == null || ndc.Length == 0) {
+ Debug.LogWarning("No NodeDataCache found. Creating.");
+ _instance = ScriptableObject.CreateInstance();
+ _instance.BuildCache();
+ }
+ else if (ndc.Length > 1) {
+ Debug.LogWarning("Multiple NodeDataCaches found.");
+ }
+ _instance = ndc[0];
+ }
+
+ /// Return port data from cache
+ public static void GetPorts(Node node, out List inputs, out List outputs) {
+ if (_instance == null) InitializeInstance();
+
+ System.Type nodeType = node.GetType();
+ inputs = new List();
+ outputs = new List();
+ if (!_instance.portDataCache.ContainsKey(nodeType)) return;
+ for (int i = 0; i < _instance.portDataCache[nodeType].Count; i++) {
+ if (_instance.portDataCache[nodeType][i].direction == NodePort.IO.Input) inputs.Add(new NodePort(_instance.portDataCache[nodeType][i], node));
+ else outputs.Add(new NodePort(_instance.portDataCache[nodeType][i], node));
+ }
+ }
+
+#if UNITY_EDITOR
+ [UnityEditor.InitializeOnLoadMethod]
+#endif
+ private static void Init() {
+ instance.BuildCache();
+ }
+
+ private void BuildCache() {
+ System.Type baseType = typeof(Node);
+ Assembly assembly = Assembly.GetAssembly(baseType);
+ System.Type[] nodeTypes = assembly.GetTypes().Where(t =>
+ !t.IsAbstract &&
+ baseType.IsAssignableFrom(t)
+ ).ToArray();
+ portDataCache.Clear();
+
+ for (int i = 0; i < nodeTypes.Length; i++) {
+ CachePorts(nodeTypes[i]);
+ }
+ }
+
+ private void CachePorts(System.Type nodeType) {
+ List inputPorts = new List();
+ List outputPorts = new List();
+
+ 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]);
+ }
+ }
+}
diff --git a/Scripts/NodeDataCache.cs.meta b/Scripts/NodeDataCache.cs.meta
new file mode 100644
index 0000000..34482f2
--- /dev/null
+++ b/Scripts/NodeDataCache.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 64ea6af1e195d024d8df0ead1921e517
+timeCreated: 1507566823
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs
index dfef17c..54e55d9 100644
--- a/Scripts/NodePort.cs
+++ b/Scripts/NodePort.cs
@@ -19,7 +19,6 @@ public class NodePort {
public bool IsOutput { get { return direction == IO.Output; } }
public Node node { get; private set; }
- [SerializeField] public string name;
public bool enabled { get { return _enabled; } set { _enabled = value; } }
public string fieldName { get { return _fieldName; } }
@@ -30,11 +29,9 @@ public class NodePort {
[SerializeField] private bool _enabled = true;
[SerializeField] private IO _direction;
- public NodePort(FieldInfo fieldInfo, Node node) {
+ public NodePort(FieldInfo fieldInfo) {
_fieldName = fieldInfo.Name;
- name = _fieldName;
type = fieldInfo.FieldType;
- this.node = node;
var attribs = fieldInfo.GetCustomAttributes(false);
for (int i = 0; i < attribs.Length; i++) {
@@ -43,6 +40,13 @@ public class NodePort {
}
}
+ public NodePort(NodePort nodePort, Node node) {
+ _fieldName = nodePort._fieldName;
+ type = nodePort.type;
+ this.node = node;
+ _direction = nodePort.direction;
+ }
+
/// Checks all connections for invalid references, and removes them.
public void VerifyConnections() {
for (int i = 0; i < connections.Count; i++) {