/// <summary> Update static ports to reflect class fields. </summary>
        public static void UpdatePorts(Node node, Dictionary <string, NodePort> ports)
        {
            if (!Initialized)
            {
                BuildCache();
            }

            Dictionary <string, NodePort> staticPorts = new Dictionary <string, NodePort>();

            System.Type nodeType = node.GetType();

            List <NodePort> typePortCache;

            if (portDataCache.TryGetValue(nodeType, out typePortCache))
            {
                for (int i = 0; i < typePortCache.Count; i++)
                {
                    staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]);
                }
            }

            // Cleanup port dict - Remove nonexisting static ports - update static port types
            // Loop through current node ports
            foreach (NodePort port in ports.Values.ToList())
            {
                // If port still exists, check it it has been changed
                NodePort staticPort;
                if (staticPorts.TryGetValue(port.fieldName, out staticPort))
                {
                    // If port exists but with wrong settings, remove it. Re-add it later.
                    if (port.connectionType != staticPort.connectionType || port.IsDynamic || port.direction != staticPort.direction || port.typeConstraint != staticPort.typeConstraint)
                    {
                        ports.Remove(port.fieldName);
                    }
                    else
                    {
                        port.ValueType = staticPort.ValueType;
                    }
                }
                // If port doesn't exist anymore, remove it
                else if (port.IsStatic)
                {
                    ports.Remove(port.fieldName);
                }
            }
            // Add missing ports
            foreach (NodePort staticPort in staticPorts.Values)
            {
                if (!ports.ContainsKey(staticPort.fieldName))
                {
                    ports.Add(staticPort.fieldName, new NodePort(staticPort, node));
                }
            }
        }
Exemple #2
0
        /// <summary> Update static ports to reflect class fields. </summary>
        public static void UpdatePorts(Node node, Dictionary <string, NodePort> ports)
        {
            if (!Initialized)
            {
                BuildCache();
            }

            Dictionary <string, NodePort> staticPorts = new Dictionary <string, NodePort>();

            System.Type nodeType = node.GetType();

            if (portDataCache.ContainsKey(nodeType))
            {
                for (int i = 0; i < portDataCache[nodeType].Count; i++)
                {
                    staticPorts.Add(portDataCache[nodeType][i].fieldName, portDataCache[nodeType][i]);
                }
            }

            // Cleanup port dict - Remove nonexisting static ports - update static port types
            foreach (NodePort port in ports.Values.ToList())
            {
                if (staticPorts.ContainsKey(port.fieldName))
                {
                    NodePort staticPort = staticPorts[port.fieldName];
                    if (port.IsDynamic || port.direction != staticPort.direction)
                    {
                        ports.Remove(port.fieldName);
                    }
                    else
                    {
                        port.ValueType = staticPort.ValueType;
                    }
                }
                else if (port.IsStatic)
                {
                    ports.Remove(port.fieldName);
                }
            }
            // Add missing ports
            foreach (NodePort staticPort in staticPorts.Values)
            {
                if (!ports.ContainsKey(staticPort.fieldName))
                {
                    ports.Add(staticPort.fieldName, new NodePort(staticPort, node));
                }
            }
        }
