您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
375 行
13 KiB
375 行
13 KiB
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<IGraphAsset>
|
|
{
|
|
[MenuItem("Window/Graph Editor")]
|
|
public static void OpenMenu()
|
|
{
|
|
GetWindow<GraphEditWindow>();
|
|
}
|
|
}
|
|
|
|
|
|
public abstract class AbstractGraphEditWindow<T> : 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<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.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));
|
|
}
|
|
}
|
|
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<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)
|
|
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<PropertyGenerator.TextureInfo> 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<string>();
|
|
var textures = new List<Texture>();
|
|
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;
|
|
}
|
|
}
|
|
}
|