123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEditor.Graphing.Util;
- using UnityEngine;
- using UnityEditor.Graphing;
- using Object = UnityEngine.Object;
- using UnityEditor.Graphs;
- using UnityEditor.Experimental.GraphView;
- using UnityEditor.ShaderGraph.Drawing.Colors;
- using UnityEditor.ShaderGraph.Internal;
- using UnityEngine.UIElements;
- using Edge = UnityEditor.Experimental.GraphView.Edge;
- using Node = UnityEditor.Experimental.GraphView.Node;
- namespace UnityEditor.ShaderGraph.Drawing
- {
- sealed class MaterialGraphView : GraphView
- {
- public MaterialGraphView()
- {
- styleSheets.Add(Resources.Load<StyleSheet>("Styles/MaterialGraphView"));
- serializeGraphElements = SerializeGraphElementsImplementation;
- canPasteSerializedData = CanPasteSerializedDataImplementation;
- unserializeAndPaste = UnserializeAndPasteImplementation;
- deleteSelection = DeleteSelectionImplementation;
- RegisterCallback<DragUpdatedEvent>(OnDragUpdatedEvent);
- RegisterCallback<DragPerformEvent>(OnDragPerformEvent);
- }
- protected override bool canCopySelection
- {
- get { return selection.OfType<IShaderNodeView>().Any(x => x.node.canCopyNode) || selection.OfType<Group>().Any() || selection.OfType<BlackboardField>().Any(); }
- }
- public MaterialGraphView(GraphData graph) : this()
- {
- this.graph = graph;
- }
- public GraphData graph { get; private set; }
- public Action onConvertToSubgraphClick { get; set; }
- public override List<Port> GetCompatiblePorts(Port startAnchor, NodeAdapter nodeAdapter)
- {
- var compatibleAnchors = new List<Port>();
- var startSlot = startAnchor.GetSlot();
- if (startSlot == null)
- return compatibleAnchors;
- var startStage = startSlot.stageCapability;
- if (startStage == ShaderStageCapability.All)
- startStage = NodeUtils.GetEffectiveShaderStageCapability(startSlot, true) & NodeUtils.GetEffectiveShaderStageCapability(startSlot, false);
- foreach (var candidateAnchor in ports.ToList())
- {
- var candidateSlot = candidateAnchor.GetSlot();
- if (!startSlot.IsCompatibleWith(candidateSlot))
- continue;
- if (startStage != ShaderStageCapability.All)
- {
- var candidateStage = candidateSlot.stageCapability;
- if (candidateStage == ShaderStageCapability.All)
- candidateStage = NodeUtils.GetEffectiveShaderStageCapability(candidateSlot, true)
- & NodeUtils.GetEffectiveShaderStageCapability(candidateSlot, false);
- if (candidateStage != ShaderStageCapability.All && candidateStage != startStage)
- continue;
- }
- compatibleAnchors.Add(candidateAnchor);
- }
- return compatibleAnchors;
- }
- public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
- {
- Vector2 mousePosition = evt.mousePosition;
- base.BuildContextualMenu(evt);
- if(evt.target is GraphView)
- {
- evt.menu.InsertAction(1, "Create Sticky Note", (e) => { AddStickyNote(mousePosition); });
- }
- if (evt.target is GraphView || evt.target is Node)
- {
- InitializeViewSubMenu(evt);
- evt.menu.AppendAction("Convert To Sub-graph", ConvertToSubgraph, ConvertToSubgraphStatus);
- evt.menu.AppendAction("Convert To Inline Node", ConvertToInlineNode, ConvertToInlineNodeStatus);
- evt.menu.AppendAction("Convert To Property", ConvertToProperty, ConvertToPropertyStatus);
- evt.menu.AppendAction("Group Selection", _ => GroupSelection(), (a) =>
- {
- List<ISelectable> filteredSelection = new List<ISelectable>();
- foreach (ISelectable selectedObject in selection)
- {
- if (selectedObject is Group)
- return DropdownMenuAction.Status.Disabled;
- VisualElement ve = selectedObject as VisualElement;
- if (ve.userData is AbstractMaterialNode)
- {
- var selectedNode = selectedObject as Node;
- if (selectedNode.GetContainingScope() is Group)
- return DropdownMenuAction.Status.Disabled;
- filteredSelection.Add(selectedObject);
- }
- }
- if (filteredSelection.Count > 0)
- return DropdownMenuAction.Status.Normal;
- else
- return DropdownMenuAction.Status.Disabled;
- });
- evt.menu.AppendAction("Ungroup Selection", RemoveFromGroupNode, (a) =>
- {
- List<ISelectable> filteredSelection = new List<ISelectable>();
- foreach (ISelectable selectedObject in selection)
- {
- if (selectedObject is Group)
- return DropdownMenuAction.Status.Disabled;
- VisualElement ve = selectedObject as VisualElement;
- if (ve.userData is AbstractMaterialNode)
- {
- var selectedNode = selectedObject as Node;
- if (selectedNode.GetContainingScope() is Group)
- filteredSelection.Add(selectedObject);
- }
- }
- if (filteredSelection.Count > 0)
- return DropdownMenuAction.Status.Normal;
- else
- return DropdownMenuAction.Status.Disabled;
- });
-
- var editorView = GetFirstAncestorOfType<GraphEditorView>();
- if (editorView.colorManager.activeSupportsCustom && selection.OfType<MaterialNodeView>().Any())
- {
- evt.menu.AppendSeparator();
- evt.menu.AppendAction("Color/Change...", ChangeCustomNodeColor,
- eventBase => DropdownMenuAction.Status.Normal);
- evt.menu.AppendAction("Color/Reset", menuAction =>
- {
- graph.owner.RegisterCompleteObjectUndo("Reset Node Color");
- foreach (var selectable in selection)
- {
- if (selectable is MaterialNodeView nodeView)
- {
- nodeView.node.ResetColor(editorView.colorManager.activeProviderName);
- editorView.colorManager.UpdateNodeView(nodeView);
- }
- }
- }, eventBase => DropdownMenuAction.Status.Normal);
- }
- if (selection.OfType<IShaderNodeView>().Count() == 1)
- {
- evt.menu.AppendSeparator();
- evt.menu.AppendAction("Open Documentation", SeeDocumentation, SeeDocumentationStatus);
- }
- if (selection.OfType<IShaderNodeView>().Count() == 1 && selection.OfType<IShaderNodeView>().First().node is SubGraphNode)
- {
- evt.menu.AppendSeparator();
- evt.menu.AppendAction("Open Sub Graph", OpenSubGraph, (a) => DropdownMenuAction.Status.Normal);
- }
- }
- if (evt.target is BlackboardField)
- {
- evt.menu.AppendAction("Delete", (e) => DeleteSelectionImplementation("Delete", AskUser.DontAskUser), (e) => canDeleteSelection ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
- }
- if (evt.target is MaterialGraphView)
- {
- foreach (AbstractMaterialNode node in graph.GetNodes<AbstractMaterialNode>())
- {
- if (node.hasPreview && node.previewExpanded == true)
- evt.menu.AppendAction("Collapse All Previews", CollapsePreviews, (a) => DropdownMenuAction.Status.Normal);
- if (node.hasPreview && node.previewExpanded == false)
- evt.menu.AppendAction("Expand All Previews", ExpandPreviews, (a) => DropdownMenuAction.Status.Normal);
- }
- evt.menu.AppendSeparator();
- }
- }
- private void InitializeViewSubMenu(ContextualMenuPopulateEvent evt)
- {
- // Default the menu buttons to disabled
- DropdownMenuAction.Status expandPreviewAction = DropdownMenuAction.Status.Disabled;
- DropdownMenuAction.Status collapsePreviewAction = DropdownMenuAction.Status.Disabled;
- DropdownMenuAction.Status minimizeAction = DropdownMenuAction.Status.Disabled;
- DropdownMenuAction.Status maximizeAction = DropdownMenuAction.Status.Disabled;
- // Initialize strings
- string expandPreviewText = "View/Expand Previews";
- string collapsePreviewText = "View/Collapse Previews";
- string expandPortText = "View/Expand Ports";
- string collapsePortText = "View/Collapse Ports";
- if (selection.Count == 1)
- {
- collapsePreviewText = "View/Collapse Preview";
- expandPreviewText = "View/Expand Preview";
- }
- // Check if we can expand or collapse the ports/previews
- foreach (MaterialNodeView selectedNode in selection.Where(x => x is MaterialNodeView).Select(x => x as MaterialNodeView))
- {
- if (selectedNode.node.hasPreview)
- {
- if (selectedNode.node.previewExpanded)
- collapsePreviewAction = DropdownMenuAction.Status.Normal;
- else
- expandPreviewAction = DropdownMenuAction.Status.Normal;
- }
- if (selectedNode.CanToggleExpanded())
- {
- if (selectedNode.expanded)
- minimizeAction = DropdownMenuAction.Status.Normal;
- else
- maximizeAction = DropdownMenuAction.Status.Normal;
- }
- }
- // Create the menu options
- evt.menu.AppendAction(collapsePortText, _ => SetNodeExpandedOnSelection(false), (a) => minimizeAction);
- evt.menu.AppendAction(expandPortText, _ => SetNodeExpandedOnSelection(true), (a) => maximizeAction);
- evt.menu.AppendSeparator("View/");
- evt.menu.AppendAction(expandPreviewText, _ => SetPreviewExpandedOnSelection(true), (a) => expandPreviewAction);
- evt.menu.AppendAction(collapsePreviewText, _ => SetPreviewExpandedOnSelection(false), (a) => collapsePreviewAction);
- evt.menu.AppendSeparator();
- }
- void ChangeCustomNodeColor(DropdownMenuAction menuAction)
- {
- // Color Picker is internal :(
- var t = typeof(EditorWindow).Assembly.GetTypes().FirstOrDefault(ty => ty.Name == "ColorPicker");
- var m = t?.GetMethod("Show", new[] {typeof(Action<Color>), typeof(Color), typeof(bool), typeof(bool)});
- if (m == null)
- {
- Debug.LogWarning("Could not invoke Color Picker for ShaderGraph.");
- return;
- }
- var editorView = GetFirstAncestorOfType<GraphEditorView>();
- var defaultColor = Color.gray;
- if (selection.FirstOrDefault(sel => sel is MaterialNodeView) is MaterialNodeView selNode1)
- {
- defaultColor = selNode1.GetColor();
- defaultColor.a = 1.0f;
- }
- void ApplyColor(Color pickedColor)
- {
- foreach (var selectable in selection)
- {
- if(selectable is MaterialNodeView nodeView)
- {
- nodeView.node.SetColor(editorView.colorManager.activeProviderName, pickedColor);
- editorView.colorManager.UpdateNodeView(nodeView);
- }
- }
- }
- graph.owner.RegisterCompleteObjectUndo("Change Node Color");
- m.Invoke(null, new object[] {(Action<Color>) ApplyColor, defaultColor, true, false});
- }
- protected override bool canDeleteSelection
- {
- get
- {
- return selection.Any(x => !(x is IShaderNodeView nodeView) || nodeView.node.canDeleteNode);
- }
- }
- public void GroupSelection()
- {
- var title = "New Group";
- var groupData = new GroupData(title, new Vector2(10f,10f));
- graph.owner.RegisterCompleteObjectUndo("Create Group Node");
- graph.CreateGroup(groupData);
- foreach (var shaderNodeView in selection.OfType<IShaderNodeView>())
- {
- graph.SetGroup(shaderNodeView.node, groupData);
- }
- }
- public void AddStickyNote(Vector2 position)
- {
- position = contentViewContainer.WorldToLocal(position);
- string title = "New Note";
- string content = "Write something here";
- var stickyNoteData = new StickyNoteData(title, content, new Rect(position.x, position.y, 200, 160));
- graph.owner.RegisterCompleteObjectUndo("Create Sticky Note");
- graph.AddStickyNote(stickyNoteData);
- }
- void RemoveFromGroupNode(DropdownMenuAction action)
- {
- graph.owner.RegisterCompleteObjectUndo("Ungroup Node(s)");
- foreach (ISelectable selectable in selection)
- {
- var node = selectable as Node;
- if(node == null)
- continue;
- Group group = node.GetContainingScope() as Group;
- if (group != null)
- {
- group.RemoveElement(node);
- }
- }
- }
- public void SetNodeExpandedOnSelection(bool state)
- {
- graph.owner.RegisterCompleteObjectUndo("Toggle Expansion");
- foreach (MaterialNodeView selectedNode in selection.Where(x => x is MaterialNodeView).Select(x => x as MaterialNodeView))
- {
- if (selectedNode.CanToggleExpanded())
- {
- selectedNode.expanded = state;
- selectedNode.node.Dirty(ModificationScope.Topological);
- }
- }
- }
- public void SetPreviewExpandedOnSelection(bool state)
- {
- graph.owner.RegisterCompleteObjectUndo("Toggle Preview Visibility");
- foreach (MaterialNodeView selectedNode in selection.Where(x => x is MaterialNodeView).Select(x => x as MaterialNodeView))
- {
- selectedNode.node.previewExpanded = state;
- }
- }
- void CollapsePreviews(DropdownMenuAction action)
- {
- graph.owner.RegisterCompleteObjectUndo("Collapse Previews");
- foreach (AbstractMaterialNode node in graph.GetNodes<AbstractMaterialNode>())
- {
- node.previewExpanded = false;
- }
- }
- void ExpandPreviews(DropdownMenuAction action)
- {
- graph.owner.RegisterCompleteObjectUndo("Expand Previews");
- foreach (AbstractMaterialNode node in graph.GetNodes<AbstractMaterialNode>())
- {
- node.previewExpanded = true;
- }
- }
- void SeeDocumentation(DropdownMenuAction action)
- {
- var node = selection.OfType<IShaderNodeView>().First().node;
- if (node.documentationURL != null)
- System.Diagnostics.Process.Start(node.documentationURL);
- }
- void OpenSubGraph(DropdownMenuAction action)
- {
- SubGraphNode subgraphNode = selection.OfType<IShaderNodeView>().First().node as SubGraphNode;
- var path = AssetDatabase.GetAssetPath(subgraphNode.asset);
- ShaderGraphImporterEditor.ShowGraphEditWindow(path);
- }
- DropdownMenuAction.Status SeeDocumentationStatus(DropdownMenuAction action)
- {
- if (selection.OfType<IShaderNodeView>().First().node.documentationURL == null)
- return DropdownMenuAction.Status.Disabled;
- return DropdownMenuAction.Status.Normal;
- }
- DropdownMenuAction.Status ConvertToPropertyStatus(DropdownMenuAction action)
- {
- if (selection.OfType<IShaderNodeView>().Any(v => v.node != null))
- {
- if (selection.OfType<IShaderNodeView>().Any(v => v.node is IPropertyFromNode))
- return DropdownMenuAction.Status.Normal;
- return DropdownMenuAction.Status.Disabled;
- }
- return DropdownMenuAction.Status.Hidden;
- }
- void ConvertToProperty(DropdownMenuAction action)
- {
- graph.owner.RegisterCompleteObjectUndo("Convert to Property");
- var selectedNodeViews = selection.OfType<IShaderNodeView>().Select(x => x.node).ToList();
- foreach (var node in selectedNodeViews)
- {
- if (!(node is IPropertyFromNode))
- continue;
- var converter = node as IPropertyFromNode;
- var prop = converter.AsShaderProperty();
- graph.SanitizeGraphInputName(prop);
- graph.AddGraphInput(prop);
- var propNode = new PropertyNode();
- propNode.drawState = node.drawState;
- propNode.groupGuid = node.groupGuid;
- graph.AddNode(propNode);
- propNode.propertyGuid = prop.guid;
- var oldSlot = node.FindSlot<MaterialSlot>(converter.outputSlotId);
- var newSlot = propNode.FindSlot<MaterialSlot>(PropertyNode.OutputSlotId);
- foreach (var edge in graph.GetEdges(oldSlot.slotReference))
- graph.Connect(newSlot.slotReference, edge.inputSlot);
- graph.RemoveNode(node);
- }
- }
- DropdownMenuAction.Status ConvertToInlineNodeStatus(DropdownMenuAction action)
- {
- if (selection.OfType<IShaderNodeView>().Any(v => v.node != null))
- {
- if (selection.OfType<IShaderNodeView>().Any(v => v.node is PropertyNode))
- return DropdownMenuAction.Status.Normal;
- return DropdownMenuAction.Status.Disabled;
- }
- return DropdownMenuAction.Status.Hidden;
- }
- void ConvertToInlineNode(DropdownMenuAction action)
- {
- graph.owner.RegisterCompleteObjectUndo("Convert to Inline Node");
- var selectedNodeViews = selection.OfType<IShaderNodeView>()
- .Select(x => x.node)
- .OfType<PropertyNode>();
- foreach (var propNode in selectedNodeViews)
- ((GraphData)propNode.owner).ReplacePropertyNodeWithConcreteNode(propNode);
- }
- DropdownMenuAction.Status ConvertToSubgraphStatus(DropdownMenuAction action)
- {
- if (onConvertToSubgraphClick == null) return DropdownMenuAction.Status.Hidden;
- return selection.OfType<IShaderNodeView>().Any(v => v.node != null && v.node.allowedInSubGraph && !(v.node is SubGraphOutputNode) ) ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden;
- }
- void ConvertToSubgraph(DropdownMenuAction action)
- {
- onConvertToSubgraphClick();
- }
- string SerializeGraphElementsImplementation(IEnumerable<GraphElement> elements)
- {
- var groups = elements.OfType<ShaderGroup>().Select(x => x.userData);
- var nodes = elements.OfType<IShaderNodeView>().Select(x => x.node).Where(x => x.canCopyNode);
- var edges = elements.OfType<Edge>().Select(x => x.userData).OfType<IEdge>();
- var inputs = selection.OfType<BlackboardField>().Select(x => x.userData as ShaderInput);
- var notes = elements.OfType<StickyNote>().Select(x => x.userData);
- // Collect the property nodes and get the corresponding properties
- var propertyNodeGuids = nodes.OfType<PropertyNode>().Select(x => x.propertyGuid);
- var metaProperties = this.graph.properties.Where(x => propertyNodeGuids.Contains(x.guid));
- // Collect the keyword nodes and get the corresponding keywords
- var keywordNodeGuids = nodes.OfType<KeywordNode>().Select(x => x.keywordGuid);
- var metaKeywords = this.graph.keywords.Where(x => keywordNodeGuids.Contains(x.guid));
- var graph = new CopyPasteGraph(this.graph.assetGuid, groups, nodes, edges, inputs, metaProperties, metaKeywords, notes);
- return JsonUtility.ToJson(graph, true);
- }
- bool CanPasteSerializedDataImplementation(string serializedData)
- {
- return CopyPasteGraph.FromJson(serializedData) != null;
- }
- void UnserializeAndPasteImplementation(string operationName, string serializedData)
- {
- graph.owner.RegisterCompleteObjectUndo(operationName);
- var pastedGraph = CopyPasteGraph.FromJson(serializedData);
- this.InsertCopyPasteGraph(pastedGraph);
- }
- void DeleteSelectionImplementation(string operationName, GraphView.AskUser askUser)
- {
- bool containsProperty = false;
- // Keywords need to be tested against variant limit based on multiple factors
- bool keywordsDirty = false;
- // Track dependent keyword nodes to remove them
- List<KeywordNode> keywordNodes = new List<KeywordNode>();
- foreach (var selectable in selection)
- {
- var field = selectable as BlackboardField;
- if (field != null && field.userData != null)
- {
- switch(field.userData)
- {
- case AbstractShaderProperty property:
- containsProperty = true;
- break;
- case ShaderKeyword keyword:
- keywordNodes.AddRange(graph.GetNodes<KeywordNode>().Where(x => x.keywordGuid == keyword.guid));
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- }
- if(containsProperty)
- {
- if (graph.isSubGraph)
- {
- if (!EditorUtility.DisplayDialog("Sub Graph Will Change", "If you remove a property and save the sub graph, you might change other graphs that are using this sub graph.\n\nDo you want to continue?", "Yes", "No"))
- return;
- }
- }
- // Filter nodes that cannot be deleted
- var nodesToDelete = selection.OfType<IShaderNodeView>().Where(v => !(v.node is SubGraphOutputNode) && v.node.canDeleteNode).Select(x => x.node);
-
- // Add keyword nodes dependent on deleted keywords
- nodesToDelete = nodesToDelete.Union(keywordNodes);
- // If deleting a Sub Graph node whose asset contains Keywords test variant limit
- foreach(SubGraphNode subGraphNode in nodesToDelete.OfType<SubGraphNode>())
- {
- if (subGraphNode.asset == null)
- {
- continue;
- }
- if(subGraphNode.asset.keywords.Count > 0)
- {
- keywordsDirty = true;
- }
- }
-
- graph.owner.RegisterCompleteObjectUndo(operationName);
- graph.RemoveElements(nodesToDelete.ToArray(),
- selection.OfType<Edge>().Select(x => x.userData).OfType<IEdge>().ToArray(),
- selection.OfType<ShaderGroup>().Select(x => x.userData).ToArray(),
- selection.OfType<StickyNote>().Select(x => x.userData).ToArray());
-
- foreach (var selectable in selection)
- {
- var field = selectable as BlackboardField;
- if (field != null && field.userData != null)
- {
- var input = (ShaderInput)field.userData;
- graph.RemoveGraphInput(input);
- // If deleting a Keyword test variant limit
- if(input is ShaderKeyword keyword)
- {
- keywordsDirty = true;
- }
- }
- }
- // Test Keywords against variant limit
- if(keywordsDirty)
- {
- graph.OnKeywordChangedNoValidate();
- }
- selection.Clear();
- }
- #region Drag and drop
- bool ValidateObjectForDrop(Object obj)
- {
- return EditorUtility.IsPersistent(obj) && (
- obj is Texture2D ||
- obj is Cubemap ||
- obj is SubGraphAsset asset && !asset.descendents.Contains(graph.assetGuid) && asset.assetGuid != graph.assetGuid ||
- obj is Texture2DArray ||
- obj is Texture3D);
- }
- void OnDragUpdatedEvent(DragUpdatedEvent e)
- {
- var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
- bool dragging = false;
- if (selection != null)
- {
- // Blackboard
- if (selection.OfType<BlackboardField>().Any())
- dragging = true;
- }
- else
- {
- // Handle unity objects
- var objects = DragAndDrop.objectReferences;
- foreach (Object obj in objects)
- {
- if (ValidateObjectForDrop(obj))
- {
- dragging = true;
- break;
- }
- }
- }
- if (dragging)
- {
- DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
- }
- }
- void OnDragPerformEvent(DragPerformEvent e)
- {
- Vector2 localPos = (e.currentTarget as VisualElement).ChangeCoordinatesTo(contentViewContainer, e.localMousePosition);
- var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
- if (selection != null)
- {
- // Blackboard
- if (selection.OfType<BlackboardField>().Any())
- {
- IEnumerable<BlackboardField> fields = selection.OfType<BlackboardField>();
- foreach (BlackboardField field in fields)
- {
- CreateNode(field, localPos);
- }
- }
- }
- else
- {
- // Handle unity objects
- var objects = DragAndDrop.objectReferences;
- foreach (Object obj in objects)
- {
- if (ValidateObjectForDrop(obj))
- {
- CreateNode(obj, localPos);
- }
- }
- }
- }
- void CreateNode(object obj, Vector2 nodePosition)
- {
- var texture2D = obj as Texture2D;
- if (texture2D != null)
- {
- graph.owner.RegisterCompleteObjectUndo("Drag Texture");
- bool isNormalMap = false;
- if (EditorUtility.IsPersistent(texture2D) && !string.IsNullOrEmpty(AssetDatabase.GetAssetPath(texture2D)))
- {
- var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture2D)) as TextureImporter;
- if (importer != null)
- isNormalMap = importer.textureType == TextureImporterType.NormalMap;
- }
- var node = new SampleTexture2DNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- graph.AddNode(node);
- if (isNormalMap)
- node.textureType = TextureType.Normal;
- var inputslot = node.FindInputSlot<Texture2DInputMaterialSlot>(SampleTexture2DNode.TextureInputId);
- if (inputslot != null)
- inputslot.texture = texture2D;
- }
- var textureArray = obj as Texture2DArray;
- if (textureArray != null)
- {
- graph.owner.RegisterCompleteObjectUndo("Drag Texture Array");
-
- var node = new SampleTexture2DArrayNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- graph.AddNode(node);
- var inputslot = node.FindSlot<Texture2DArrayInputMaterialSlot>(SampleTexture2DArrayNode.TextureInputId);
- if (inputslot != null)
- inputslot.textureArray = textureArray;
- }
- var texture3D = obj as Texture3D;
- if (texture3D != null)
- {
- graph.owner.RegisterCompleteObjectUndo("Drag Texture 3D");
- var node = new SampleTexture3DNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- graph.AddNode(node);
- var inputslot = node.FindSlot<Texture3DInputMaterialSlot>(SampleTexture3DNode.TextureInputId);
- if (inputslot != null)
- inputslot.texture = texture3D;
- }
- var cubemap = obj as Cubemap;
- if (cubemap != null)
- {
- graph.owner.RegisterCompleteObjectUndo("Drag Cubemap");
-
- var node = new SampleCubemapNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- graph.AddNode(node);
- var inputslot = node.FindInputSlot<CubemapInputMaterialSlot>(SampleCubemapNode.CubemapInputId);
- if (inputslot != null)
- inputslot.cubemap = cubemap;
- }
- var subGraphAsset = obj as SubGraphAsset;
- if (subGraphAsset != null)
- {
- graph.owner.RegisterCompleteObjectUndo("Drag Sub-Graph");
- var node = new SubGraphNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- node.asset = subGraphAsset;
- graph.AddNode(node);
- }
- var blackboardField = obj as BlackboardField;
- if (blackboardField != null)
- {
- graph.owner.RegisterCompleteObjectUndo("Drag Graph Input");
- switch(blackboardField.userData)
- {
- case AbstractShaderProperty property:
- {
- var node = new PropertyNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- graph.AddNode(node);
- // Setting the guid requires the graph to be set first.
- node.propertyGuid = property.guid;
- break;
- }
- case ShaderKeyword keyword:
- {
- var node = new KeywordNode();
- var drawState = node.drawState;
- drawState.position = new Rect(nodePosition, drawState.position.size);
- node.drawState = drawState;
- graph.AddNode(node);
- // Setting the guid requires the graph to be set first.
- node.keywordGuid = keyword.guid;
- break;
- }
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- }
- #endregion
- }
- static class GraphViewExtensions
- {
- internal static void InsertCopyPasteGraph(this MaterialGraphView graphView, CopyPasteGraph copyGraph)
- {
- if (copyGraph == null)
- return;
- // Keywords need to be tested against variant limit based on multiple factors
- bool keywordsDirty = false;
- // Make new inputs from the copied graph
- foreach (ShaderInput input in copyGraph.inputs)
- {
- ShaderInput copiedInput = input.Copy();
- graphView.graph.SanitizeGraphInputName(copiedInput);
- graphView.graph.SanitizeGraphInputReferenceName(copiedInput, input.overrideReferenceName);
- graphView.graph.AddGraphInput(copiedInput);
- switch(input)
- {
- case AbstractShaderProperty property:
- // Update the property nodes that depends on the copied node
- var dependentPropertyNodes = copyGraph.GetNodes<PropertyNode>().Where(x => x.propertyGuid == input.guid);
- foreach (var node in dependentPropertyNodes)
- {
- node.owner = graphView.graph;
- node.propertyGuid = copiedInput.guid;
- }
- break;
- case ShaderKeyword shaderKeyword:
- // Update the keyword nodes that depends on the copied node
- var dependentKeywordNodes = copyGraph.GetNodes<KeywordNode>().Where(x => x.keywordGuid == input.guid);
- foreach (var node in dependentKeywordNodes)
- {
- node.owner = graphView.graph;
- node.keywordGuid = copiedInput.guid;
- }
- // Pasting a new Keyword so need to test against variant limit
- keywordsDirty = true;
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- // Pasting a Sub Graph node that contains Keywords so need to test against variant limit
- foreach(SubGraphNode subGraphNode in copyGraph.GetNodes<SubGraphNode>())
- {
- if(subGraphNode.asset.keywords.Count > 0)
- {
- keywordsDirty = true;
- }
- }
- // Test Keywords against variant limit
- if(keywordsDirty)
- {
- graphView.graph.OnKeywordChangedNoValidate();
- }
- using (var remappedNodesDisposable = ListPool<AbstractMaterialNode>.GetDisposable())
- {
- using (var remappedEdgesDisposable = ListPool<IEdge>.GetDisposable())
- {
- var remappedNodes = remappedNodesDisposable.value;
- var remappedEdges = remappedEdgesDisposable.value;
- graphView.graph.PasteGraph(copyGraph, remappedNodes, remappedEdges);
- if (graphView.graph.assetGuid != copyGraph.sourceGraphGuid)
- {
- // Compute the mean of the copied nodes.
- Vector2 centroid = Vector2.zero;
- var count = 1;
- foreach (var node in remappedNodes)
- {
- var position = node.drawState.position.position;
- centroid = centroid + (position - centroid) / count;
- ++count;
- }
- // Get the center of the current view
- var viewCenter = graphView.contentViewContainer.WorldToLocal(graphView.layout.center);
- foreach (var node in remappedNodes)
- {
- var drawState = node.drawState;
- var positionRect = drawState.position;
- var position = positionRect.position;
- position += viewCenter - centroid;
- positionRect.position = position;
- drawState.position = positionRect;
- node.drawState = drawState;
- }
- }
- // Add new elements to selection
- graphView.ClearSelection();
- graphView.graphElements.ForEach(element =>
- {
- if (element is Edge edge && remappedEdges.Contains(edge.userData as IEdge))
- graphView.AddToSelection(edge);
- if (element is IShaderNodeView nodeView && remappedNodes.Contains(nodeView.node))
- graphView.AddToSelection((Node)nodeView);
- });
- }
- }
- }
- }
- }
|