Exemple #3
0
        /// <summary> Automatically delete Node sub-assets before deleting their script.
        /// <para/> This is important to do, because you can't delete null sub assets. </summary>
        private static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options)
        {
            // Get the object that is requested for deletion
            UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath <UnityEngine.Object> (path);

            // If we aren't deleting a script, return
            if (!(obj is UnityEditor.MonoScript))
            {
                return(AssetDeleteResult.DidNotDelete);
            }

            // Check script type. Return if deleting a non-node script
            UnityEditor.MonoScript script     = obj as UnityEditor.MonoScript;
            System.Type            scriptType = script.GetClass();
            if (scriptType == null || (scriptType != typeof(XNode.Node) && !scriptType.IsSubclassOf(typeof(XNode.Node))))
            {
                return(AssetDeleteResult.DidNotDelete);
            }

            // Find all ScriptableObjects using this script
            string[] guids = AssetDatabase.FindAssets("t:" + scriptType);
            for (int i = 0; i < guids.Length; i++)
            {
                string   assetpath = AssetDatabase.GUIDToAssetPath(guids[i]);
                Object[] objs      = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath);
                for (int k = 0; k < objs.Length; k++)
                {
                    XNode.Node node = objs[k] as XNode.Node;
                    if (node.GetType() == scriptType)
                    {
                        if (node != null && node.graph != null)
                        {
                            // 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);
                            node.graph.RemoveNode(node);
                        }
                    }
                }
            }
            // We didn't actually delete the script. Tell the internal system to carry on with normal deletion procedure
            return(AssetDeleteResult.DidNotDelete);
        }
        public static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options)
        {
            UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath <UnityEngine.Object>(path);

            if (!(obj is UnityEditor.MonoScript))
            {
                return(AssetDeleteResult.DidNotDelete);
            }

            UnityEditor.MonoScript script     = obj as UnityEditor.MonoScript;
            System.Type            scriptType = script.GetClass();

            if (scriptType != typeof(XNode.Node) && !scriptType.IsSubclassOf(typeof(XNode.Node)))
            {
                return(AssetDeleteResult.DidNotDelete);
            }

            //Find ScriptableObjects using this script
            string[] guids = AssetDatabase.FindAssets("t:" + scriptType);
            for (int i = 0; i < guids.Length; i++)
            {
                string   assetpath = AssetDatabase.GUIDToAssetPath(guids[i]);
                Object[] objs      = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath);
                for (int k = 0; k < objs.Length; k++)
                {
                    XNode.Node node = objs[k] as XNode.Node;
                    if (node.GetType() == scriptType)
                    {
                        if (node != null && node.graph != null)
                        {
                            Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
                            node.graph.RemoveNode(node);
                        }
                    }
                }
            }
            return(AssetDeleteResult.DidNotDelete);
        }
Exemple #5
0
        /// <summary> Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. </summary>
        public static void UpdatePorts(Node node, Dictionary <string, NodePort> ports)
        {
            if (!Initialized)
            {
                BuildCache();
            }

            Dictionary <string, NodePort>         staticPorts  = new Dictionary <string, NodePort>();
            Dictionary <string, List <NodePort> > removedPorts = new Dictionary <string, List <NodePort> >();

            System.Type nodeType = node.GetType();

            Dictionary <string, string> formerlySerializedAs = null;

            if (formerlySerializedAsCache != null)
            {
                formerlySerializedAsCache.TryGetValue(nodeType, out formerlySerializedAs);
            }

            List <NodePort> dynamicListPorts = new List <NodePort>();

            List <NodePort> typePortCache;

            if (portDataCache.TryGetValue(nodeType, out typePortCache))
            {
                for (int i = 0; i < typePortCache.Count; i++)
                {
                    staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]);
                }
            }

            // Cleanup port dict - Remove nonexisting static ports - update static port types
            // AND update dynamic ports (albeit only those in lists) too, in order to enforce proper serialisation.
            // Loop through current node ports
            foreach (NodePort port in ports.Values.ToList())
            {
                // If port still exists, check it it has been changed
                NodePort staticPort;
                if (staticPorts.TryGetValue(port.fieldName, out staticPort))
                {
                    // If port exists but with wrong settings, remove it. Re-add it later.
                    if (port.IsDynamic || port.direction != staticPort.direction || port.connectionType != staticPort.connectionType || port.typeConstraint != staticPort.typeConstraint)
                    {
                        // If port is not dynamic and direction hasn't changed, add it to the list so we can try reconnecting the ports connections.
                        if (!port.IsDynamic && port.direction == staticPort.direction)
                        {
                            removedPorts.Add(port.fieldName, port.GetConnections());
                        }
                        port.ClearConnections();
                        ports.Remove(port.fieldName);
                    }
                    else
                    {
                        port.ValueType = staticPort.ValueType;
                    }
                }
                // If port doesn't exist anymore, remove it
                else if (port.IsStatic)
                {
                    //See if the field is tagged with FormerlySerializedAs, if so add the port with its new field name to removedPorts
                    // so it can be reconnected in missing ports stage.
                    string newName = null;
                    if (formerlySerializedAs != null && formerlySerializedAs.TryGetValue(port.fieldName, out newName))
                    {
                        removedPorts.Add(newName, port.GetConnections());
                    }

                    port.ClearConnections();
                    ports.Remove(port.fieldName);
                }
                // If the port is dynamic and is managed by a dynamic port list, flag it for reference updates
                else if (IsDynamicListPort(port))
                {
                    dynamicListPorts.Add(port);
                }
            }
            // Add missing ports
            foreach (NodePort staticPort in staticPorts.Values)
            {
                if (!ports.ContainsKey(staticPort.fieldName))
                {
                    NodePort port = new NodePort(staticPort, node);
                    //If we just removed the port, try re-adding the connections
                    List <NodePort> reconnectConnections;
                    if (removedPorts.TryGetValue(staticPort.fieldName, out reconnectConnections))
                    {
                        for (int i = 0; i < reconnectConnections.Count; i++)
                        {
                            NodePort connection = reconnectConnections[i];
                            if (connection == null)
                            {
                                continue;
                            }
                            if (port.CanConnectTo(connection))
                            {
                                port.Connect(connection);
                            }
                        }
                    }
                    ports.Add(staticPort.fieldName, port);
                }
            }

            // Finally, make sure dynamic list port settings correspond to the settings of their "backing port"
            foreach (NodePort listPort in dynamicListPorts)
            {
                // At this point we know that ports here are dynamic list ports
                // which have passed name/"backing port" checks, ergo we can proceed more safely.
                string   backingPortName = listPort.fieldName.Split(' ')[0];
                NodePort backingPort     = staticPorts[backingPortName];

                // Update port constraints. Creating a new port instead will break the editor, mandating the need for setters.
                listPort.ValueType      = GetBackingValueType(backingPort.ValueType);
                listPort.direction      = backingPort.direction;
                listPort.connectionType = backingPort.connectionType;
                listPort.typeConstraint = backingPort.typeConstraint;
            }
        }
