123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using UnityEditor.Graphing;
- using UnityEditor.Graphing.Util;
- using UnityEngine;
- using UnityEditor.UIElements;
- using UnityEditor.Experimental.GraphView;
- using UnityEngine.UIElements;
- using UnityEditor.Searcher;
- using UnityEngine.Profiling;
- using UnityEngine.Pool;
- using Object = UnityEngine.Object;
-
- namespace UnityEditor.ShaderGraph.Drawing
- {
- internal struct NodeEntry
- {
- public string[] title;
- public AbstractMaterialNode node;
- public int compatibleSlotId;
- public string slotName;
- }
-
- class SearchWindowProvider : IDisposable
- {
- internal EditorWindow m_EditorWindow;
- internal GraphData m_Graph;
- internal GraphView m_GraphView;
- internal Texture2D m_Icon;
- public List<NodeEntry> currentNodeEntries;
- public ShaderPort connectedPort { get; set; }
- public bool nodeNeedsRepositioning { get; set; }
- public SlotReference targetSlotReference { get; internal set; }
- public Vector2 targetPosition { get; internal set; }
- public VisualElement target { get; internal set; }
- public bool regenerateEntries { get; set; }
- private const string k_HiddenFolderName = "Hidden";
- ShaderStageCapability m_ConnectedSlotCapability; // calculated in GenerateNodeEntries
-
- public void Initialize(EditorWindow editorWindow, GraphData graph, GraphView graphView)
- {
- m_EditorWindow = editorWindow;
- m_Graph = graph;
- m_GraphView = graphView;
- GenerateNodeEntries();
-
- // 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();
- }
-
- public void Dispose()
- {
- if (m_Icon != null)
- {
- Object.DestroyImmediate(m_Icon);
- m_Icon = null;
- }
-
- m_EditorWindow = null;
- m_Graph = null;
- m_GraphView = null;
- connectedPort = null;
-
- currentNodeEntries?.Clear();
- currentNodeEntries = null;
- }
-
- List<int> m_Ids;
- List<MaterialSlot> m_Slots = new List<MaterialSlot>();
-
- public void GenerateNodeEntries()
- {
- Profiler.BeginSample("SearchWindowProvider.GenerateNodeEntries");
- // 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.
- List<NodeEntry> nodeEntries = new List<NodeEntry>();
-
- bool hideCustomInterpolators = m_Graph.activeTargets.All(at => at.ignoreCustomInterpolators);
-
- if (connectedPort != null)
- {
- var slot = connectedPort.slot;
-
- // Precalculate slot compatibility to avoid traversing graph for every added entry.
- m_ConnectedSlotCapability = slot.stageCapability;
- if (m_ConnectedSlotCapability == ShaderStageCapability.All || slot.owner is SubGraphNode)
- {
- m_ConnectedSlotCapability = NodeUtils.GetEffectiveShaderStageCapability(slot, true)
- & NodeUtils.GetEffectiveShaderStageCapability(slot, false);
- }
- }
- else
- {
- m_ConnectedSlotCapability = ShaderStageCapability.All;
- }
-
- if (target is ContextView contextView)
- {
- // Iterate all BlockFieldDescriptors currently cached on GraphData
- foreach (var field in m_Graph.blockFieldDescriptors)
- {
- if (field.isHidden)
- continue;
-
- // Test stage
- if (field.shaderStage != contextView.contextData.shaderStage)
- continue;
-
- // Create title
- List<string> title = ListPool<string>.Get();
- if (!string.IsNullOrEmpty(field.path))
- {
- var path = field.path.Split('/').ToList();
- title.AddRange(path);
- }
- title.Add(field.displayName);
-
- // Create and initialize BlockNode instance then add entry
- var node = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
- node.Init(field);
- AddEntries(node, title.ToArray(), nodeEntries);
- }
-
- SortEntries(nodeEntries);
-
- if (contextView.contextData.shaderStage == ShaderStage.Vertex && !hideCustomInterpolators)
- {
- var customBlockNodeStub = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
- customBlockNodeStub.InitCustomDefault();
- AddEntries(customBlockNodeStub, new string[] { "Custom Interpolator" }, nodeEntries);
- }
-
- currentNodeEntries = nodeEntries;
- return;
- }
-
-
- Profiler.BeginSample("SearchWindowProvider.GenerateNodeEntries.IterateKnowNodes");
- foreach (var type in NodeClassCache.knownNodeTypes)
- {
- if ((!type.IsClass || type.IsAbstract)
- || type == typeof(PropertyNode)
- || type == typeof(KeywordNode)
- || type == typeof(DropdownNode)
- || type == typeof(SubGraphNode))
- continue;
-
- TitleAttribute titleAttribute = NodeClassCache.GetAttributeOnNodeType<TitleAttribute>(type);
- if (titleAttribute != null)
- {
- var node = (AbstractMaterialNode)Activator.CreateInstance(type);
- if (!node.ExposeToSearcher)
- continue;
-
- if (ShaderGraphPreferences.allowDeprecatedBehaviors && node.latestVersion > 0)
- {
- var versions = node.allowedNodeVersions ?? Enumerable.Range(0, node.latestVersion + 1);
- bool multiple = (versions.Count() > 1);
- foreach (int i in versions)
- {
- var depNode = (AbstractMaterialNode)Activator.CreateInstance(type);
- depNode.ChangeVersion(i);
- if (multiple)
- AddEntries(depNode, titleAttribute.title.Append($"v{i}").ToArray(), nodeEntries);
- else
- AddEntries(depNode, titleAttribute.title, nodeEntries);
- }
- }
- else
- {
- AddEntries(node, titleAttribute.title, nodeEntries);
- }
- }
- }
- Profiler.EndSample();
-
-
- Profiler.BeginSample("SearchWindowProvider.GenerateNodeEntries.IterateSubgraphAssets");
- foreach (var asset in NodeClassCache.knownSubGraphAssets)
- {
- if (asset == null)
- continue;
-
- var node = new SubGraphNode { asset = asset };
- var title = asset.path.Split('/').ToList();
-
- if (asset.descendents.Contains(m_Graph.assetGuid) || asset.assetGuid == m_Graph.assetGuid)
- {
- continue;
- }
-
- if (string.IsNullOrEmpty(asset.path))
- {
- AddEntries(node, new string[1] { asset.name }, nodeEntries);
- }
- else if (title[0] != k_HiddenFolderName)
- {
- title.Add(asset.name);
- AddEntries(node, title.ToArray(), nodeEntries);
- }
- }
- Profiler.EndSample();
-
-
- Profiler.BeginSample("SearchWindowProvider.GenerateNodeEntries.IterateGraphInputs");
- foreach (var property in m_Graph.properties)
- {
- if (property is Serialization.MultiJsonInternal.UnknownShaderPropertyType)
- continue;
-
- var node = new PropertyNode();
- node.property = property;
- AddEntries(node, new[] { "Properties", "Property: " + property.displayName }, nodeEntries);
- }
- foreach (var keyword in m_Graph.keywords)
- {
- var node = new KeywordNode();
- node.keyword = keyword;
- AddEntries(node, new[] { "Keywords", "Keyword: " + keyword.displayName }, nodeEntries);
- }
- foreach (var dropdown in m_Graph.dropdowns)
- {
- var node = new DropdownNode();
- node.dropdown = dropdown;
- AddEntries(node, new[] { "Dropdowns", "dropdown: " + dropdown.displayName }, nodeEntries);
- }
- if (!hideCustomInterpolators)
- {
- foreach (var cibnode in m_Graph.vertexContext.blocks.Where(b => b.value.isCustomBlock))
- {
- var node = Activator.CreateInstance<CustomInterpolatorNode>();
- node.ConnectToCustomBlock(cibnode.value);
- AddEntries(node, new[] { "Custom Interpolator", cibnode.value.customName }, nodeEntries);
- }
- }
- Profiler.EndSample();
-
- SortEntries(nodeEntries);
- currentNodeEntries = nodeEntries;
- Profiler.EndSample();
- }
-
- void SortEntries(List<NodeEntry> nodeEntries)
- {
- // 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
- nodeEntries.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))
- {
- //once nodes are sorted, sort slot entries by slot order instead of alphebetically
- var alphaOrder = entry1.title.Length < entry2.title.Length ? -1 : 1;
- var slotOrder = entry1.compatibleSlotId.CompareTo(entry2.compatibleSlotId);
- return alphaOrder.CompareTo(slotOrder);
- }
-
- return value;
- }
- }
- return 0;
- });
- }
-
- void AddEntries(AbstractMaterialNode node, string[] title, List<NodeEntry> addNodeEntries)
- {
- if (m_Graph.isSubGraph && !node.allowedInSubGraph)
- return;
- if (!m_Graph.isSubGraph && !node.allowedInMainGraph)
- return;
- if (connectedPort == null)
- {
- addNodeEntries.Add(new NodeEntry
- {
- node = node,
- title = title,
- compatibleSlotId = -1
- });
- return;
- }
-
- var connectedSlot = connectedPort.slot;
- m_Slots.Clear();
- node.GetSlots(m_Slots);
-
- foreach (var slot in m_Slots)
- {
- if (!slot.IsCompatibleWith(connectedSlot))
- {
- continue;
- }
-
- if (!slot.IsCompatibleStageWith(m_ConnectedSlotCapability))
- {
- continue;
- }
-
- //var entryTitle = new string[title.Length];
- //title.CopyTo(entryTitle, 0);
- //entryTitle[entryTitle.Length - 1] += ": " + slot.displayName;
- addNodeEntries.Add(new NodeEntry
- {
- title = title,
- node = node,
- compatibleSlotId = slot.id,
- slotName = slot.displayName
- });
- }
- }
- }
- class SearcherProvider : SearchWindowProvider
- {
- public Searcher.Searcher LoadSearchWindow()
- {
- if (regenerateEntries)
- {
- GenerateNodeEntries();
- regenerateEntries = false;
- }
-
- //create empty root for searcher tree
- var root = new List<SearcherItem>();
- var dummyEntry = new NodeEntry();
-
- foreach (var nodeEntry in currentNodeEntries)
- {
- SearcherItem item = null;
- SearcherItem parent = null;
- for (int i = 0; i < nodeEntry.title.Length; i++)
- {
- var pathEntry = nodeEntry.title[i];
- List<SearcherItem> children = parent != null ? parent.Children : root;
- item = children.Find(x => x.Name == pathEntry);
-
- if (item == null)
- {
- //if we have slot entries and are at a leaf, add the slot name to the entry title
- if (nodeEntry.compatibleSlotId != -1 && i == nodeEntry.title.Length - 1)
- item = new SearchNodeItem(pathEntry + ": " + nodeEntry.slotName, nodeEntry, nodeEntry.node.synonyms);
- //if we don't have slot entries and are at a leaf, add userdata to the entry
- else if (nodeEntry.compatibleSlotId == -1 && i == nodeEntry.title.Length - 1)
- item = new SearchNodeItem(pathEntry, nodeEntry, nodeEntry.node.synonyms);
- //if we aren't a leaf, don't add user data
- else
- item = new SearchNodeItem(pathEntry, dummyEntry, null);
-
- if (parent != null)
- {
- parent.AddChild(item);
- }
- else
- {
- children.Add(item);
- }
- }
-
- parent = item;
-
- if (parent.Depth == 0 && !root.Contains(parent))
- root.Add(parent);
- }
- }
-
- var nodeDatabase = SearcherDatabase.Create(root, string.Empty, false);
-
- return new Searcher.Searcher(nodeDatabase, new SearchWindowAdapter("Create Node"));
- }
-
- public bool OnSearcherSelectEntry(SearcherItem entry, Vector2 screenMousePosition)
- {
- if (entry == null || (entry as SearchNodeItem).NodeGUID.node == null)
- return true;
-
- var nodeEntry = (entry as SearchNodeItem).NodeGUID;
-
- if (nodeEntry.node is PropertyNode propNode)
- if (propNode.property is Serialization.MultiJsonInternal.UnknownShaderPropertyType)
- return true;
-
- var node = CopyNodeForGraph(nodeEntry.node);
-
- var windowRoot = m_EditorWindow.rootVisualElement;
- var windowMousePosition = windowRoot.ChangeCoordinatesTo(windowRoot.parent, screenMousePosition); //- m_EditorWindow.position.position);
- var graphMousePosition = m_GraphView.contentViewContainer.WorldToLocal(windowMousePosition);
-
- m_Graph.owner.RegisterCompleteObjectUndo("Add " + node.name);
-
- if (node is BlockNode blockNode)
- {
- if (!(target is ContextView contextView))
- return true;
-
- // ensure custom blocks have a unique name provided the existing context.
- if (blockNode.isCustomBlock)
- {
- HashSet<string> usedNames = new HashSet<string>();
- foreach (var other in contextView.contextData.blocks) usedNames.Add(other.value.descriptor.displayName);
- blockNode.customName = GraphUtil.SanitizeName(usedNames, "{0}_{1}", blockNode.descriptor.displayName);
- }
- // Test against all current BlockNodes in the Context
- // Never allow duplicate BlockNodes
- else if (contextView.contextData.blocks.Where(x => x.value.name == blockNode.name).FirstOrDefault().value != null)
- {
- return true;
- }
-
- // Insert block to Data
- blockNode.owner = m_Graph;
- int index = contextView.GetInsertionIndex(screenMousePosition);
- m_Graph.AddBlock(blockNode, contextView.contextData, index);
- return true;
- }
-
- var drawState = node.drawState;
- drawState.position = new Rect(graphMousePosition, Vector2.zero);
- node.drawState = drawState;
- m_Graph.AddNode(node);
-
- if (connectedPort != null)
- {
- var connectedSlot = connectedPort.slot;
- var connectedSlotReference = connectedSlot.owner.GetSlotReference(connectedSlot.id);
- var compatibleSlotReference = node.GetSlotReference(nodeEntry.compatibleSlotId);
-
- var fromReference = connectedSlot.isOutputSlot ? connectedSlotReference : compatibleSlotReference;
- var toReference = connectedSlot.isOutputSlot ? compatibleSlotReference : connectedSlotReference;
- m_Graph.Connect(fromReference, toReference);
-
- nodeNeedsRepositioning = true;
- targetSlotReference = compatibleSlotReference;
- targetPosition = graphMousePosition;
- }
-
- return true;
- }
-
- public AbstractMaterialNode CopyNodeForGraph(AbstractMaterialNode oldNode)
- {
- var newNode = (AbstractMaterialNode)Activator.CreateInstance(oldNode.GetType());
- if (ShaderGraphPreferences.allowDeprecatedBehaviors && oldNode.sgVersion != newNode.sgVersion)
- {
- newNode.ChangeVersion(oldNode.sgVersion);
- }
- if (newNode is SubGraphNode subgraphNode)
- {
- subgraphNode.asset = ((SubGraphNode)oldNode).asset;
- }
- else if (newNode is PropertyNode propertyNode)
- {
- propertyNode.owner = m_Graph;
- propertyNode.property = ((PropertyNode)oldNode).property;
- propertyNode.owner = null;
- }
- else if (newNode is KeywordNode keywordNode)
- {
- keywordNode.owner = m_Graph;
- keywordNode.keyword = ((KeywordNode)oldNode).keyword;
- keywordNode.owner = null;
- }
- else if (newNode is DropdownNode dropdownNode)
- {
- dropdownNode.owner = m_Graph;
- dropdownNode.dropdown = ((DropdownNode)oldNode).dropdown;
- dropdownNode.owner = null;
- }
- else if (newNode is BlockNode blockNode)
- {
- blockNode.owner = m_Graph;
- blockNode.Init(((BlockNode)oldNode).descriptor);
- blockNode.owner = null;
- }
- else if (newNode is CustomInterpolatorNode cinode)
- {
- cinode.owner = m_Graph;
- cinode.ConnectToCustomBlockByName(((CustomInterpolatorNode)oldNode).customBlockNodeName);
- cinode.owner = null;
- }
- return newNode;
- }
- }
- }
|