@@ -21,45 +21,168 @@ public struct PortDescription
21
21
public string portDisplayName ;
22
22
}
23
23
24
- static Dictionary < Type , Type > nodeViewPerType = new Dictionary < Type , Type > ( ) ;
25
- static Dictionary < string , Type > nodePerMenuTitle = new Dictionary < string , Type > ( ) ;
26
24
static Dictionary < Type , MonoScript > nodeViewScripts = new Dictionary < Type , MonoScript > ( ) ;
27
25
static Dictionary < Type , MonoScript > nodeScripts = new Dictionary < Type , MonoScript > ( ) ;
28
- static List < Type > slotTypes = new List < Type > ( ) ;
29
- static List < PortDescription > nodeCreatePortDescription = new List < PortDescription > ( ) ;
26
+ static Dictionary < Type , Type > nodeViewPerType = new Dictionary < Type , Type > ( ) ;
27
+
28
+ public class NodeDescriptions
29
+ {
30
+ public Dictionary < string , Type > nodePerMenuTitle = new Dictionary < string , Type > ( ) ;
31
+ public List < Type > slotTypes = new List < Type > ( ) ;
32
+ public List < PortDescription > nodeCreatePortDescription = new List < PortDescription > ( ) ;
33
+ }
34
+
35
+ public struct NodeSpecificToGraph
36
+ {
37
+ public Type nodeType ;
38
+ public List < MethodInfo > isCompatibleWithGraph ;
39
+ public Type compatibleWithGraphType ;
40
+ }
41
+
42
+ static Dictionary < BaseGraph , NodeDescriptions > specificNodeDescriptions = new Dictionary < BaseGraph , NodeDescriptions > ( ) ;
43
+ static List < NodeSpecificToGraph > specificNodes = new List < NodeSpecificToGraph > ( ) ;
44
+
45
+ static NodeDescriptions genericNodes = new NodeDescriptions ( ) ;
30
46
31
47
static NodeProvider ( )
48
+ {
49
+ BuildScriptCache ( ) ;
50
+ BuildGenericNodeCache ( ) ;
51
+ }
52
+
53
+ public static void LoadGraph ( BaseGraph graph )
54
+ {
55
+ // Clear old graph data in case there was some
56
+ specificNodeDescriptions . Remove ( graph ) ;
57
+ var descriptions = new NodeDescriptions ( ) ;
58
+ specificNodeDescriptions . Add ( graph , descriptions ) ;
59
+
60
+ var graphType = graph . GetType ( ) ;
61
+ foreach ( var nodeInfo in specificNodes )
62
+ {
63
+ bool compatible = nodeInfo . compatibleWithGraphType == null || nodeInfo . compatibleWithGraphType == graphType ;
64
+
65
+ if ( nodeInfo . isCompatibleWithGraph != null )
66
+ {
67
+ foreach ( var method in nodeInfo . isCompatibleWithGraph )
68
+ compatible &= ( bool ) method ? . Invoke ( null , new object [ ] { graph } ) ;
69
+ }
70
+
71
+ if ( compatible )
72
+ BuildCacheForNode ( nodeInfo . nodeType , descriptions , graph ) ;
73
+ }
74
+ }
75
+
76
+ public static void UnloadGraph ( BaseGraph graph )
77
+ {
78
+ specificNodeDescriptions . Remove ( graph ) ;
79
+ }
80
+
81
+ static void BuildGenericNodeCache ( )
32
82
{
33
83
foreach ( var nodeType in TypeCache . GetTypesDerivedFrom < BaseNode > ( ) )
34
84
{
35
- if ( nodeType . IsAbstract )
85
+ if ( ! IsNodeAccessibleFromMenu ( nodeType ) )
36
86
continue ;
37
87
38
- AddNodeType ( nodeType ) ;
88
+ if ( IsNodeSpecificToGraph ( nodeType ) )
89
+ continue ;
90
+
91
+ BuildCacheForNode ( nodeType , genericNodes ) ;
92
+ }
93
+ }
94
+
95
+ static void BuildCacheForNode ( Type nodeType , NodeDescriptions targetDescription , BaseGraph graph = null )
96
+ {
97
+ var attrs = nodeType . GetCustomAttributes ( typeof ( NodeMenuItemAttribute ) , false ) as NodeMenuItemAttribute [ ] ;
98
+
99
+ if ( attrs != null && attrs . Length > 0 )
100
+ {
101
+ foreach ( var attr in attrs )
102
+ targetDescription . nodePerMenuTitle [ attr . menuTitle ] = nodeType ;
103
+ }
104
+
105
+ foreach ( var field in nodeType . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) )
106
+ {
107
+ if ( field . GetCustomAttribute < HideInInspector > ( ) == null && field . GetCustomAttributes ( ) . Any ( c => c is InputAttribute || c is OutputAttribute ) )
108
+ targetDescription . slotTypes . Add ( field . FieldType ) ;
109
+ }
110
+
111
+ ProvideNodePortCreationDescription ( nodeType , targetDescription , graph ) ;
112
+ }
113
+
114
+ static bool IsNodeAccessibleFromMenu ( Type nodeType )
115
+ {
116
+ if ( nodeType . IsAbstract )
117
+ return false ;
118
+
119
+ return nodeType . GetCustomAttributes < NodeMenuItemAttribute > ( ) . Count ( ) > 0 ;
120
+ }
121
+
122
+ // Check if node has anything that depends on the graph type or settings
123
+ static bool IsNodeSpecificToGraph ( Type nodeType )
124
+ {
125
+ var isCompatibleWithGraphMethods = nodeType . GetMethods ( BindingFlags . Static | BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . FlattenHierarchy ) . Where ( m => m . GetCustomAttribute < IsCompatibleWithGraph > ( ) != null ) ;
126
+ var nodeMenuAttributes = nodeType . GetCustomAttributes < NodeMenuItemAttribute > ( ) ;
39
127
40
- foreach ( var field in nodeType . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) )
128
+ List < Type > compatibleGraphTypes = nodeMenuAttributes . Where ( n => n . onlyCompatibleWithGraph != null ) . Select ( a => a . onlyCompatibleWithGraph ) . ToList ( ) ;
129
+
130
+ List < MethodInfo > compatibleMethods = new List < MethodInfo > ( ) ;
131
+ foreach ( var method in isCompatibleWithGraphMethods )
132
+ {
133
+ // Check if the method is static and have the correct prototype
134
+ var p = method . GetParameters ( ) ;
135
+ if ( method . ReturnType != typeof ( bool ) || p . Count ( ) != 1 || p [ 0 ] . ParameterType != typeof ( BaseGraph ) )
136
+ Debug . LogError ( $ "The function '{ method . Name } ' marked with the IsCompatibleWithGraph attribute either doesn't return a boolean or doesn't take one parameter of BaseGraph type.") ;
137
+ else
138
+ compatibleMethods . Add ( method ) ;
139
+ }
140
+
141
+ if ( compatibleMethods . Count > 0 || compatibleGraphTypes . Count > 0 )
142
+ {
143
+ // We still need to add the element in specificNode even without specific graph
144
+ if ( compatibleGraphTypes . Count == 0 )
145
+ compatibleGraphTypes . Add ( null ) ;
146
+
147
+ foreach ( var graphType in compatibleGraphTypes )
41
148
{
42
- if ( field . GetCustomAttribute < HideInInspector > ( ) == null && field . GetCustomAttributes ( ) . Any ( c => c is InputAttribute || c is OutputAttribute ) )
43
- slotTypes . Add ( field . FieldType ) ;
149
+ specificNodes . Add ( new NodeSpecificToGraph {
150
+ nodeType = nodeType ,
151
+ isCompatibleWithGraph = compatibleMethods ,
152
+ compatibleWithGraphType = graphType
153
+ } ) ;
44
154
}
155
+ return true ;
156
+ }
157
+ return false ;
158
+ }
159
+
160
+ static void BuildScriptCache ( )
161
+ {
162
+ foreach ( var nodeType in TypeCache . GetTypesDerivedFrom < BaseNode > ( ) )
163
+ {
164
+ if ( ! IsNodeAccessibleFromMenu ( nodeType ) )
165
+ continue ;
45
166
46
- ProvideNodePortCreationDescription ( nodeType ) ;
167
+ AddNodeScriptAsset ( nodeType ) ;
47
168
}
48
169
49
170
foreach ( var nodeViewType in TypeCache . GetTypesDerivedFrom < BaseNodeView > ( ) )
50
171
{
51
172
if ( ! nodeViewType . IsAbstract )
52
- AddNodeViewType ( nodeViewType ) ;
173
+ AddNodeViewScriptAsset ( nodeViewType ) ;
53
174
}
54
175
}
55
176
56
- static void ProvideNodePortCreationDescription ( Type nodeType )
177
+ static FieldInfo SetGraph = typeof ( BaseNode ) . GetField ( "graph" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
178
+ static void ProvideNodePortCreationDescription ( Type nodeType , NodeDescriptions targetDescription , BaseGraph graph = null )
57
179
{
58
180
var node = Activator . CreateInstance ( nodeType ) as BaseNode ;
59
181
try {
182
+ SetGraph . SetValue ( node , graph ) ;
60
183
node . InitializePorts ( ) ;
61
184
node . UpdateAllPorts ( ) ;
62
- } catch ( Exception ) { }
185
+ } catch ( Exception ) { }
63
186
64
187
foreach ( var p in node . inputPorts )
65
188
AddPort ( p , true ) ;
@@ -68,7 +191,7 @@ static void ProvideNodePortCreationDescription(Type nodeType)
68
191
69
192
void AddPort ( NodePort p , bool input )
70
193
{
71
- nodeCreatePortDescription . Add ( new PortDescription {
194
+ targetDescription . nodeCreatePortDescription . Add ( new PortDescription {
72
195
nodeType = nodeType ,
73
196
portType = p . portData . displayType ?? p . fieldInfo . FieldType ,
74
197
isInput = input ,
@@ -79,16 +202,8 @@ void AddPort(NodePort p, bool input)
79
202
}
80
203
}
81
204
82
- static void AddNodeType ( Type type )
205
+ static void AddNodeScriptAsset ( Type type )
83
206
{
84
- var attrs = type . GetCustomAttributes ( typeof ( NodeMenuItemAttribute ) , false ) as NodeMenuItemAttribute [ ] ;
85
-
86
- if ( attrs != null && attrs . Length > 0 )
87
- {
88
- foreach ( var attr in attrs )
89
- nodePerMenuTitle [ attr . menuTitle ] = type ;
90
- }
91
-
92
207
var nodeScriptAsset = FindScriptFromClassName ( type . Name ) ;
93
208
94
209
// Try find the class name with Node name at the end
@@ -98,7 +213,7 @@ static void AddNodeType(Type type)
98
213
nodeScripts [ type ] = nodeScriptAsset ;
99
214
}
100
215
101
- static void AddNodeViewType ( Type type )
216
+ static void AddNodeViewScriptAsset ( Type type )
102
217
{
103
218
var attrs = type . GetCustomAttributes ( typeof ( NodeCustomEditor ) , false ) as NodeCustomEditor [ ] ;
104
219
@@ -154,9 +269,16 @@ public static Type GetNodeViewTypeFromType(Type nodeType)
154
269
return view ;
155
270
}
156
271
157
- public static Dictionary < string , Type > GetNodeMenuEntries ( )
272
+ public static IEnumerable < ( string path , Type type ) > GetNodeMenuEntries ( BaseGraph graph = null )
158
273
{
159
- return nodePerMenuTitle ;
274
+ foreach ( var node in genericNodes . nodePerMenuTitle )
275
+ yield return ( node . Key , node . Value ) ;
276
+
277
+ if ( graph != null && specificNodeDescriptions . TryGetValue ( graph , out var specificNodes ) )
278
+ {
279
+ foreach ( var node in specificNodes . nodePerMenuTitle )
280
+ yield return ( node . Key , node . Value ) ;
281
+ }
160
282
}
161
283
162
284
public static MonoScript GetNodeViewScript ( Type type )
@@ -173,19 +295,48 @@ public static MonoScript GetNodeScript(Type type)
173
295
return script ;
174
296
}
175
297
176
- public static List < Type > GetSlotTypes ( ) => slotTypes ;
298
+ public static IEnumerable < Type > GetSlotTypes ( BaseGraph graph = null )
299
+ {
300
+ foreach ( var type in genericNodes . slotTypes )
301
+ yield return type ;
177
302
178
- public static List < PortDescription > GetEdgeCreationNodeMenuEntry ( PortView portView )
303
+ if ( graph != null && specificNodeDescriptions . TryGetValue ( graph , out var specificNodes ) )
304
+ {
305
+ foreach ( var type in specificNodes . slotTypes )
306
+ yield return type ;
307
+ }
308
+ }
309
+
310
+ public static IEnumerable < PortDescription > GetEdgeCreationNodeMenuEntry ( PortView portView , BaseGraph graph = null )
179
311
{
180
- return nodeCreatePortDescription . Where ( n => {
181
- if ( portView . direction == Direction . Input && n . isInput || portView . direction == Direction . Output && ! n . isInput )
312
+ foreach ( var description in genericNodes . nodeCreatePortDescription )
313
+ {
314
+ if ( ! IsPortCompatible ( description ) )
315
+ continue ;
316
+
317
+ yield return description ;
318
+ }
319
+
320
+ if ( graph != null && specificNodeDescriptions . TryGetValue ( graph , out var specificNodes ) )
321
+ {
322
+ foreach ( var description in specificNodes . nodeCreatePortDescription )
323
+ {
324
+ if ( ! IsPortCompatible ( description ) )
325
+ continue ;
326
+ yield return description ;
327
+ }
328
+ }
329
+
330
+ bool IsPortCompatible ( PortDescription description )
331
+ {
332
+ if ( portView . direction == Direction . Input && description . isInput || portView . direction == Direction . Output && ! description . isInput )
182
333
return false ;
183
334
184
- if ( ! BaseGraph . TypesAreConnectable ( n . portType , portView . portType ) )
335
+ if ( ! BaseGraph . TypesAreConnectable ( description . portType , portView . portType ) )
185
336
return false ;
186
-
337
+
187
338
return true ;
188
- } ) . ToList ( ) ;
339
+ }
189
340
}
190
341
}
191
342
}
0 commit comments