616 lines
20 KiB
C#
616 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using AssetGraph.Core.Attributes;
|
|
using AssetGraph.Core.GraphElements;
|
|
using AssetGraph.Nodes;
|
|
using ImageProcessingGraph.Editor;
|
|
using UnityEditor;
|
|
using UnityEditor.Experimental.GraphView;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace AssetGraph.Core
|
|
{
|
|
public class AssetGraphViewWindow : GraphView
|
|
{
|
|
internal AssetGraphData asset;
|
|
private SerializedObject serializedObject;
|
|
private AssetGraphEditorWindow window;
|
|
public AssetGraphEditorWindow Window => window;
|
|
|
|
public List<AssetGraphNodeEditor> graphNodes;
|
|
public Dictionary<string, AssetGraphNodeEditor> nodeDictionary;
|
|
public Dictionary<Edge, GraphConnection> connectionDictionary;
|
|
public Dictionary<string, AssetGraphStickyNote> stickyNoteDictionary;
|
|
|
|
internal AssetGraphSearchProvider searchProvider;
|
|
public AssetGraphEdgeConnector edgeConnectorListener;
|
|
|
|
private bool isDropdownEnabled = false;
|
|
public Vector2 cachedMousePos;
|
|
|
|
|
|
private Button debugModeButton;
|
|
private Button outputRunOrder;
|
|
private Button makeStickyNode;
|
|
|
|
public AssetGraphViewWindow(SerializedObject obeject, AssetGraphEditorWindow window)
|
|
{
|
|
this.serializedObject = obeject;
|
|
this.asset = obeject.targetObject as AssetGraphData;
|
|
|
|
this.graphNodes = new List<AssetGraphNodeEditor>();
|
|
nodeDictionary = new Dictionary<string, AssetGraphNodeEditor>();
|
|
stickyNoteDictionary = new Dictionary<string, AssetGraphStickyNote>();
|
|
connectionDictionary = new Dictionary<Edge, GraphConnection>();
|
|
|
|
searchProvider = ScriptableObject.CreateInstance<AssetGraphSearchProvider>();
|
|
searchProvider.graph = this;
|
|
|
|
edgeConnectorListener = new AssetGraphEdgeConnector(this);
|
|
|
|
this.nodeCreationRequest = ShowSearchWindow;
|
|
|
|
this.window = window;
|
|
|
|
|
|
|
|
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/GraphView.uss");
|
|
if (styleSheet == null)
|
|
{
|
|
styleSheet = EditorGUIUtility.Load("Packages/com.chromium.imageprocessingrah/GraphView.uss") as StyleSheet;
|
|
}
|
|
styleSheets.Add(styleSheet);
|
|
|
|
GridBackground background = new GridBackground();
|
|
background.name = "Grid";
|
|
this.Add(background);
|
|
background.SendToBack();
|
|
|
|
CreateButtons();
|
|
|
|
|
|
this.AddManipulator(new ContentDragger());
|
|
this.AddManipulator(new SelectionDragger());
|
|
this.AddManipulator(new RectangleSelector());
|
|
this.AddManipulator(new ClickSelector());
|
|
this.AddManipulator(new ContentZoomer() );
|
|
|
|
DrawNodes();
|
|
DrawConnections();
|
|
DrawStickyNotes();
|
|
|
|
graphViewChanged += OnGraphViewChanged;
|
|
|
|
Undo.undoRedoPerformed += UndoRedoPerformed;
|
|
RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
|
}
|
|
|
|
private void OnMouseMove(MouseMoveEvent evt)
|
|
{
|
|
cachedMousePos = evt.localMousePosition;
|
|
}
|
|
|
|
|
|
private void UndoRedoPerformed()
|
|
{
|
|
DrawNodes();
|
|
DrawConnections();
|
|
DrawStickyNotes();
|
|
}
|
|
|
|
private void CreateButtons()
|
|
{
|
|
#region Run Button
|
|
Button runButton = new Button
|
|
{
|
|
text = "Run",
|
|
style =
|
|
{
|
|
width = 100,
|
|
height = 40,
|
|
position = Position.Absolute,
|
|
top = 10,
|
|
left = 10
|
|
}
|
|
};
|
|
|
|
runButton.clicked += () =>
|
|
{
|
|
asset.RunGraph();
|
|
};
|
|
|
|
Add(runButton);
|
|
#endregion
|
|
|
|
#if false
|
|
debugModeButton = new Button
|
|
{
|
|
text = "Debug Mode",
|
|
style =
|
|
{
|
|
width = 120,
|
|
height = 40,
|
|
position = Position.Absolute,
|
|
top = 55,
|
|
right = 10
|
|
}
|
|
};
|
|
|
|
debugModeButton.clicked += () =>
|
|
{
|
|
|
|
};
|
|
#endif
|
|
|
|
outputRunOrder = new Button
|
|
{
|
|
text = "Recalculate Run Order",
|
|
style =
|
|
{
|
|
width = 120,
|
|
height = 40,
|
|
position = Position.Absolute,
|
|
top = 55,
|
|
right = 10,
|
|
fontSize = 9
|
|
}
|
|
};
|
|
|
|
outputRunOrder.clicked += () =>
|
|
{
|
|
asset.runOrder = asset.GetExecutionOrder(asset.Nodes, asset.Connections);
|
|
};
|
|
|
|
makeStickyNode = new Button
|
|
{
|
|
text = "Make Sticky Node",
|
|
style =
|
|
{
|
|
width = 120,
|
|
height = 40,
|
|
position = Position.Absolute,
|
|
top = 55+45,
|
|
right = 10,
|
|
fontSize = 9
|
|
}
|
|
};
|
|
|
|
makeStickyNode.clicked += () =>
|
|
{
|
|
var noteData = new AssetGraphStickyNoteData("");
|
|
asset.stickyNotes.Add(noteData);
|
|
this.DrawStickyNote(noteData);
|
|
};
|
|
|
|
|
|
Button dropdownButton = new Button
|
|
{
|
|
text = "Options",
|
|
style =
|
|
{
|
|
width = 120,
|
|
height = 40,
|
|
position = Position.Absolute,
|
|
top = 10,
|
|
right = 10
|
|
}
|
|
};
|
|
|
|
dropdownButton.clicked+= () =>
|
|
{
|
|
if (!isDropdownEnabled)
|
|
{
|
|
#if false
|
|
Add(debugModeButton);
|
|
#endif
|
|
Add(outputRunOrder);
|
|
Add(makeStickyNode);
|
|
}
|
|
else
|
|
{
|
|
#if false
|
|
Remove(debugModeButton);
|
|
#endif
|
|
Remove(outputRunOrder);
|
|
Remove(makeStickyNode);
|
|
}
|
|
|
|
isDropdownEnabled = !isDropdownEnabled;
|
|
};
|
|
|
|
|
|
Add(dropdownButton);
|
|
}
|
|
|
|
private AssetGraphNodeEditor GetNode(string NodeID)
|
|
{
|
|
AssetGraphNodeEditor node = null;
|
|
nodeDictionary.TryGetValue(NodeID, out node);
|
|
return node;
|
|
}
|
|
|
|
public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
|
|
{
|
|
List<Port> compatiblePorts = new List<Port>();
|
|
|
|
foreach (var node in graphNodes)
|
|
{
|
|
if (node == startPort.node)
|
|
continue;
|
|
|
|
var ports = node.inputContainer.Children()
|
|
.Concat(node.outputContainer.Children())
|
|
.OfType<Port>();
|
|
|
|
foreach (var port in ports)
|
|
{
|
|
if (startPort.direction == port.direction)
|
|
continue;
|
|
|
|
Type typeA = GetPortEffectiveType(startPort);
|
|
Type typeB = GetPortEffectiveType(port);
|
|
|
|
|
|
// if (typeA == typeof(object))
|
|
// {
|
|
// var BaseGenPort = startPort.node as ImageProcessingGraphNodeVisual;
|
|
// var GenPort = BaseGenPort.GraphNode as GenericConnection;
|
|
//
|
|
// if (GenPort.InternalType != null)
|
|
// {
|
|
// if(!GenPort.InternalType.IsAssignableFrom(typeB))
|
|
// continue;
|
|
// }
|
|
// }
|
|
//
|
|
// if (typeB == typeof(object))
|
|
// {
|
|
// var BaseGenPort = port.node as ImageProcessingGraphNodeVisual;
|
|
// var GenPort = BaseGenPort.GraphNode as GenericConnection;
|
|
//
|
|
// if (GenPort.InternalType != null)
|
|
// {
|
|
// if(!GenPort.InternalType.IsAssignableFrom(typeA))
|
|
// continue;
|
|
// }
|
|
// }
|
|
|
|
if (!typeA.IsAssignableFrom(typeB) && !typeB.IsAssignableFrom(typeA))
|
|
continue;
|
|
|
|
if (startPort.direction == Direction.Output && CreatesCycle(startPort, port))
|
|
continue;
|
|
if (startPort.direction == Direction.Input && CreatesCycle(port, startPort))
|
|
continue;
|
|
|
|
compatiblePorts.Add(port);
|
|
}
|
|
}
|
|
|
|
return compatiblePorts;
|
|
}
|
|
|
|
|
|
private Type GetPortEffectiveType(Port port)
|
|
{
|
|
if (port is AssetGraphPort iptPort)
|
|
{
|
|
if (iptPort.node is AssetGraphNodeEditor vis &&
|
|
vis.GraphNode is GenericConnection conn &&
|
|
conn.InternalType != null)
|
|
{
|
|
return conn.InternalType;
|
|
}
|
|
return port.portType ?? typeof(object);
|
|
}
|
|
|
|
return port.portType ?? typeof(object);
|
|
}
|
|
|
|
|
|
|
|
private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange)
|
|
{
|
|
|
|
if (graphviewchange.movedElements != null)
|
|
{
|
|
Undo.RecordObject(serializedObject.targetObject, "Moved Graph Elements");
|
|
foreach (var VARIABLE in graphviewchange.movedElements.OfType<AssetGraphNodeEditor>())
|
|
{
|
|
VARIABLE.SavePosition();
|
|
}
|
|
|
|
foreach (var VARIABLE in graphviewchange.movedElements.OfType<AssetGraphStickyNote>())
|
|
{
|
|
VARIABLE.SavePosition();
|
|
}
|
|
}
|
|
|
|
if (graphviewchange.elementsToRemove != null)
|
|
{
|
|
List<AssetGraphNodeEditor> nodesToRemove = graphviewchange.elementsToRemove.OfType<AssetGraphNodeEditor>().ToList();
|
|
List<Edge> edges = graphviewchange.elementsToRemove.OfType<Edge>().ToList();
|
|
|
|
if (nodesToRemove.Count > 0)
|
|
{
|
|
Undo.RecordObject(serializedObject.targetObject, "Remove Node");
|
|
|
|
foreach (var VARIABLE in nodesToRemove)
|
|
{
|
|
RemoveNode(VARIABLE);
|
|
}
|
|
}
|
|
|
|
|
|
if (edges.Count > 0)
|
|
{
|
|
Undo.RecordObject(serializedObject.targetObject, "Remove Edge");
|
|
|
|
foreach (var VARIABLE in edges)
|
|
{
|
|
RemoveEdge(VARIABLE);
|
|
}
|
|
}
|
|
|
|
foreach (var VARIABLE in graphviewchange.elementsToRemove.OfType<Edge>())
|
|
{
|
|
RemoveEdge(VARIABLE);
|
|
}
|
|
|
|
}
|
|
|
|
if (graphviewchange.edgesToCreate != null)
|
|
{
|
|
foreach (Edge edge in graphviewchange.edgesToCreate)
|
|
{
|
|
Undo.RecordObject(serializedObject.targetObject, "Created Connection");
|
|
CreateEdge(edge);
|
|
}
|
|
}
|
|
|
|
return graphviewchange;
|
|
}
|
|
|
|
#region Edges
|
|
|
|
public void CreateEdge(Edge edge)
|
|
{
|
|
AssetGraphNodeEditor outputNode = (AssetGraphNodeEditor)edge.output.node;
|
|
AssetGraphNodeEditor inputNode = (AssetGraphNodeEditor)edge.input.node;
|
|
|
|
|
|
int outputIndex = outputNode.OutputPorts.IndexOf(edge.output);
|
|
int inputIndex = inputNode.InputPorts.IndexOf(edge.input);
|
|
|
|
string inputType = inputNode.GraphNode.GetType().Name;
|
|
string outputType = outputNode.GraphNode.GetType().Name;
|
|
|
|
GraphConnection connection = new GraphConnection(inputNode.GraphNode.ID,inputIndex, inputType,outputNode.GraphNode.ID, outputIndex, outputType, edge);
|
|
|
|
edge.output.Connect(edge);
|
|
edge.input.Connect(edge);
|
|
|
|
AssetGraphPort portIn = (AssetGraphPort)edge.output;
|
|
AssetGraphPort portOut = (AssetGraphPort)edge.output;
|
|
|
|
if (portIn.fieldInfo != null)
|
|
inputNode.ToggleExposedVariable(edge.input, false);
|
|
|
|
asset.Connections.Add(connection);
|
|
connectionDictionary.Add(edge, connection);
|
|
}
|
|
|
|
private void RemoveEdge(Edge edge)
|
|
{
|
|
if (connectionDictionary.TryGetValue(edge, out GraphConnection connection))
|
|
{
|
|
asset.Connections.Remove(connection);
|
|
connectionDictionary.Remove(edge);
|
|
|
|
edge.output.Disconnect(edge);
|
|
edge.input.Disconnect(edge);
|
|
|
|
AssetGraphNodeEditor inputNode = (AssetGraphNodeEditor)edge.input.node;
|
|
|
|
AssetGraphPort portIn = (AssetGraphPort)edge.input;
|
|
|
|
if(portIn.fieldInfo != null)
|
|
inputNode.ToggleExposedVariable(edge.input, true);
|
|
}
|
|
}
|
|
|
|
private void DrawConnections()
|
|
{
|
|
foreach (KeyValuePair<Edge, GraphConnection> node in connectionDictionary)
|
|
{
|
|
RemoveElement(node.Key);
|
|
}
|
|
connectionDictionary.Clear();
|
|
|
|
if (asset.Connections != null)
|
|
{
|
|
foreach (GraphConnection conn in asset.Connections)
|
|
{
|
|
AssetGraphNodeEditor inputNode = GetNode(conn.inputPort.nodeID);
|
|
AssetGraphNodeEditor outputNode = GetNode(conn.outputPort.nodeID);
|
|
|
|
if (inputNode != null && outputNode != null)
|
|
{
|
|
AssetGraphPort inPort = inputNode.InputPorts[conn.inputPort.portID] as AssetGraphPort;
|
|
AssetGraphPort outPort = outputNode.OutputPorts[conn.outputPort.portID] as AssetGraphPort;
|
|
|
|
Edge edge = inPort.ConnectTo(outPort);
|
|
AddElement(edge);
|
|
connectionDictionary.Add(edge, conn);
|
|
conn.SetInternalEdge(edge);
|
|
|
|
((AssetGraphNodeEditor)inPort.node).ToggleExposedVariable(inPort, false);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private bool DoesConversionNodeExist()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#region Cycle Logic
|
|
|
|
private bool CreatesCycle(Port fromPort, Port toPort)
|
|
{
|
|
var visited = new HashSet<Node>();
|
|
return HasPathTo(toPort.node, fromPort.node, visited);
|
|
}
|
|
|
|
private bool HasPathTo(Node current, Node target, HashSet<Node> visited)
|
|
{
|
|
if (current == target)
|
|
return true;
|
|
|
|
if (visited.Contains(current))
|
|
return false;
|
|
|
|
visited.Add(current);
|
|
|
|
foreach (var outputPort in current.outputContainer.Children().OfType<Port>())
|
|
{
|
|
foreach (var edge in outputPort.connections)
|
|
{
|
|
var nextNode = edge.input.node;
|
|
if (HasPathTo(nextNode, target, visited))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Nodes
|
|
private void DrawNodes()
|
|
{
|
|
foreach (AssetGraphNodeEditor node in graphNodes)
|
|
{
|
|
RemoveElement(node);
|
|
}
|
|
graphNodes.Clear();
|
|
|
|
foreach (KeyValuePair<string, AssetGraphNodeEditor> node in nodeDictionary)
|
|
{
|
|
RemoveElement(node.Value);
|
|
}
|
|
nodeDictionary.Clear();
|
|
|
|
foreach (var variable in asset.Nodes)
|
|
{
|
|
AddNodeToGraph(variable);
|
|
}
|
|
}
|
|
|
|
private void AddNodeToGraph(AssetGraphNode node)
|
|
{
|
|
node.typeName = node.GetType().AssemblyQualifiedName;
|
|
|
|
var infoAttr = node.GetType().GetCustomAttribute<NodeInfoAttribute>();
|
|
|
|
AssetGraphNodeEditor editorNode = null;
|
|
if (typeof(AssetGraphNodeEditor).IsAssignableFrom(infoAttr.EditorType))
|
|
{
|
|
editorNode = (AssetGraphNodeEditor)Activator.CreateInstance(infoAttr.EditorType, node, this);
|
|
}
|
|
else
|
|
{
|
|
editorNode = new AssetGraphNodeEditor(node, this);
|
|
}
|
|
|
|
editorNode.SetPosition(node.Position);
|
|
|
|
graphNodes.Add(editorNode);
|
|
nodeDictionary.Add(node.ID, editorNode);
|
|
|
|
|
|
AddElement(editorNode);
|
|
}
|
|
|
|
public void Add(AssetGraphNode node)
|
|
{
|
|
Undo.RecordObject(serializedObject.targetObject, "Added Node");
|
|
asset.Nodes.Add(node);
|
|
|
|
serializedObject.Update();
|
|
|
|
AddNodeToGraph(node);
|
|
|
|
}
|
|
|
|
private void RemoveNode(AssetGraphNodeEditor variable)
|
|
{
|
|
List<GraphConnection> connectionsToRemove = new List<GraphConnection>();
|
|
|
|
foreach (var connection in asset.Connections)
|
|
{
|
|
if (connection.inputPort.nodeID == variable.GraphNode.ID || connection.outputPort.nodeID == variable.GraphNode.ID)
|
|
connectionsToRemove.Add(connection);
|
|
}
|
|
|
|
foreach (var connection in connectionsToRemove)
|
|
{
|
|
asset.Connections.Remove(connection);
|
|
}
|
|
|
|
asset.Nodes.Remove(variable.GraphNode);
|
|
nodeDictionary.Remove(variable.GraphNode.ID);
|
|
graphNodes.Remove(variable);
|
|
serializedObject.Update();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Search Window
|
|
|
|
private void ShowSearchWindow(NodeCreationContext obj)
|
|
{
|
|
searchProvider.target = (VisualElement)focusController.focusedElement;
|
|
SearchWindow.Open(new SearchWindowContext(obj.screenMousePosition), searchProvider);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Sticky Note
|
|
|
|
public void DrawStickyNotes()
|
|
{
|
|
foreach (var stick in asset.stickyNotes)
|
|
{
|
|
this.stickyNoteDictionary.Add(stick.GUID, DrawStickyNote(stick));
|
|
}
|
|
}
|
|
|
|
public AssetGraphStickyNote DrawStickyNote(AssetGraphStickyNoteData stickyNote)
|
|
{
|
|
AssetGraphStickyNote stickyNoteVisual = new AssetGraphStickyNote(stickyNote, this);
|
|
|
|
stickyNoteVisual.SetPosition(stickyNote.Position);
|
|
stickyNoteVisual.style.width = stickyNote.Size.x;
|
|
stickyNoteVisual.style.height = stickyNote.Size.y;
|
|
|
|
this.Add(stickyNoteVisual);
|
|
return stickyNoteVisual;
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
}
|