diff --git a/TNodeCore/Attribute/InternalModel.cs b/TNodeCore/Attribute/InternalModel.cs new file mode 100644 index 0000000..f9b10cd --- /dev/null +++ b/TNodeCore/Attribute/InternalModel.cs @@ -0,0 +1,10 @@ +namespace TNodeCore.Attribute{ + /// + /// Internal use only. so that Editor Cache and Runtime cache could register it globally. + /// + public class InternalModel:System.Attribute{ + public InternalModel(){ + + } + } +} \ No newline at end of file diff --git a/TNodeCore/Attribute/InternalModel.cs.meta b/TNodeCore/Attribute/InternalModel.cs.meta new file mode 100644 index 0000000..16bb590 --- /dev/null +++ b/TNodeCore/Attribute/InternalModel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bcd3a2f1670c4bb2b5ef98337ba785d6 +timeCreated: 1658132721 \ No newline at end of file diff --git a/TNodeCore/Editor/Resources/GraphViewBackground.uss b/TNodeCore/Editor/Resources/GraphViewBackground.uss index 6c9fbfc..c82adec 100644 --- a/TNodeCore/Editor/Resources/GraphViewBackground.uss +++ b/TNodeCore/Editor/Resources/GraphViewBackground.uss @@ -10,7 +10,7 @@ GridBackground{ left: 45%; top: 45%; - font-size: 14; + font-size: 24px; } #TopMenu{ @@ -20,4 +20,9 @@ GridBackground{ width: 100%; height: 24px; background-color: #171717; +} +.unity-text-element--inner-input-field-component{ + width: 100%; + min-width: 70px; + max-width: 250px; } \ No newline at end of file diff --git a/TNodeCore/Models/BlackboardDragNodeData.cs b/TNodeCore/Models/BlackboardDragNodeData.cs index 773bcb5..0f1622f 100644 --- a/TNodeCore/Models/BlackboardDragNodeData.cs +++ b/TNodeCore/Models/BlackboardDragNodeData.cs @@ -1,10 +1,12 @@ using System; +using TNodeCore.Attribute; using TNodeCore.Attribute.Ports; using TNodeCore.RuntimeCache; using UnityEngine; namespace TNodeCore.Models{ [Serializable] + [InternalModel] public class BlackboardDragNodeData:NodeData{ public string blackDragData; [SerializeReference] diff --git a/TNodeCore/Models/IModel.cs b/TNodeCore/Models/IModel.cs index c2261e3..b0a1e42 100644 --- a/TNodeCore/Models/IModel.cs +++ b/TNodeCore/Models/IModel.cs @@ -1,5 +1,7 @@ -namespace TNodeCore.Models{ +using System; + +namespace TNodeCore.Models{ public interface IModel{ - + } } \ No newline at end of file diff --git a/TNodeCore/Models/NodeData.cs b/TNodeCore/Models/NodeData.cs index 3e1565a..5701a69 100644 --- a/TNodeCore/Models/NodeData.cs +++ b/TNodeCore/Models/NodeData.cs @@ -22,7 +22,6 @@ namespace TNodeCore.Models{ public string nodeName; public bool entryPoint; - public virtual void Process(){ } diff --git a/TNodeCore/Models/PortInfo.cs b/TNodeCore/Models/PortInfo.cs index dbd4e8c..1eb1ed8 100644 --- a/TNodeCore/Models/PortInfo.cs +++ b/TNodeCore/Models/PortInfo.cs @@ -1,9 +1,10 @@ using System; +using UnityEngine.Serialization; namespace TNodeCore.Models{ [Serializable] public class PortInfo{ - public string portName; + public string portEntryName; public string nodeDataId; } } \ No newline at end of file diff --git a/TNodeCore/Runtime/RuntimeGraph.cs b/TNodeCore/Runtime/RuntimeGraph.cs index 771be99..235f792 100644 --- a/TNodeCore/Runtime/RuntimeGraph.cs +++ b/TNodeCore/Runtime/RuntimeGraph.cs @@ -33,8 +33,8 @@ namespace TNodeCore.Runtime{ //out node is node output data //in node is node receive data - var outValue = outNode.GetOutput(nodeLink.outPort.portName); - inNode.SetInput(nodeLink.inPort.portName, outValue); + var outValue = outNode.GetOutput(nodeLink.outPort.portEntryName); + inNode.SetInput(nodeLink.inPort.portEntryName, outValue); } public GraphTool(List list, Dictionary graphNodes){ RuntimeNodes = graphNodes; @@ -66,7 +66,7 @@ namespace TNodeCore.Runtime{ if(TopologicalOrder.Count!= list.Count){ throw new Exception("Topological sort failed,circular dependency detected"); } - RuntimeNodes.Clear(); + inDegreeCounterForTopologicalSort.Clear(); queue.Clear(); } @@ -75,7 +75,7 @@ namespace TNodeCore.Runtime{ } [SerializeReference] public BlackboardData runtimeBlackboardData; - + [NonSerialized] private bool _build = false; public void Build(){ @@ -85,6 +85,7 @@ namespace TNodeCore.Runtime{ ModifyOrCreateInNode(linkData); ModifyOrCreateOutNode(linkData); } + Debug.Log("hi"); var nodeList = RuntimeNodes.Values; _graphTool = new GraphTool(nodeList.ToList(),RuntimeNodes); _build = true; @@ -93,7 +94,6 @@ namespace TNodeCore.Runtime{ public RuntimeNode Get(NodeData nodeData){ if(!_build) Build(); - if(RuntimeNodes.ContainsKey(nodeData.id)){ return RuntimeNodes[nodeData.id]; } @@ -104,11 +104,12 @@ namespace TNodeCore.Runtime{ if (RuntimeNodes.ContainsKey(id)){ return RuntimeNodes[id]; } - return null; } //DFS search for resolving dependency public bool ResolveDependency(NodeData startNode){ + if(!_build) + Build(); if (_graphTool == null) return false; _graphTool.DependencyTraversal(Get(startNode)); @@ -140,8 +141,17 @@ namespace TNodeCore.Runtime{ runtimeBlackboardData = graphData.blackboardData.Clone() as BlackboardData; } } - - + + public void OnDisable(){ + RuntimeNodes.Clear(); + _build = false; + } + + public void OnDestroy(){ + RuntimeNodes.Clear(); + _build = false; + } + } public enum ProcessingStrategy{ diff --git a/TNodeCore/Runtime/RuntimeNode.cs b/TNodeCore/Runtime/RuntimeNode.cs index 34181fc..515f840 100644 --- a/TNodeCore/Runtime/RuntimeNode.cs +++ b/TNodeCore/Runtime/RuntimeNode.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using Codice.Client.Common.TreeGrouper; using TNodeCore.Attribute.Ports; using TNodeCore.Models; @@ -9,49 +10,33 @@ namespace TNodeCore.Runtime{ public class RuntimeNode{ public NodeData NodeData { get; set; } //the link connect to node's in port - public List InputLink; + public List InputLink = new List(); //the link connect to node's out port - public List OutputLink; + public List OutputLink = new List(); //Cache node data type for fast access private readonly Type _type; public void SetInput(string portName,object value){ - NodeData.SetValue(portName, value); + _portAccessors[portName].SetValue(this.NodeData,value); + } public object GetOutput(string portName){ - return NodeData.GetValue(portName); + + return _portAccessors[portName].GetValue(this.NodeData); } + private readonly Dictionary _portAccessors; - private Dictionary _inputPorts = new(); - private Dictionary _outputPorts = new(); - - - private void CachingPorts(){ - var properties = _type.GetProperties(); - foreach (var propertyInfo in properties){ - //Check if the property is a port - var attribute = propertyInfo.GetCustomAttributes(typeof(InputAttribute),true); - if (attribute.Length > 0){ - - _inputPorts.Add(propertyInfo.Name,NodeData.CacheSetProperty(propertyInfo.Name)); - } - - attribute = propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true); - if (attribute.Length > 0){ - _outputPorts.Add(propertyInfo.Name,NodeData.CacheGetProperty(propertyInfo.Name)); - } - } - } + public RuntimeNode(NodeData nodeData){ NodeData = nodeData; //Caching the type of the node _type = nodeData.GetType(); var info = nodeData.GetType().GetProperties(); - - CachingPorts(); + + _portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPropertyAccessors[_type]; } public List GetInputNodesId(){ List dependencies = new List(); diff --git a/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs new file mode 100644 index 0000000..88a5cae --- /dev/null +++ b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs @@ -0,0 +1,8 @@ +using System; + +namespace TNodeCore.RuntimeCache{ + public interface IModelPropertyAccessor{ + object GetValue(object model); + void SetValue(object model, object value); + } +} \ No newline at end of file diff --git a/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta new file mode 100644 index 0000000..29b0319 --- /dev/null +++ b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bcaebfe910b84f5dbd81bc4330fe6f7b +timeCreated: 1658131431 \ No newline at end of file diff --git a/TNodeCore/RuntimeCache/RuntimeCache.cs b/TNodeCore/RuntimeCache/RuntimeCache.cs index dfd88e8..5eb6068 100644 --- a/TNodeCore/RuntimeCache/RuntimeCache.cs +++ b/TNodeCore/RuntimeCache/RuntimeCache.cs @@ -8,6 +8,35 @@ using TNodeCore.Models; using UnityEngine; namespace TNodeCore.RuntimeCache{ + public class PropAccessor:IModelPropertyAccessor{ + public readonly Func Get; + public readonly Action Set; + public PropAccessor(string propName){ + Type t = typeof(T1); + MethodInfo getter = t.GetMethod("get_" + propName); + MethodInfo setter = t.GetMethod("set_" + propName); + if(getter!=null) + Get = (Func)Delegate.CreateDelegate(typeof(Func), null, getter); + if(setter!=null) + Set = (Action)Delegate.CreateDelegate(typeof(Action), null, setter); + } + public static PropAccessor Create(string propName){ + return new PropAccessor(propName); + } + + public object GetValue(object model){ + return Get((T1)model); + } + + public void SetValue(object model, object value){ + Set((T1)model,(T2)value); + } + } + public class PropertyNotFoundException : Exception{ + public PropertyNotFoundException(string path):base("Property not found :"+path){ + + } + } public class RuntimeCache{ //Singleton instance for the runtime cache private static RuntimeCache _instance; @@ -18,18 +47,16 @@ namespace TNodeCore.RuntimeCache{ public delegate object GetValueDelegate(IModel nodeData); public delegate void SetValueDelegate(IModel nodeData,object value); - public delegate object GetPropertyValueDelegate(); - public delegate void SetPropertyValueDelegate(object value); + public readonly Dictionary> CachedDelegatesForGettingValue = new (); public readonly Dictionary> CachedDelegatesForSettingValue = new (); - public readonly Dictionary> CachedDelegatesForGettingPropertyValue = - new (); - public readonly Dictionary> CachedDelegatesForSettingPropertyValue = + public readonly Dictionary> CachedPropertyAccessors = new (); + private readonly Dictionary _graphBlackboardDictionary = new Dictionary(); private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"}; @@ -46,23 +73,28 @@ namespace TNodeCore.RuntimeCache{ if (attribute is GraphUsageAttribute){ //if the type has GraphUsageAttribute, add it to the cache AddTypeToCache(type,attribute as GraphUsageAttribute); - + } + + if (attribute is InternalModel){ + AddTypeToCache(type,attribute as InternalModel); } } } + + } - private void AddTypeToCache(Type type,GraphUsageAttribute attribute){ + private void AddTypeToCache(Type type,System.Attribute attribute){ //Check if the type is a blackboard data type if(typeof(BlackboardData).IsAssignableFrom(type)){ //if it is, add it to the cache - AddBlackboardDataTypeToCache(type,attribute); - RegisterRuntimeBlackboard(type); + AddBlackboardDataTypeToCache(type,(GraphUsageAttribute)attribute); + CacheRuntimeBlackboard(type); } //Check if the type is a node data type if(typeof(NodeData).IsAssignableFrom(type)){ //if it is, add it to the cache - RegisterRuntimeNodeData(type); + CacheRuntimeNodeData(type); } } @@ -87,7 +119,7 @@ namespace TNodeCore.RuntimeCache{ } return null; } - public void RegisterRuntimeBlackboard(Type type){ + public void CacheRuntimeBlackboard(Type type){ if (type == null) return; if(!CachedDelegatesForGettingValue.ContainsKey(type)){ CachedDelegatesForGettingValue.Add(type, new Dictionary()); @@ -114,29 +146,24 @@ namespace TNodeCore.RuntimeCache{ } } } - - public void RegisterRuntimeNodeData(Type type){ + public static IModelPropertyAccessor Create(string propName,Type targetType,Type valueType){ + var makeGenericType = typeof (PropAccessor<,>).MakeGenericType(targetType,valueType); + var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)}); + var instance = constructor?.Invoke(new object[]{propName}); + return (IModelPropertyAccessor) instance; + } + public void CacheRuntimeNodeData(Type type){ if (type == null) return; if(!CachedDelegatesForGettingValue.ContainsKey(type)){ CachedDelegatesForGettingValue.Add(type, new Dictionary()); CachedDelegatesForSettingValue.Add(type,new Dictionary()); - + CachedPropertyAccessors.Add(type,new Dictionary()); var properties = type.GetProperties(); foreach(var property in properties){ - //if the property only has a setter ,skip - - // if(property.GetSetMethod(false) != null){ - // var setValueDelegate = SetValueDelegateForProperty(property); - // CachedDelegatesForSettingPropertyValue[type].Add(property.Name,setValueDelegate); - // } - // if(property.GetMethod != null){ - // var getValueDelegate = GetValueDelegateForProperty(property); - // CachedDelegatesForGettingPropertyValue[type].Add(property.Name,getValueDelegate); - // } - - + var propertyAccessor = Create(property.Name,type,property.PropertyType); + CachedPropertyAccessors[type].Add(property.Name,propertyAccessor); } //register the fields var fields = type.GetFields(); @@ -160,18 +187,9 @@ namespace TNodeCore.RuntimeCache{ return field.SetValue; } - private GetPropertyValueDelegate GetValueDelegateForProperty(PropertyInfo property){ - var getValueDelegate = (GetPropertyValueDelegate)Delegate.CreateDelegate(typeof(GetPropertyValueDelegate), property.GetGetMethod()); - return getValueDelegate; - } - private SetPropertyValueDelegate SetValueDelegateForProperty(PropertyInfo property){ - Debug.Log(property.GetSetMethod()); - - var setValueDelegate = (SetPropertyValueDelegate)Delegate.CreateDelegate(typeof(SetPropertyValueDelegate), property.GetSetMethod()); - return setValueDelegate; - } - + } + public static class RuntimeExtension{ //todo latter on i will try some way caching reflection more efficiently @@ -192,46 +210,5 @@ namespace TNodeCore.RuntimeCache{ var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path]; method.Invoke(data,value); } - - public static RuntimeCache.GetValueDelegate GetValueDelegate(this IModel blackboardData,string path){ - var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path]; - return method; - } - /// - /// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need - /// - /// - /// - /// - /// - public static RuntimeCache.GetPropertyValueDelegate CacheGetProperty(this IModel data,string path){ - var type = data.GetType(); - var property = type.GetProperty(path); - if (property == null) throw new PropertyNotFoundException(path); - var instance = Delegate.CreateDelegate(typeof(RuntimeCache.GetPropertyValueDelegate), data, - property.GetGetMethod()); - return instance as RuntimeCache.GetPropertyValueDelegate; - } - /// - /// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need - /// - /// - /// - /// - /// - public static RuntimeCache.SetPropertyValueDelegate CacheSetProperty(this IModel data,string path){ - var type = data.GetType(); - var property = type.GetProperty(path); - if (property == null) throw new PropertyNotFoundException(path); - var instance = Delegate.CreateDelegate(typeof(RuntimeCache.SetPropertyValueDelegate), data, - property.GetSetMethod()); - return instance as RuntimeCache.SetPropertyValueDelegate; - } - } - - public class PropertyNotFoundException : Exception{ - public PropertyNotFoundException(string path):base("Property not found :"+path){ - - } } } \ No newline at end of file diff --git a/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs b/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs index 702151d..1c03a31 100644 --- a/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs +++ b/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs @@ -57,7 +57,8 @@ namespace TNode.Editor.Inspector{ } - var globalTest = GetFirstAncestorOfType()?.BaseDataGraphView?.TestMode; + + var globalTest = GetFirstAncestorOfType()?.TestMode; if(globalTest??false){ CreateTestButton(); } @@ -74,7 +75,7 @@ namespace TNode.Editor.Inspector{ if(!test.IsRuntimeGraph) return; var runtimeGraph = test.GetRuntimeGraph(); if (runtimeGraph != null){ - runtimeGraph.ResolveDependency(_data); + var res = runtimeGraph.ResolveDependency(_data); } _data.OnTest(); } diff --git a/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs b/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs index f07e68c..216cb24 100644 --- a/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs +++ b/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs @@ -71,12 +71,10 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{ /// Probably reusable in later GTFs version /// private void WaitingForAGraph(){ - Debug.Log("hello"); VisualElement visualElement = new VisualElement(); //Set background color to white visualElement.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1)); - Debug.Log("hello2"); visualElement.StretchToParentSize(); visualElement.name = "WaitingForAGraph"; Add(visualElement); @@ -279,8 +277,8 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{ var outputNodeView = _nodeDict[outputNode.id]; Edge newEdge = new Edge(){ - input = inputNodeView.inputContainer.Q(edge.inPort.portName), - output = outputNodeView.outputContainer.Q(edge.outPort.portName) + input = inputNodeView.inputContainer.Q(edge.inPort.portEntryName), + output = outputNodeView.outputContainer.Q(edge.outPort.portEntryName) }; newEdge.input?.Connect(newEdge); @@ -378,11 +376,11 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{ var outputNodeData = outputNode.GetNodeData(); var newNodeLink = new NodeLink(new PortInfo(){ nodeDataId = inputNodeData.id, - portName = edge.input.portName, + portEntryName = edge.input.name, }, new PortInfo(){ nodeDataId = outputNodeData.id, - portName = edge.output.portName + portEntryName = edge.output.name }); links.Add(newNodeLink); } diff --git a/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs b/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs index 5a9c81e..a3417da 100644 --- a/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs +++ b/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs @@ -108,7 +108,8 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{ this.outputContainer.Add(port); var portName = BuildPortName(attribute,propertyInfo); port.portName = portName; - port.name = portName; + port.name = propertyInfo.Name; + } } foreach (var propertyInfo in propertyInfos){ @@ -117,7 +118,7 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{ this.inputContainer.Add(port); var portName = BuildPortName(attribute,propertyInfo); port.portName = portName; - port.name = portName; + port.name = propertyInfo.Name; } } }