浏览代码

[Material Graph]Add copy paste support via serializing temp graph and rewriting internal connectors. Next step: auto create sub graph.

/main
Tim Cooper 8 年前
当前提交
40ac12ad
共有 16 个文件被更改,包括 375 次插入54 次删除
  1. 8
      MaterialGraphProject/Assets/Canvas2D/Editor/CanvasElement.cs
  2. 40
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DeleteSelected.cs
  3. 17
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DrawableNode.cs
  4. 4
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/GraphDataSource.cs
  5. 3
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/GraphEditWindow.cs
  6. 18
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/NodeAnchor.cs
  7. 6
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Implementation/SerializableGraph.cs
  8. 22
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Implementation/SerializableNode.cs
  9. 7
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Implementation/SerializableSlot.cs
  10. 1
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Interfaces/INode.cs
  11. 4
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/NodeDrawers/SubGraphNodeUI.cs
  12. 26
      MaterialGraphProject/Assets/UnityShaderEditor/Runtime/Nodes/MaterialSlot.cs
  13. 233
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/CopySelected.cs
  14. 12
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/CopySelected.cs.meta
  15. 16
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DrawableEdge.cs
  16. 12
      MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DrawableEdge.cs.meta

8
MaterialGraphProject/Assets/Canvas2D/Editor/CanvasElement.cs


public event CanvasEvent DoubleClick;
public event CanvasEvent ScrollWheel;
public event CanvasEvent KeyDown;
public event CanvasEvent ValidateCommand;
public event CanvasEvent ExecuteCommand;
public event CanvasEvent OnWidget;
public event CanvasEvent ContextClick;
public event CanvasEvent DragPerform;

break;
case EventType.ContextClick:
handled = ContextClick == null ? false : ContextClick(target, evt, parent);
break;
case EventType.ValidateCommand:
handled = ValidateCommand == null ? false : ValidateCommand(target, evt, parent);
break;
case EventType.ExecuteCommand:
handled = ExecuteCommand == null ? false : ExecuteCommand(target, evt, parent);
break;
}

40
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DeleteSelected.cs


public delegate void DeleteElements(List<CanvasElement> elements);
private readonly DeleteElements m_DeletionCallback;
private readonly Canvas2D m_Canvas;
public DeleteSelected(DeleteElements deletionCallback, Canvas2D canvas)
public DeleteSelected(DeleteElements deletionCallback)
m_Canvas = canvas;
}
public bool GetCaps(ManipulatorCapability cap)

public void AttachTo(CanvasElement element)
{
element.KeyDown += KeyDown;
element.ValidateCommand += Validate;
element.ExecuteCommand += Delete;
private bool KeyDown(CanvasElement element, Event e, Canvas2D parent)
private bool Validate(CanvasElement element, Event e, Canvas2D parent)
if (e.keyCode == KeyCode.Delete)
if (e.commandName != "Delete" && e.commandName != "SoftDelete")
return false;
e.Use();
return true;
}
private bool Delete(CanvasElement element, Event e, Canvas2D parent)
{
if (e.type == EventType.Used)
return false;
if (e.commandName != "Delete" && e.commandName != "SoftDelete")
return false;
if (m_DeletionCallback != null)
if (m_DeletionCallback != null)
{
m_DeletionCallback(parent.selection);
m_Canvas.ReloadData();
m_Canvas.Repaint();
return true;
}
m_DeletionCallback(parent.selection);
parent.ReloadData();
parent.Repaint();
return false;
e.Use();
return true;
}
}
}

17
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DrawableNode.cs


public override void Render(Rect parentRect, Canvas2D canvas)
{
// some imgui elements eat key down events
// this is okay a lot of the time
// but for delete key it is not desired.
// if we have a delete key here
// just return. This allows us to get
// a validate / execute later on that can
// be handled by the graph.
var evt = Event.current;
if (evt.type == EventType.keyDown
&& (evt.keyCode == KeyCode.Backspace || evt.keyCode == KeyCode.Delete))
{
return;
}
if (evt.type == EventType.ValidateCommand || evt.type == EventType.ExecuteCommand)
return;
Color backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.7f);
Color selectedColor = new Color(1.0f, 0.7f, 0.0f, 0.7f);
EditorGUI.DrawRect(new Rect(0, 0, scale.x, scale.y), m_Node.hasError ? Color.red : selected ? selectedColor : backgroundColor);

