|
|
|
|
|
|
void Repaint(); |
|
|
|
|
|
|
|
void ToggleRequiresTime(); |
|
|
|
void ToSubGraph(); |
|
|
|
} |
|
|
|
|
|
|
|
public class MaterialGraphEditWindow : AbstractMaterialGraphEditWindow<UnityEngine.MaterialGraph.MaterialGraph> |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void ToSubGraph() |
|
|
|
{ |
|
|
|
string path = EditorUtility.SaveFilePanelInProject("Save subgraph", "New SubGraph", "ShaderSubGraph", ""); |
|
|
|
path = path.Replace(Application.dataPath, "Assets"); |
|
|
|
if (path.Length == 0) |
|
|
|
return; |
|
|
|
|
|
|
|
var graphDataSource = m_GraphEditorView.presenter; |
|
|
|
var selected = graphDataSource.elements.Where(e => e.selected).ToArray(); |
|
|
|
var deserialized = MaterialGraphPresenter.DeserializeCopyBuffer(JsonUtility.ToJson(MaterialGraphPresenter.CreateCopyPasteGraph(selected))); |
|
|
|
|
|
|
|
if (deserialized == null) |
|
|
|
return; |
|
|
|
|
|
|
|
var graph = new SubGraph(); |
|
|
|
graph.AddNode(new SubGraphInputNode()); |
|
|
|
graph.AddNode(new SubGraphOutputNode()); |
|
|
|
|
|
|
|
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.slotId); |
|
|
|
var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotId); |
|
|
|
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 slotId = inputNode.AddSlot(); |
|
|
|
|
|
|
|
var outputSlotRef = new SlotReference(inputNode.guid, slotId); |
|
|
|
|
|
|
|
foreach (var edge in group) |
|
|
|
{ |
|
|
|
var newEdge = graph.Connect(outputSlotRef, new SlotReference(nodeGuidMap[edge.inputSlot.nodeGuid], edge.inputSlot.slotId)); |
|
|
|
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 slotId = outputNode.AddSlot(); |
|
|
|
|
|
|
|
var inputSlotRef = new SlotReference(outputNode.guid, slotId); |
|
|
|
|
|
|
|
foreach (var edge in group) |
|
|
|
{ |
|
|
|
var newEdge = graph.Connect(new SlotReference(nodeGuidMap[edge.outputSlot.nodeGuid], edge.outputSlot.slotId), inputSlotRef); |
|
|
|
outputsNeedingConnection.Add(new KeyValuePair<IEdge, IEdge>(edge, newEdge)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
File.WriteAllText(path, EditorJsonUtility.ToJson(graph)); |
|
|
|
AssetDatabase.ImportAsset(path); |
|
|
|
|
|
|
|
var subGraph = AssetDatabase.LoadAssetAtPath(path, typeof(MaterialSubGraphAsset)) as MaterialSubGraphAsset; |
|
|
|
if (subGraph == null) |
|
|
|
return; |
|
|
|
|
|
|
|
var subGraphNode = new SubGraphNode(); |
|
|
|
graphDataSource.AddNode(subGraphNode); |
|
|
|
subGraphNode.subGraphAsset = subGraph; |
|
|
|
|
|
|
|
foreach (var edgeMap in inputsNeedingConnection) |
|
|
|
{ |
|
|
|
graphDataSource.graph.Connect(edgeMap.Key.outputSlot, new SlotReference(subGraphNode.guid, edgeMap.Value.outputSlot.slotId)); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (var edgeMap in outputsNeedingConnection) |
|
|
|
{ |
|
|
|
graphDataSource.graph.Connect(new SlotReference(subGraphNode.guid, edgeMap.Value.inputSlot.slotId), edgeMap.Key.inputSlot); |
|
|
|
} |
|
|
|
|
|
|
|
var toDelete = graphDataSource.elements.Where(e => e.selected).OfType<MaterialNodePresenter>(); |
|
|
|
graphDataSource.RemoveElements(toDelete, new List<GraphEdgePresenter>()); |
|
|
|
} |
|
|
|
|
|
|
|
private void UpdateShaderSubGraphOnDisk(string path) |
|
|
|
{ |
|
|
|
var graph = inMemoryAsset as SubGraph; |
|
|
|
|
|
|
File.WriteAllText(path, EditorJsonUtility.ToJson(inMemoryAsset)); |
|
|
|
AssetDatabase.ImportAsset(path); |
|
|
|
} |
|
|
|
|
|
|
|
private void UpdateShaderGraphOnDisk(string path) |
|
|
|