|
|
|
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.IO; |
|
|
|
using System.Linq; |
|
|
|
using UnityEditor.MaterialGraph; |
|
|
|
using UnityEngine; |
|
|
|
using UnityEngine.Graphing; |
|
|
|
using UnityEngine.MaterialGraph; |
|
|
|
|
|
|
// The following manipulator show how to work with canvas2d overlay and background rendering
|
|
|
|
m_Canvas.AddManipulator(new RectangleSelect()); |
|
|
|
m_Canvas.AddManipulator(new ScreenSpaceGrid()); |
|
|
|
m_Canvas.AddManipulator(new ContextualMenu(DoAddNodeMenu)); |
|
|
|
m_Canvas.AddManipulator(new ContextualMenu(DoContextMenu)); |
|
|
|
|
|
|
|
m_Canvas.AddManipulator(new DeleteSelected(m_DataSource.DeleteElements)); |
|
|
|
m_Canvas.AddManipulator(new CopySelected()); |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public virtual bool CanAddToNodeMenu(Type type) { return true; } |
|
|
|
protected bool DoAddNodeMenu(Event @event, Canvas2D parent, Object customData) |
|
|
|
protected bool DoContextMenu(Event @event, Canvas2D parent, Object customData) |
|
|
|
{ |
|
|
|
var gm = new GenericMenu(); |
|
|
|
foreach (Type type in Assembly.GetAssembly(typeof(AbstractMaterialNode)).GetTypes()) |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//gm.AddSeparator("");
|
|
|
|
// gm.AddItem(new GUIContent("Convert To/SubGraph"), true, ConvertSelectionToSubGraph);
|
|
|
|
private void ConvertSelectionToSubGraph() |
|
|
|
{ |
|
|
|
if (m_Canvas.dataSource == null) |
|
|
|
return; |
|
|
|
|
|
|
|
var dataSource = m_Canvas.dataSource as GraphDataSource; |
|
|
|
if (dataSource == null) |
|
|
|
return; |
|
|
|
|
|
|
|
var asset = dataSource.graphAsset; |
|
|
|
if (asset == null) |
|
|
|
return; |
|
|
|
|
|
|
|
var targetGraph = asset.graph; |
|
|
|
if (targetGraph == null) |
|
|
|
return; |
|
|
|
|
|
|
|
if (!m_Canvas.selection.Any()) |
|
|
|
return; |
|
|
|
|
|
|
|
var serialzied = CopySelected.SerializeSelectedElements(m_Canvas); |
|
|
|
var deserialized = CopySelected.DeserializeSelectedElements(serialzied); |
|
|
|
if (deserialized == null) |
|
|
|
return; |
|
|
|
|
|
|
|
string path = EditorUtility.SaveFilePanelInProject("Save subgraph", "New SubGraph", "ShaderSubGraph", ""); |
|
|
|
path = path.Replace(Application.dataPath, "Assets"); |
|
|
|
if (path.Length == 0) |
|
|
|
return; |
|
|
|
|
|
|
|
var graphAsset = CreateInstance<MaterialSubGraphAsset>(); |
|
|
|
graphAsset.name = Path.GetFileName(path); |
|
|
|
graphAsset.PostCreate(); |
|
|
|
|
|
|
|
var graph = graphAsset.subGraph; |
|
|
|
if (graphAsset.graph == null) |
|
|
|
return; |
|
|
|
|
|
|
|
var nodeGuidMap = new Dictionary<Guid, Guid>(); |
|
|
|
foreach (var node in deserialized.GetNodes<INode>()) |
|
|
|
{ |
|
|
|
var oldGuid = node.guid; |
|
|
|
var newGuid = node.RewriteGuid(); |
|
|
|
nodeGuidMap[oldGuid] = newGuid; |
|
|
|
graph.AddNode(node); |
|
|
|
} |
|
|
|
|
|
|
|
// remap outputs to the subgraph
|
|
|
|
var inputEdgeNeedsRemap = new List<IEdge>(); |
|
|
|
var outputEdgeNeedsRemap = new List<IEdge>(); |
|
|
|
foreach (var edge in deserialized.edges) |
|
|
|
{ |
|
|
|
var outputSlot = edge.outputSlot; |
|
|
|
var inputSlot = edge.inputSlot; |
|
|
|
|
|
|
|
Guid remappedOutputNodeGuid; |
|
|
|
Guid remappedInputNodeGuid; |
|
|
|
var outputRemapExists = nodeGuidMap.TryGetValue(outputSlot.nodeGuid, out remappedOutputNodeGuid); |
|
|
|
var inputRemapExists = nodeGuidMap.TryGetValue(inputSlot.nodeGuid, out remappedInputNodeGuid); |
|
|
|
|
|
|
|
// pasting nice internal links!
|
|
|
|
if (outputRemapExists && inputRemapExists) |
|
|
|
{ |
|
|
|
var outputSlotRef = new SlotReference(remappedOutputNodeGuid, outputSlot.slotName); |
|
|
|
var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotName); |
|
|
|
graph.Connect(outputSlotRef, inputSlotRef); |
|
|
|
} |
|
|
|
// one edge needs to go to outside world
|
|
|
|
else if (outputRemapExists) |
|
|
|
{ |
|
|
|
inputEdgeNeedsRemap.Add(edge); |
|
|
|
} |
|
|
|
else if (inputRemapExists) |
|
|
|
{ |
|
|
|
outputEdgeNeedsRemap.Add(edge); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// we do a grouping here as the same output can
|
|
|
|
// point to multiple inputs
|
|
|
|
var uniqueOutputs = outputEdgeNeedsRemap.GroupBy(edge => edge.outputSlot); |
|
|
|
var inputsNeedingConnection = new List<KeyValuePair<IEdge, IEdge>>(); |
|
|
|
foreach (var group in uniqueOutputs) |
|
|
|
{ |
|
|
|
var inputNode = graph.inputNode; |
|
|
|
var index = inputNode.GetOutputSlots<ISlot>().Count(); |
|
|
|
inputNode.AddSlot(); |
|
|
|
|
|
|
|
var outputSlotRef = new SlotReference(inputNode.guid, "Input" + index); |
|
|
|
|
|
|
|
foreach (var edge in group) |
|
|
|
{ |
|
|
|
var newEdge = graph.Connect(outputSlotRef, new SlotReference(nodeGuidMap[edge.inputSlot.nodeGuid], edge.inputSlot.slotName)); |
|
|
|
inputsNeedingConnection.Add(new KeyValuePair<IEdge, IEdge>(edge, newEdge)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var uniqueInputs = inputEdgeNeedsRemap.GroupBy(edge => edge.inputSlot); |
|
|
|
var outputsNeedingConnection = new List<KeyValuePair<IEdge, IEdge>>(); |
|
|
|
foreach (var group in uniqueInputs) |
|
|
|
{ |
|
|
|
var outputNode = graph.outputNode; |
|
|
|
var index = outputNode.GetInputSlots<ISlot>().Count(); |
|
|
|
outputNode.AddSlot(); |
|
|
|
|
|
|
|
var inputSlotRef = new SlotReference(outputNode.guid, "Output" + index); |
|
|
|
|
|
|
|
foreach (var edge in group) |
|
|
|
{ |
|
|
|
var newEdge = graph.Connect(new SlotReference(nodeGuidMap[edge.outputSlot.nodeGuid], edge.outputSlot.slotName), inputSlotRef); |
|
|
|
outputsNeedingConnection.Add(new KeyValuePair<IEdge, IEdge>(edge, newEdge)); |
|
|
|
} |
|
|
|
} |
|
|
|
AssetDatabase.CreateAsset(graphAsset, path); |
|
|
|
|
|
|
|
var subGraphNode = new SubGraphNode(); |
|
|
|
targetGraph.AddNode(subGraphNode); |
|
|
|
subGraphNode.subGraphAsset = graphAsset; |
|
|
|
|
|
|
|
foreach (var edgeMap in inputsNeedingConnection) |
|
|
|
{ |
|
|
|
targetGraph.Connect(edgeMap.Key.outputSlot, new SlotReference(subGraphNode.guid, edgeMap.Value.outputSlot.slotName)); |
|
|
|
} |
|
|
|
foreach (var edgeMap in outputsNeedingConnection) |
|
|
|
{ |
|
|
|
targetGraph.Connect(new SlotReference(subGraphNode.guid, edgeMap.Value.inputSlot.slotName), edgeMap.Key.inputSlot); |
|
|
|
} |
|
|
|
|
|
|
|
var toDelete = m_Canvas.selection.Where(x => x is DrawableNode).ToList(); |
|
|
|
dataSource.DeleteElements(toDelete); |
|
|
|
|
|
|
|
targetGraph.ValidateGraph(); |
|
|
|
m_Canvas.ReloadData(); |
|
|
|
m_Canvas.Invalidate(); |
|
|
|
m_Canvas.selection.Clear(); |
|
|
|
|
|
|
|
var toSelect = m_Canvas.elements.OfType<DrawableNode>().FirstOrDefault(x => x.m_Node == subGraphNode); |
|
|
|
if (toSelect != null) |
|
|
|
{ |
|
|
|
toSelect.selected = true; |
|
|
|
m_Canvas.selection.Add(toSelect); |
|
|
|
} |
|
|
|
m_Canvas.Repaint(); |
|
|
|
} |
|
|
|
|
|
|
|
private void Rebuild() |
|
|
|
{ |
|
|
|
if (m_Canvas == null || m_LastSelection == null) |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
m_Canvas.OnGUI(this, new Rect(0, 0, position.width - 250, position.height)); |
|
|
|
|
|
|
|
if (GUI.Button(new Rect(position.width - 250, 0, 250, 50), "Convert to Sub-Graph")) |
|
|
|
ConvertSelectionToSubGraph(); |
|
|
|
} |
|
|
|
|
|
|
|
/*public void RenderOptions(MaterialGraph graph) |
|
|
|