4
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/GraphDataSource.cs


}
// Add the edges now
var drawableEdges = new List<Edge<NodeAnchor>>();
var drawableEdges = new List<DrawableEdge<NodeAnchor>>();
foreach (var drawableMaterialNode in m_DrawableNodes)
{
var baseNode = drawableMaterialNode.m_Node;

var toSlot = toNode.FindInputSlot<ISlot>(edge.inputSlot.slotName);
var targetNode = m_DrawableNodes.FirstOrDefault(x => x.m_Node == toNode);
var targetAnchor = (NodeAnchor)targetNode.Children().FirstOrDefault(x => x is NodeAnchor && ((NodeAnchor) x).m_Slot == toSlot);
drawableEdges.Add(new Edge<NodeAnchor>(this, sourceAnchor, targetAnchor));
drawableEdges.Add(new DrawableEdge<NodeAnchor>(edge, this, sourceAnchor, targetAnchor));
}
}
}

3
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/GraphEditWindow.cs


m_Canvas.AddManipulator(new ScreenSpaceGrid());
m_Canvas.AddManipulator(new ContextualMenu(DoAddNodeMenu));
m_Canvas.AddManipulator(new DeleteSelected(m_DataSource.DeleteElements, m_Canvas));
m_Canvas.AddManipulator(new DeleteSelected(m_DataSource.DeleteElements));
m_Canvas.AddManipulator(new CopySelected());
}
Rebuild();

18
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/NodeAnchor.cs


return Orientation.Horizontal;
}
/* private static string ConcreteSlotValueTypeAsString(ConcreteSlotValueType type)
{
switch (type)
{
case ConcreteSlotValueType.Vector1:
return "(1)";
case ConcreteSlotValueType.Vector2:
return "(2)";
case ConcreteSlotValueType.Vector3:
return "(3)";
case ConcreteSlotValueType.Vector4:
return "(4)";
default:
return "(E)";
}
}*/
public override void Render(Rect parentRect, Canvas2D canvas)
{
var anchorColor = Color.yellow;

6
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Implementation/SerializableGraph.cs


m_Nodes.Remove(node);
}
public virtual IEdge Connect(SlotReference fromSlotRef, SlotReference toSlotRef)
{
var fromNode = GetNodeFromGuid(fromSlotRef.nodeGuid);

public virtual void OnAfterDeserialize()
{
m_Nodes = SerializationHelper.Deserialize<INode>(m_SerializableNodes, new object[] {});
m_Nodes = SerializationHelper.Deserialize<INode>(m_SerializableNodes);
m_Edges = SerializationHelper.Deserialize<IEdge>(m_SerializableEdges, new object[] {});
m_Edges = SerializationHelper.Deserialize<IEdge>(m_SerializableEdges);
m_SerializableEdges = null;
ValidateGraph();

22
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Implementation/SerializableNode.cs


}
public virtual bool hasError { get; protected set; }
public SerializableNode()
{
m_DrawData.expanded = true;
m_Guid = Guid.NewGuid();
}
public Guid RewriteGuid()
{
m_Guid = Guid.NewGuid();
return m_Guid;
}
{}
{ }
public IEnumerable<T> GetInputSlots<T>() where T : ISlot
{

public IEnumerable<T> GetSlots<T>() where T : ISlot
{
return m_Slots.OfType<T>();
}
public SerializableNode()
{
m_DrawData.expanded = true;
m_Guid = Guid.NewGuid();
}
public virtual void AddSlot(ISlot slot)

7
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Implementation/SerializableSlot.cs


get { return m_Name; }
}
public string displayName
public virtual string displayName
{
get { return m_DisplayName; }
set { m_DisplayName = value; }

m_DisplayName = displayName;
m_SlotType = slotType;
m_Priority = priority;
}
public virtual bool OnGUI()
{
return false;
}
}
}

1
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Runtime/Interfaces/INode.cs


