浏览代码

Node selector / search

/main
Peter Bay Bastian 7 年前
当前提交
e91932f8
共有 6 个文件被更改,包括 254 次插入105 次删除
  1. 4
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Data/Nodes/TitleAttribute.cs
  2. 97
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Manipulators/NodeCreator.cs
  3. 4
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/MaterialGraphEditWindow.cs
  4. 20
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/GraphEditorView.cs
  5. 231
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/SearchWindowProvider.cs
  6. 3
      MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/SearchWindowProvider.cs.meta

4
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Data/Nodes/TitleAttribute.cs


[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
public class TitleAttribute : Attribute
{
public string m_Title;
public TitleAttribute(string title) { m_Title = title; }
public string[] title;
public TitleAttribute(params string[] title) { this.title = title; }
}
}

97
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Manipulators/NodeCreator.cs


using UnityEditor.Experimental.UIElements.GraphView;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph;
namespace UnityEditor.ShaderGraph.Drawing

VisualElement m_ContentViewContainer;
IGraph m_Graph;
Vector2 m_MouseUpPosition;
GenericMenu m_Menu;
SearchWindowProvider m_SearchWindowProvider;
public NodeCreator(IGraph graph)
public NodeCreator(SearchWindowProvider searchWindowProvider)
m_Graph = graph;
m_SearchWindowProvider = searchWindowProvider;
m_Menu = new GenericMenu();
foreach (var type in Assembly.GetAssembly(typeof(AbstractMaterialNode)).GetTypes())
{
if (type.IsClass && !type.IsAbstract && (type.IsSubclassOf(typeof(AbstractMaterialNode))))
{
var attrs = type.GetCustomAttributes(typeof(TitleAttribute), false) as TitleAttribute[];
if (attrs != null && attrs.Length > 0)
m_Menu.AddItem(new GUIContent(attrs[0].m_Title), false, OnAddNode, type);
}
}
m_Menu.AddSeparator("");
m_Menu.AddItem(new GUIContent("Convert to Property"), false, OnConvertToProperty);
m_Menu.AddItem(new GUIContent("Convert to Inline node"), false, OnConvertToInlineNode);
private void OnConvertToInlineNode()
{
if (m_GraphView == null)
return;
var slected = m_GraphView.selection.OfType<MaterialNodeView>()
.Select(x => x.node)
.OfType<PropertyNode>();
foreach (var propNode in slected)
((AbstractMaterialGraph)propNode.owner).ReplacePropertyNodeWithConcreteNode(propNode);
}
private void OnConvertToProperty()
{
if (m_GraphView == null)
return;
var graph = m_Graph as AbstractMaterialGraph;
if (graph == null)
return;
var slected = m_GraphView.selection.OfType<MaterialNodeView>().Select(x => x.node);
foreach (var node in slected.ToArray())
{
if (!(node is IPropertyFromNode))
continue;
var converter = node as IPropertyFromNode;
var prop = converter.AsShaderProperty();
graph.AddShaderProperty(prop);
var propNode = new PropertyNode();
propNode.drawState = node.drawState;
graph.AddNode(propNode);
propNode.propertyGuid = prop.guid;
var oldSlot = node.FindSlot<MaterialSlot>(converter.outputSlotId);
var newSlot = propNode.FindSlot<MaterialSlot>(PropertyNode.OutputSlotId);
var edges = graph.GetEdges(oldSlot.slotReference).ToArray();
foreach (var edge in edges)
graph.Connect(newSlot.slotReference, edge.inputSlot);
graph.RemoveNode(node);
}
}
void OnAddNode(object userData)
{
var type = userData as Type;
var node = Activator.CreateInstance(type) as INode;
if (node == null)
return;
var drawState = node.drawState;
drawState.position = new Rect(m_MouseUpPosition.x, m_MouseUpPosition.y, 0, 0);
node.drawState = drawState;
m_Graph.owner.RegisterCompleteObjectUndo("Add " + node.name);
m_Graph.AddNode(node);
}
private GraphView m_GraphView;
m_GraphView = target as GraphView;
if (m_GraphView == null)
return;
m_ContentViewContainer = m_GraphView.contentViewContainer;
target.RegisterCallback<MouseUpEvent>(OnMouseUp);
}