Exemple #6
0
        private void DrawNodes()
        {
            Event e = Event.current;

            if (e.type == EventType.Layout)
            {
                selectionCache = new List <UnityEngine.Object>(Selection.objects);
            }

            System.Reflection.MethodInfo onValidate = null;
            if (Selection.activeObject != null && Selection.activeObject is XNode.Node)
            {
                onValidate = Selection.activeObject.GetType().GetMethod("OnValidate");
                if (onValidate != null)
                {
                    EditorGUI.BeginChangeCheck();
                }
            }

            BeginZoomed(position, zoom, topPadding);

            Vector2 mousePos = Event.current.mousePosition;

            if (e.type != EventType.Layout)
            {
                hoveredNode = null;
                hoveredPort = null;
                hoveredLink = null;
            }

            List <UnityEngine.Object> preSelection = preBoxSelection != null ? new List <UnityEngine.Object>(preBoxSelection) : new List <UnityEngine.Object>();

            // Selection box stuff
            Vector2 boxStartPos = GridToWindowPositionNoClipped(dragBoxStart);
            Vector2 boxSize     = mousePos - boxStartPos;

            if (boxSize.x < 0)
            {
                boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x);
            }
            if (boxSize.y < 0)
            {
                boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y);
            }
            Rect selectionBox = new Rect(boxStartPos, boxSize);

            //Save guiColor so we can revert it
            Color guiColor = GUI.color;

            List <XNode.NodePort> removeEntries = new List <XNode.NodePort>();

            if (e.type == EventType.Layout)
            {
                culledNodes = new List <XNode.Node>();
            }

            Action <NodeLinkPort> selectLinkIfHovered = link =>
            {
                if (link == null || !linkConnectionPoints.ContainsKey(link))
                {
                    return;
                }
                Rect r = GridToWindowRectNoClipped(linkConnectionPoints[link]);
                if (r.Contains(mousePos))
                {
                    hoveredLink = link;
                }
            };

            for (int n = 0; n < graph.nodes.Count; n++)
            {
                // 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 (n >= graph.nodes.Count)
                {
                    return;
                }
                XNode.Node node = graph.nodes[n];

                // Culling
                if (e.type == EventType.Layout)
                {
                    // Cull unselected nodes outside view
                    if (!Selection.Contains(node) && ShouldBeCulled(node))
                    {
                        culledNodes.Add(node);
                        continue;
                    }
                }
                else if (culledNodes.Contains(node))
                {
                    continue;
                }

                if (e.type == EventType.Repaint)
                {
                    removeEntries.Clear();
                    foreach (var kvp in portConnectionPoints)
                    {
                        if (kvp.Key.node == node)
                        {
                            removeEntries.Add(kvp.Key);
                        }
                    }
                    foreach (var k in removeEntries)
                    {
                        portConnectionPoints.Remove(k);
                    }
                }

                NodeEditor nodeEditor = NodeEditor.GetEditor(node, this);

                NodeEditor.portPositions.Clear();

                // Set default label width. This is potentially overridden in OnBodyGUI
                EditorGUIUtility.labelWidth = 84;

                //Get node position
                Vector2 nodePos = GridToWindowPositionNoClipped(node.position);

                GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000)));

                bool selected = selectionCache.Contains(graph.nodes[n]);

                if (selected)
                {
                    GUIStyle style          = new GUIStyle(nodeEditor.GetBodyStyle());
                    GUIStyle highlightStyle = new GUIStyle(nodeEditor.GetBodyHighlightStyle());
                    highlightStyle.padding = style.padding;
                    style.padding          = new RectOffset();
                    GUI.color = nodeEditor.GetTint();
                    GUILayout.BeginVertical(style);
                    GUI.color = NodeEditorPreferences.GetSettings().highlightColor;
                    GUILayout.BeginVertical(new GUIStyle(highlightStyle));
                }
                else
                {
                    GUIStyle style = new GUIStyle(nodeEditor.GetBodyStyle());
                    GUI.color = nodeEditor.GetTint();
                    GUILayout.BeginVertical(style);
                }

                GUI.color = guiColor;
                EditorGUI.BeginChangeCheck();

                //Draw node contents
                nodeEditor.OnHeaderGUI();
                nodeEditor.OnBodyGUI();

                //If user changed a value, notify other scripts through onUpdateNode
                if (EditorGUI.EndChangeCheck())
                {
                    if (NodeEditor.onUpdateNode != null)
                    {
                        NodeEditor.onUpdateNode(node);
                    }
                    EditorUtility.SetDirty(node);
                    nodeEditor.serializedObject.ApplyModifiedProperties();
                }

                GUILayout.EndVertical();

                //Cache data about the node for next frame
                if (e.type == EventType.Repaint)
                {
                    Vector2 size = GUILayoutUtility.GetLastRect().size;
                    if (nodeSizes.ContainsKey(node))
                    {
                        nodeSizes[node] = size;
                    }
                    else
                    {
                        nodeSizes.Add(node, size);
                    }

                    foreach (var kvp in NodeEditor.portPositions)
                    {
                        Vector2 portHandlePos = kvp.Value;
                        portHandlePos += node.position;
                        Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16);
                        portConnectionPoints[kvp.Key] = rect;
                    }

                    foreach (var kvp in NodeEditor.linkPositions)
                    {
                        if (kvp.Key.Node != node)
                        {
                            continue;
                        }
                        Vector2 portHandlePos = kvp.Value;
                        portHandlePos += node.position;
                        Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16);
                        linkConnectionPoints[kvp.Key] = rect;
                    }
                }

                if (selected)
                {
                    GUILayout.EndVertical();
                }

                if (e.type != EventType.Layout)
                {
                    //Check if we are hovering this node
                    Vector2 nodeSize   = GUILayoutUtility.GetLastRect().size;
                    Rect    windowRect = new Rect(nodePos, nodeSize);
                    if (windowRect.Contains(mousePos))
                    {
                        hoveredNode = node;
                    }

                    //If dragging a selection box, add nodes inside to selection
                    if (currentActivity == NodeActivity.DragGrid)
                    {
                        if (windowRect.Overlaps(selectionBox))
                        {
                            preSelection.Add(node);
                        }
                    }

                    //Check if we are hovering any of this nodes ports
                    //Check input ports
                    foreach (XNode.NodePort input in node.Inputs)
                    {
                        //Check if port rect is available
                        if (!portConnectionPoints.ContainsKey(input))
                        {
                            continue;
                        }
                        Rect r = GridToWindowRectNoClipped(portConnectionPoints[input]);
                        if (r.Contains(mousePos))
                        {
                            hoveredPort = input;
                        }
                    }
                    //Check all output ports
                    foreach (XNode.NodePort output in node.Outputs)
                    {
                        //Check if port rect is available
                        if (!portConnectionPoints.ContainsKey(output))
                        {
                            continue;
                        }
                        Rect r = GridToWindowRectNoClipped(portConnectionPoints[output]);
                        if (r.Contains(mousePos))
                        {
                            hoveredPort = output;
                        }
                    }

                    var links = NodeDataCache.GetLinks(node.GetType()).Select(x => new NodeLinkPort(node, x));
                    foreach (NodeLinkPort link in links)
                    {
                        selectLinkIfHovered(link);
                    }
                }

                var selectedLink = Selection.activeObject as NodeLink;
                selectLinkIfHovered(selectedLink?.GetFromPort());
                selectLinkIfHovered(selectedLink?.GetToPort());

                GUILayout.EndArea();
            }

            if (e.type != EventType.Layout && currentActivity == NodeActivity.DragGrid)
            {
                Selection.objects = preSelection.ToArray();
            }
            EndZoomed(position, zoom, topPadding);

            //If a change in is detected in the selected node, call OnValidate method.
            //This is done through reflection because OnValidate is only relevant in editor,
            //and thus, the code should not be included in build.
            if (onValidate != null && EditorGUI.EndChangeCheck())
            {
                onValidate.Invoke(Selection.activeObject, null);
            }
        }