{
IGraph owner { get; set; }
Guid guid { get; }
Guid RewriteGuid();
string name { get; set; }
bool canDeleteNode { get; }
IEnumerable<T> GetInputSlots<T>() where T : ISlot;

4
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/NodeDrawers/SubGraphNodeUI.cs


[CustomNodeUI(typeof(SubGraphNode))]
public class SubgraphNodeUI : AbstractMaterialNodeUI
{
public float GetNodeUiHeight(float width)
public override float GetNodeUiHeight(float width)
return base.GetNodeUiHeight(width) + 2 * EditorGUIUtility.singleLineHeight;
return base.GetNodeUiHeight(width) + EditorGUIUtility.singleLineHeight;
}
public override GUIModificationType Render(Rect area)

26
MaterialGraphProject/Assets/UnityShaderEditor/Runtime/Nodes/MaterialSlot.cs


break;
}
}
private static string ConcreteSlotValueTypeAsString(ConcreteSlotValueType type)
{
switch (type)
{
case ConcreteSlotValueType.Vector1:
return "(1)";
case ConcreteSlotValueType.Vector2:
return "(2)";
case ConcreteSlotValueType.Vector3:
return "(3)";
case ConcreteSlotValueType.Vector4:
return "(4)";
default:
return "(E)";
}
}
public override string displayName
{
get { return base.displayName + ConcreteSlotValueTypeAsString(concreteValueType); }
set { base.displayName = value; }
}
public Vector4 defaultValue
{
get { return m_DefaultValue; }

233
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/CopySelected.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Experimental;
using UnityEngine;
using UnityEngine.Graphing;
namespace UnityEditor.Graphing.Drawing
{
[Serializable]
internal class CopyPasteGraph : ISerializationCallbackReceiver
{
[NonSerialized]
private List<IEdge> m_Edges = new List<IEdge>();
[NonSerialized]
private List<INode> m_Nodes = new List<INode>();
[SerializeField]
List<SerializationHelper.JSONSerializedElement> m_SerializableNodes = new List<SerializationHelper.JSONSerializedElement>();
[SerializeField]
List<SerializationHelper.JSONSerializedElement> m_SerializableEdges = new List<SerializationHelper.JSONSerializedElement>();
public virtual void AddNode(INode node)
{
m_Nodes.Add(node);
}
public void AddEdge(IEdge edge)
{
m_Edges.Add(edge);
}
public IEnumerable<T> GetNodes<T>() where T : INode
{
return m_Nodes.OfType<T>();
}
public IEnumerable<IEdge> edges
{
get { return m_Edges; }
}
public virtual void OnBeforeSerialize()
{
m_SerializableNodes = SerializationHelper.Serialize(m_Nodes);
m_SerializableEdges = SerializationHelper.Serialize(m_Edges);
}
public virtual void OnAfterDeserialize()
{
m_Nodes = SerializationHelper.Deserialize<INode>(m_SerializableNodes);
m_SerializableNodes = null;
m_Edges = SerializationHelper.Deserialize<IEdge>(m_SerializableEdges);
m_SerializableEdges = null;
}
}
internal class CopySelected : IManipulate
{
public delegate void CopyElements(List<CanvasElement> elements);
public bool GetCaps(ManipulatorCapability cap)
{
return false;
}
public void AttachTo(CanvasElement element)
{
element.ValidateCommand += Validate;
element.ExecuteCommand += CopyPaste;
}
private bool Validate(CanvasElement element, Event e, Canvas2D parent)
{
if (e.type == EventType.Used)
return false;
if (e.commandName != "Copy" && e.commandName != "Paste")
return false;
e.Use();
return true;
}
private bool CopyPaste(CanvasElement element, Event e, Canvas2D parent)
{
if (e.type == EventType.Used)
return false;
if (e.commandName != "Copy" && e.commandName != "Paste")
return false;
if (e.commandName == "Copy")
DoCopy(parent);
if (e.commandName == "Paste")
DoPaste(parent);
e.Use();
return true;
}
private void DoCopy(Canvas2D parent)
{
var selectedElements = parent.selection;
// build a graph to serialize (will just contain the
// nodes and edges we are interested in.
var graph = new CopyPasteGraph();
foreach (var thing in selectedElements)
{
var dNode = thing as DrawableNode;
if (dNode != null)
{
graph.AddNode(dNode.m_Node);
continue;
}
var dEdge = thing as DrawableEdge<NodeAnchor>;
if (dEdge != null)
{
graph.AddEdge(dEdge.m_Edge);
}
}
// serialize then break references
var serialized = JsonUtility.ToJson(graph, true);
EditorGUIUtility.systemCopyBuffer = serialized;
}
private void DoPaste(Canvas2D parent)
{
var copyText = EditorGUIUtility.systemCopyBuffer;
if (string.IsNullOrEmpty(copyText))
return;
CopyPasteGraph pastedGraph;
try
{
pastedGraph = JsonUtility.FromJson<CopyPasteGraph>(copyText);
}
catch
{
// ignored. just means copy buffer was not a graph :(
return;
}
if (pastedGraph == null)
return;
if (parent.dataSource == null)
return;
var dataSource = parent.dataSource as GraphDataSource;
if (dataSource == null)
return;
var asset = dataSource.graphAsset;
if (asset == null)
return;
var graph = asset.graph;
if (asset.graph == null)
return;
var addedNodes = new List<INode>();
var nodeGuidMap = new Dictionary<Guid, Guid>();
foreach (var node in pastedGraph.GetNodes<INode>())
{
var oldGuid = node.guid;
var newGuid = node.RewriteGuid();
nodeGuidMap[oldGuid] = newGuid;
var drawState = node.drawState;
var position = drawState.position;
position.x += 30;
position.y += 30;
drawState.position = position;
node.drawState = drawState;
graph.AddNode(node);
addedNodes.Add(node);
}
// only connect edges within pasted elements, discard
// external edges.
var addedEdges = new List<IEdge>();
foreach (var edge in pastedGraph.edges)
{
var outputSlot = edge.outputSlot;
var inputSlot = edge.inputSlot;
Guid remappedOutputNodeGuid;
Guid remappedInputNodeGuid;
if (nodeGuidMap.TryGetValue(outputSlot.nodeGuid, out remappedOutputNodeGuid)
&& nodeGuidMap.TryGetValue(inputSlot.nodeGuid, out remappedInputNodeGuid))
{
var outputSlotRef = new SlotReference(remappedOutputNodeGuid, outputSlot.slotName);
var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotName);
addedEdges.Add(graph.Connect(outputSlotRef, inputSlotRef));
}
}
graph.ValidateGraph();
parent.ReloadData();
parent.Invalidate();
parent.selection.Clear();
foreach (var element in parent.elements)
{
var drawableNode = element as DrawableNode;
if (drawableNode != null && addedNodes.Any(x => x == drawableNode.m_Node))
{
drawableNode.selected = true;
parent.selection.Add(drawableNode);
continue;
}
var drawableEdge = element as DrawableEdge<NodeAnchor>;
if (drawableEdge != null && addedEdges.Any(x => x == drawableEdge.m_Edge))
{
drawableEdge.selected = true;
parent.selection.Add(drawableEdge);
}
}
parent.Repaint();
}
}
}

12
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/CopySelected.cs.meta


fileFormatVersion: 2
guid: 79cf463d6baadf347bd87892ef921015
timeCreated: 1468575128
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

16
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DrawableEdge.cs


using UnityEditor.Experimental.Graph;
using UnityEditor.Experimental;
using UnityEngine.Graphing;
namespace UnityEditor.Graphing.Drawing
{
internal class DrawableEdge<T> : Edge<T> where T : CanvasElement, IConnect
{
public readonly IEdge m_Edge;
public DrawableEdge(IEdge edge, ICanvasDataSource data, T left, T right) : base(data, left, right)
{
m_Edge = edge;
}
}
}

12
MaterialGraphProject/Assets/GraphFramework/SerializableGraph/Editor/Drawing/DrawableEdge.cs.meta


fileFormatVersion: 2
guid: 3af037115cde5d64383adcc76ce18b5b
timeCreated: 1468575128
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存