{
m_MouseUpPosition = m_ContentViewContainer.transform.matrix.inverse.MultiplyPoint3x4(evt.localMousePosition);
m_Menu.ShowAsContext();
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(evt.mousePosition)), m_SearchWindowProvider);
evt.StopPropagation();
}
}

4
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/MaterialGraphEditWindow.cs


if (graphEditorView == null)
{
var asset = AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GUIDToAssetPath(selectedGuid));
graphEditorView = new GraphEditorView(materialGraph, asset.name) { persistenceKey = AssetDatabase.AssetPathToGUID(AssetDatabase.GUIDToAssetPath(selectedGuid)) };
graphEditorView = new GraphEditorView(this, materialGraph, asset.name) { persistenceKey = AssetDatabase.AssetPathToGUID(AssetDatabase.GUIDToAssetPath(selectedGuid)) };
}
graphEditorView.HandleGraphChanges();

graphObject.graph.OnEnable();
graphObject.graph.ValidateGraph();
graphEditorView = new GraphEditorView(m_GraphObject.graph as AbstractMaterialGraph, asset.name) { persistenceKey = AssetDatabase.GUIDToAssetPath(selectedGuid) };
graphEditorView = new GraphEditorView(this, m_GraphObject.graph as AbstractMaterialGraph, asset.name) { persistenceKey = AssetDatabase.GUIDToAssetPath(selectedGuid) };
graphEditorView.RegisterCallback<PostLayoutEvent>(OnPostLayout);
titleContent = new GUIContent(asset.name);

20
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/Views/GraphEditorView.cs


{
public class GraphEditorView : VisualElement, IDisposable
{
AbstractMaterialGraph m_Graph;
MaterialGraphView m_GraphView;
GraphInspectorView m_GraphInspectorView;
ToolbarView m_ToolbarView;

AbstractMaterialGraph m_Graph;
SearchWindowProvider m_SearchWindowProvider;
public Action onUpdateAssetClick { get; set; }
public Action onConvertToSubgraphClick { get; set; }

get { return m_GraphInspectorView; }
}
public GraphEditorView(AbstractMaterialGraph graph, string assetName)
public GraphEditorView(EditorWindow editorWindow, AbstractMaterialGraph graph, string assetName)
m_SearchWindowProvider = ScriptableObject.CreateInstance<SearchWindowProvider>();
AddStyleSheetPath("Styles/MaterialGraph");
previewManager = new PreviewManager(graph);

m_GraphView.AddManipulator(new RectangleSelector());
m_GraphView.AddManipulator(new SelectionDragger());
m_GraphView.AddManipulator(new ClickSelector());
m_GraphView.AddManipulator(new NodeCreator(graph));
m_GraphView.AddManipulator(new GraphDropTarget(graph));
content.Add(m_GraphView);

m_GraphView.graphViewChanged = GraphViewChanged;
}
m_SearchWindowProvider.Initialize(editorWindow, m_Graph, m_GraphView);
m_GraphView.AddManipulator(new NodeCreator(m_SearchWindowProvider));
foreach (var node in graph.GetNodes<INode>())
AddNode(node);

output = sourceAnchor,
input = targetAnchor
};
// edgeView.UpdateClasses(sourceSlot.concreteValueType, targetSlot.concreteValueType);
edgeView.output.Connect(edgeView);
edgeView.input.Connect(edgeView);
m_GraphView.AddElement(edgeView);

