using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; using UnityEngine.Graphing; using UnityEngine.MaterialGraph; using UnityEngine.RMGUI; namespace UnityEditor.Graphing.Drawing { public class GraphEditWindow : AbstractGraphEditWindow { [MenuItem("Window/Graph Editor")] public static void OpenMenu() { GetWindow(); } } public abstract class AbstractGraphEditWindow : EditorWindow, ISerializationCallbackReceiver where T : class, IGraphAsset { public RenderTexture rt; [NonSerialized] private T m_LastSelection; [SerializeField] private ScriptableObject m_LastSelectedGraphSerialized; private bool shouldRepaint { get { return m_LastSelection != null && m_LastSelection.shouldRepaint; } } private MaterialGraphView m_Contents; void OnEnable() { m_Contents = new MaterialGraphView(); m_Contents.name = "theView"; m_Contents.dataSource = new MaterialGraphDataSource(m_LastSelection); m_Contents.StretchToParentSize(); windowRoot.AddChild(m_Contents); } void OnDisable() { windowRoot.ClearChildren(); } void Update() { if (shouldRepaint) Repaint(); } void OnSelectionChange() { if (Selection.activeObject == null || !EditorUtility.IsPersistent(Selection.activeObject)) return; if (Selection.activeObject is ScriptableObject) { var selection = Selection.activeObject as T; if (selection != m_LastSelection) { var graph = selection.graph; graph.OnEnable(); graph.ValidateGraph(); m_LastSelection = selection; m_Contents.dataSource = new MaterialGraphDataSource(m_LastSelection); m_Contents.StretchToParentSize(); Repaint(); } } } /* 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(); graphAsset.name = Path.GetFileName(path); graphAsset.PostCreate(); var graph = graphAsset.subGraph; if (graphAsset.graph == null) return; var nodeGuidMap = new Dictionary(); foreach (var node in deserialized.GetNodes()) { var oldGuid = node.guid; var newGuid = node.RewriteGuid(); nodeGuidMap[oldGuid] = newGuid; graph.AddNode(node); } // remap outputs to the subgraph var inputEdgeNeedsRemap = new List(); var outputEdgeNeedsRemap = new List(); 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>(); 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(edge, newEdge)); } } var uniqueInputs = inputEdgeNeedsRemap.GroupBy(edge => edge.inputSlot); var outputsNeedingConnection = new List>(); 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(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.slotId)); } foreach (var edgeMap in outputsNeedingConnection) { targetGraph.Connect(new SlotReference(subGraphNode.guid, edgeMap.Value.inputSlot.slotId), 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().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) return; m_DataSource.graphAsset = m_LastSelection; m_Canvas.ReloadData(); }*/ /* void OnGUI() { m_HostWindow = this; if (m_Canvas == null) { InitializeCanvas(); } if (m_LastSelection == null || m_LastSelection.graph == null) { GUILayout.Label("No Graph selected"); return; } 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(); if (GUI.Button(new Rect(position.width - 250, 70, 250, 50), "Export")) Export(false); if (GUI.Button(new Rect(position.width - 250, 140, 250, 50), "Export - quick")) Export(true); EditorGUI.ObjectField(new Rect(position.width - 250, 210, 250, 50), rt, typeof(RenderTexture), false); }*/ private string m_LastPath; public void Export(bool quickExport) { var path = quickExport ? m_LastPath : EditorUtility.SaveFilePanelInProject("Export shader to file...", "shader.shader", "shader", "Enter file name"); m_LastPath = path; // For quick exporting var ds = m_Contents.dataSource as MaterialGraphDataSource; if (ds != null && !string.IsNullOrEmpty(path)) { ExportShader(ds.graphAsset as MaterialGraphAsset, path); } else EditorUtility.DisplayDialog("Export Shader Error", "Cannot export shader", "Ok"); } public static Shader ExportShader(MaterialGraphAsset graphAsset, string path) { if (graphAsset == null) return null; var materialGraph = graphAsset.graph as PixelGraph; if (materialGraph == null) return null; List configuredTextures; var shaderString = ShaderGenerator.GenerateSurfaceShader(materialGraph.pixelMasterNode, new MaterialOptions(), materialGraph.name, false, out configuredTextures); File.WriteAllText(path, shaderString); AssetDatabase.Refresh(); // Investigate if this is optimal var shader = AssetDatabase.LoadAssetAtPath(path, typeof(Shader)) as Shader; if (shader == null) return null; var shaderImporter = AssetImporter.GetAtPath(path) as ShaderImporter; if (shaderImporter == null) return null; var textureNames = new List(); var textures = new List(); foreach (var textureInfo in configuredTextures.Where(x => x.modifiable == TexturePropertyChunk.ModifiableState.Modifiable)) { var texture = EditorUtility.InstanceIDToObject(textureInfo.textureId) as Texture; if (texture == null) continue; textureNames.Add(textureInfo.name); textures.Add(texture); } shaderImporter.SetDefaultTextures(textureNames.ToArray(), textures.ToArray()); textureNames.Clear(); textures.Clear(); foreach (var textureInfo in configuredTextures.Where(x => x.modifiable == TexturePropertyChunk.ModifiableState.NonModifiable)) { var texture = EditorUtility.InstanceIDToObject(textureInfo.textureId) as Texture; if (texture == null) continue; textureNames.Add(textureInfo.name); textures.Add(texture); } shaderImporter.SetNonModifiableTextures(textureNames.ToArray(), textures.ToArray()); shaderImporter.SaveAndReimport(); return shaderImporter.GetShader(); } /*public void RenderOptions(MaterialGraph graph) { EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); EditorGUILayout.BeginVertical(); m_ScrollPos = GUILayout.BeginScrollView(m_ScrollPos, EditorStyles.textArea, GUILayout.width(250), GUILayout.ExpandHeight(true)); graph.materialOptions.DoGUI(); EditorGUILayout.Separator(); m_NodeExpanded = MaterialGraphStyles.Header("Selected", m_NodeExpanded); if (m_NodeExpanded) DrawableMaterialNode.OnGUI(m_Canvas.selection); GUILayout.EndScrollView(); if (GUILayout.Button("Export")) m_DataSource.Export(false); GUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); }*/ public void OnBeforeSerialize() { var o = m_LastSelection as ScriptableObject; if (o != null) m_LastSelectedGraphSerialized = o; } public void OnAfterDeserialize() { if (m_LastSelectedGraphSerialized != null) m_LastSelection = m_LastSelectedGraphSerialized as T; m_LastSelectedGraphSerialized = null; } } }