A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEngine;
5using UnityEngine.UIElements;
6using UnityEditor.Experimental.GraphView;
7using UnityEditor.ShaderGraph.Drawing;
8using UnityEditor.ShaderGraph.Serialization;
9
10namespace UnityEditor.ShaderGraph
11{
12 sealed class ContextView : StackNode, IDisposable
13 {
14 ContextData m_ContextData;
15
16 // Currently we only need one Port per context
17 // As the Contexts are hardcoded we know their directions
18 Port m_Port;
19
20 //need this from graph view specifically for nodecreation
21 EditorWindow m_EditorWindow;
22
23 // When dealing with more Contexts, `name` should be serialized in the ContextData
24 // Right now we dont do this so we dont overcommit to serializing unknowns
25 public ContextView(string name, ContextData contextData, EditorWindow editorWindow)
26 {
27 // Set data
28 m_ContextData = contextData;
29 m_EditorWindow = editorWindow;
30
31 // Header
32 var headerLabel = new Label() { name = "headerLabel" };
33 headerLabel.text = name;
34 headerContainer.Add(headerLabel);
35 }
36
37 public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
38 {
39 // Disable the context menu for block nodes. This prevents a duplicate "disconnect all"
40 // option from getting registered which grays out stack block node's option.
41 if (evt.target is MaterialNodeView) return;
42
43 // If the user didn't click on a block node (i.e. the stack frame), include the "Add Block Node" item.
44 InsertCreateNodeAction(evt, childCount, 0);
45 evt.menu.InsertSeparator(null, 1);
46 }
47
48 public ContextData contextData => m_ContextData;
49 public Port port => m_Port;
50
51 // We need to use graphViewChange.movedElements to check whether a BlockNode has moved onto the GraphView
52 // but Nodes return in movedElements when they are mid-drag because they are removed from the stack (placeholder)
53 // StackNode has `dragEntered` but its protected so we need `isDragging`
54 public bool isDragging => dragEntered;
55
56 public void AddPort(Direction direction)
57 {
58 var capacity = direction == Direction.Input ? Port.Capacity.Single : Port.Capacity.Multi;
59 var container = direction == Direction.Input ? inputContainer : outputContainer;
60 m_Port = Port.Create<Edge>(Orientation.Vertical, direction, capacity, null);
61 m_Port.portName = "";
62
63 // Vertical ports have no representation in Model
64 // Therefore we need to disable interaction
65 m_Port.pickingMode = PickingMode.Ignore;
66
67 container.Add(m_Port);
68 }
69
70 public void InsertBlock(MaterialNodeView nodeView)
71 {
72 if (!(nodeView.userData is BlockNode blockNode))
73 return;
74
75 // If index is -1 the node is being added to the end of the Stack
76 if (blockNode.index == -1)
77 {
78 AddElement(nodeView);
79 return;
80 }
81
82 // Add or Insert based on index
83 if (blockNode.index >= contentContainer.childCount)
84 {
85 AddElement(nodeView);
86 }
87 else
88 {
89 InsertElement(blockNode.index, nodeView);
90 }
91 }
92
93 public void InsertElements(int insertIndex, IEnumerable<GraphElement> elements)
94 {
95 var blockDatas = elements.Select(x => x.userData as BlockNode).ToArray();
96 for (int i = 0; i < blockDatas.Length; i++)
97 {
98 contextData.blocks.Remove(blockDatas[i]);
99 }
100
101 int count = elements.Count();
102 var refs = new JsonRef<BlockNode>[count];
103 for (int i = 0; i < count; i++)
104 {
105 refs[i] = blockDatas[i];
106 }
107
108 contextData.blocks.InsertRange(insertIndex, refs);
109
110 var window = m_EditorWindow as MaterialGraphEditWindow;
111 window?.graphEditorView?.graphView?.graph?.ValidateCustomBlockLimit();
112 }
113
114 protected override bool AcceptsElement(GraphElement element, ref int proposedIndex, int maxIndex)
115 {
116 return element.userData is BlockNode blockNode && blockNode.descriptor != null &&
117 blockNode.descriptor.shaderStage == contextData.shaderStage;
118 }
119
120 protected override void OnSeparatorContextualMenuEvent(ContextualMenuPopulateEvent evt, int separatorIndex)
121 {
122 base.OnSeparatorContextualMenuEvent(evt, separatorIndex);
123 InsertCreateNodeAction(evt, separatorIndex, 0);
124 }
125
126 void InsertCreateNodeAction(ContextualMenuPopulateEvent evt, int separatorIndex, int itemIndex)
127 {
128 //we need to arbitrarily add the editor position values because node creation context
129 //exptects a non local coordinate
130 var mousePosition = evt.mousePosition + m_EditorWindow.position.position;
131 var graphView = GetFirstAncestorOfType<MaterialGraphView>();
132
133 evt.menu.InsertAction(itemIndex, "Add Block Node", (e) =>
134 {
135 var context = new NodeCreationContext
136 {
137 screenMousePosition = mousePosition,
138 target = this,
139 index = separatorIndex,
140 };
141 graphView.nodeCreationRequest(context);
142 });
143 }
144
145 public void Dispose()
146 {
147 m_Port = null;
148 m_ContextData = null;
149 m_EditorWindow = null;
150 inputContainer.Clear();
151 outputContainer.Clear();
152 }
153 }
154}