nodeView.UpdatePortInputTypes();
foreach (var anchorView in nodeView.outputContainer.Children().OfType<Port>())
{
var sourceSlot = (MaterialSlot)anchorView.userData;
// edgeView.UpdateClasses(sourceSlot.concreteValueType, targetSlot.concreteValueType);
var connectedNodeView = edgeView.input.node as MaterialNodeView;
if (connectedNodeView != null && !nodeViews.Contains(connectedNodeView))
{

continue;
foreach (var edgeView in anchorView.connections.OfType<Edge>())
{
var sourceSlot = (MaterialSlot)edgeView.output.userData;
// edgeView.UpdateClasses(sourceSlot.concreteValueType, targetSlot.concreteValueType);
var connectedNodeView = edgeView.output.node as MaterialNodeView;
if (connectedNodeView != null && !nodeViews.Contains(connectedNodeView))
{

{
previewManager.Dispose();
previewManager = null;
}
if (m_SearchWindowProvider != null)
{
Object.DestroyImmediate(m_SearchWindowProvider);
m_SearchWindowProvider = null;
}
}
}

231
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/SearchWindowProvider.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using ICSharpCode.NRefactory.Ast;
using UnityEditor.Experimental.UIElements;
using UnityEditor.Experimental.UIElements.GraphView;
using UnityEditor.Graphing;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
using INode = UnityEditor.Graphing.INode;
namespace UnityEditor.ShaderGraph.Drawing
{
public class SearchWindowProvider : ScriptableObject, ISearchWindowProvider
{
EditorWindow m_EditorWindow;
IGraph m_Graph;
GraphView m_GraphView;
Texture2D m_Icon;
static readonly string k_Actions = "Actions";
static readonly string k_AddNode = "Add Node";
static readonly string k_ConvertToProperty = "Convert to Property";
static readonly string k_ConvertToInlineNode = "Convert to Inline Node";
public void Initialize(EditorWindow editorWindow, IGraph graph, GraphView graphView)
{
m_EditorWindow = editorWindow;
m_Graph = graph;
m_GraphView = graphView;
// Transparent icon to trick search window into indenting items
m_Icon = new Texture2D(1, 1);
m_Icon.SetPixel(0, 0, new Color(0, 0, 0, 0));
m_Icon.Apply();
}
void OnDestroy()
{
if (m_Icon != null)
{
DestroyImmediate(m_Icon);
m_Icon = null;
}
}
struct NestedEntry
{
public string[] title;
public Type type;
}
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
// First build up temporary data structure containing group & title as an array of strings (the last one is the actual title) and associated node type.
var nestedEntries = new List<NestedEntry>();
foreach (var type in Assembly.GetAssembly(typeof(AbstractMaterialNode)).GetTypes())
{
if (type.IsClass && !type.IsAbstract && (type.IsSubclassOf(typeof(AbstractMaterialNode))))
{
var attrs = type.GetCustomAttributes(typeof(TitleAttribute), false) as TitleAttribute[];
if (attrs != null && attrs.Length > 0)
nestedEntries.Add(new NestedEntry { title = attrs[0].title, type = type });
}
}
// Sort the entries lexicographically by group then title with the requirement that items always comes before sub-groups in the same group.
// Example result:
// - Art/BlendMode
// - Art/Adjustments/ColorBalance
// - Art/Adjustments/Contrast
nestedEntries.Sort((entry1, entry2) =>
{
for (var i = 0; i < entry1.title.Length; i++)
{
if (i >= entry2.title.Length)
return 1;
var value = entry1.title[i].CompareTo(entry2.title[i]);
if (value != 0)
{
// Make sure that leaves go before nodes
if (entry1.title.Length != entry2.title.Length && (i == entry1.title.Length - 1 || i == entry2.title.Length - 1))
return entry1.title.Length < entry2.title.Length ? -1 : 1;
return value;
}
}
return 0;
});
//* Build up the data structure needed by SearchWindow.
// `groups` contains the current group path we're in.
var groups = new List<string>();
// First item in the tree is the title of the window.
var tree = new List<SearchTreeEntry>
{
new SearchTreeGroupEntry(new GUIContent(k_Actions), 0),
new SearchTreeGroupEntry(new GUIContent(k_AddNode)) { level = 1 },
};
// Add in contextual node actions
if (m_GraphView.selection.OfType<MaterialNodeView>().Any())
{
tree.Add(new SearchTreeEntry(new GUIContent(k_ConvertToProperty, m_Icon)) { level = 1 });
tree.Add(new SearchTreeEntry(new GUIContent(k_ConvertToInlineNode, m_Icon)) { level = 1 });
}
foreach (var nestedEntry in nestedEntries)
{
// `createIndex` represents from where we should add new group entries from the current entry's group path.
var createIndex = int.MaxValue;
// Compare the group path of the current entry to the current group path.
for (var i = 0; i < nestedEntry.title.Length - 1; i++)
{
var group = nestedEntry.title[i];
if (i >= groups.Count)
{
// The current group path matches a prefix of the current entry's group path, so we add the
// rest of the group path from the currrent entry.
createIndex = i;
break;
}
if (groups[i] != group)
{
// A prefix of the current group path matches a prefix of the current entry's group path,
// so we remove everyfrom from the point where it doesn't match anymore, and then add the rest
// of the group path from the current entry.
groups.RemoveRange(i, groups.Count - i);
createIndex = i;
break;
}
}
// Create new group entries as needed.
// If we don't need to modify the group path, `createIndex` will be `int.MaxValue` and thus the loop won't run.
for (var i = createIndex; i < nestedEntry.title.Length - 1; i++)
{
var group = nestedEntry.title[i];
groups.Add(group);
tree.Add(new SearchTreeGroupEntry(new GUIContent(group)) { level = i + 2 });
}
// Finally, add the actual entry.
tree.Add(new SearchTreeEntry(new GUIContent(nestedEntry.title.Last(), m_Icon)) { level = nestedEntry.title.Length + 1, userData = nestedEntry.type });
}
return tree;
}
public bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
if (entry.name == k_ConvertToProperty)
return OnConvertToProperty();
if (entry.name == k_ConvertToInlineNode)
return OnConvertToInlineNode();
var type = (Type)entry.userData;
var node = Activator.CreateInstance(type) as INode;
if (node == null)
return false;
var drawState = node.drawState;
var windowMousePosition = context.screenMousePosition - m_EditorWindow.position.position;
var graphMousePosition = m_EditorWindow.GetRootVisualContainer().ChangeCoordinatesTo(m_GraphView.contentViewContainer, windowMousePosition);
drawState.position = new Rect(graphMousePosition, Vector2.zero);
node.drawState = drawState;
m_Graph.owner.RegisterCompleteObjectUndo("Add " + node.name);
m_Graph.AddNode(node);
return true;
}
bool OnConvertToProperty()
{
if (m_GraphView == null)
return false;
var graph = m_Graph as AbstractMaterialGraph;
if (graph == null)
return false;
var selectedNodeViews = m_GraphView.selection.OfType<MaterialNodeView>().Select(x => x.node);
foreach (var node in selectedNodeViews.ToArray())
{
if (!(node is IPropertyFromNode))
continue;
var converter = node as IPropertyFromNode;
var prop = converter.AsShaderProperty();
graph.AddShaderProperty(prop);
var propNode = new PropertyNode();
propNode.drawState = node.drawState;
graph.AddNode(propNode);
propNode.propertyGuid = prop.guid;
var oldSlot = node.FindSlot<MaterialSlot>(converter.outputSlotId);
var newSlot = propNode.FindSlot<MaterialSlot>(PropertyNode.OutputSlotId);
var edges = graph.GetEdges(oldSlot.slotReference).ToArray();
foreach (var edge in edges)
graph.Connect(newSlot.slotReference, edge.inputSlot);
graph.RemoveNode(node);
}
return true;
}
bool OnConvertToInlineNode()
{
if (m_GraphView == null)
return false;
var selectedNodeViews = m_GraphView.selection.OfType<MaterialNodeView>()
.Select(x => x.node)
.OfType<PropertyNode>();
foreach (var propNode in selectedNodeViews)
((AbstractMaterialGraph)propNode.owner).ReplacePropertyNodeWithConcreteNode(propNode);
return true;
}
}
}

3
MaterialGraphProject/Assets/UnityShaderEditor/Editor/Drawing/SearchWindowProvider.cs.meta


fileFormatVersion: 2
guid: 2cc3fd443c2a4dcc95b30a8ef3eaa6d3
timeCreated: 1512377373
正在加载...
取消
保存