Exemple #7
0
        /// <summary> Update static ports to reflect class fields. </summary>
        public static void UpdatePorts(Node node, Dictionary <string, NodePort> ports)
        {
            if (!Initialized)
            {
                BuildCache();
            }

            Dictionary <string, NodePort>         staticPorts  = new Dictionary <string, NodePort>();
            Dictionary <string, List <NodePort> > removedPorts = new Dictionary <string, List <NodePort> >();

            System.Type nodeType = node.GetType();

            List <NodePort> typePortCache;

            if (portDataCache.TryGetValue(nodeType, out typePortCache))
            {
                for (int i = 0; i < typePortCache.Count; i++)
                {
                    staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]);
                }
            }

            // Cleanup port dict - Remove nonexisting static ports - update static port types
            // Loop through current node ports
            foreach (NodePort port in ports.Values.ToList())
            {
                // If port still exists, check it it has been changed
                NodePort staticPort;
                if (staticPorts.TryGetValue(port.fieldName, out staticPort))
                {
                    // If port exists but with wrong settings, remove it. Re-add it later.
                    if (port.IsDynamic || port.direction != staticPort.direction || port.connectionType != staticPort.connectionType || port.typeConstraint != staticPort.typeConstraint)
                    {
                        // If port is not dynamic and direction hasn't changed, add it to the list so we can try reconnecting the ports connections.
                        if (!port.IsDynamic && port.direction == staticPort.direction)
                        {
                            removedPorts.Add(port.fieldName, port.GetConnections());
                        }
                        port.ClearConnections();
                        ports.Remove(port.fieldName);
                    }
                    else
                    {
                        port.ValueType = staticPort.ValueType;
                    }
                }
                // If port doesn't exist anymore, remove it
                else if (port.IsStatic)
                {
                    port.ClearConnections();
                    ports.Remove(port.fieldName);
                }
            }
            // Add missing ports
            foreach (NodePort staticPort in staticPorts.Values)
            {
                if (!ports.ContainsKey(staticPort.fieldName))
                {
                    NodePort port = new NodePort(staticPort, node);
                    //If we just removed the port, try re-adding the connections
                    List <NodePort> reconnectConnections;
                    if (removedPorts.TryGetValue(staticPort.fieldName, out reconnectConnections))
                    {
                        for (int i = 0; i < reconnectConnections.Count; i++)
                        {
                            NodePort connection = reconnectConnections[i];
                            if (connection == null)
                            {
                                continue;
                            }
                            if (port.CanConnectTo(connection))
                            {
                                port.Connect(connection);
                            }
                        }
                    }
                    ports.Add(staticPort.fieldName, port);
                }
            }
        }
Exemple #8
0
 /// <summary>
 /// Checks if the output/input field is the same type as <paramref name="connectingNode"/>
 /// </summary>
 /// <param name="connectingNode"></param>
 /// <returns></returns>
 private bool IsBackingValueTypeSameAsConnectingNode(Node connectingNode)
 {
     return(connectingNode.GetType().IsSubclassOf(Type.GetType(_typeQualifiedName)));
 }