A game about forced loneliness, made by TACStudios
1using UnityEngine;
2using System;
3using System.Collections.Generic;
4using UnityEditor.Graphing;
5using UnityEditor.ShaderGraph.Drawing.Controls;
6using UnityEditor.ShaderGraph.Internal;
7using UnityEngine.Assertions;
8using UnityEngine.UIElements;
9using GraphDataStore = UnityEditor.ShaderGraph.DataStore<UnityEditor.ShaderGraph.GraphData>;
10
11namespace UnityEditor.ShaderGraph.Drawing
12{
13 class ChangeExposedFlagAction : IGraphDataAction
14 {
15 internal ChangeExposedFlagAction(ShaderInput shaderInput, bool newIsExposed)
16 {
17 this.shaderInputReference = shaderInput;
18 this.newIsExposedValue = newIsExposed;
19 this.oldIsExposedValue = shaderInput.generatePropertyBlock;
20 }
21
22 void ChangeExposedFlag(GraphData graphData)
23 {
24 AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeExposedFlagAction");
25 AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ChangeExposedFlagAction");
26 // The Undos are currently handled in ShaderInputPropertyDrawer but we want to move that out from there and handle here
27 //graphData.owner.RegisterCompleteObjectUndo("Change Exposed Toggle");
28 shaderInputReference.generatePropertyBlock = newIsExposedValue;
29 }
30
31 public Action<GraphData> modifyGraphDataAction => ChangeExposedFlag;
32
33 // Reference to the shader input being modified
34 internal ShaderInput shaderInputReference { get; private set; }
35
36 // New value of whether the shader input should be exposed to the material inspector
37 internal bool newIsExposedValue { get; private set; }
38 internal bool oldIsExposedValue { get; private set; }
39 }
40
41 class ChangePropertyValueAction : IGraphDataAction
42 {
43 void ChangePropertyValue(GraphData graphData)
44 {
45 AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangePropertyValueAction");
46 AssertHelpers.IsNotNull(shaderInputReference, "ShaderPropertyReference is null while carrying out ChangePropertyValueAction");
47 // The Undos are currently handled in ShaderInputPropertyDrawer but we want to move that out from there and handle here
48 //graphData.owner.RegisterCompleteObjectUndo("Change Property Value");
49 var material = graphData.owner.materialArtifact;
50 switch (shaderInputReference)
51 {
52 case BooleanShaderProperty booleanProperty:
53 booleanProperty.value = ((ToggleData)newShaderInputValue).isOn;
54 if (material) material.SetFloat(shaderInputReference.referenceName, booleanProperty.value ? 1.0f : 0.0f);
55 break;
56 case Vector1ShaderProperty vector1Property:
57 vector1Property.value = (float)newShaderInputValue;
58 if (material) material.SetFloat(shaderInputReference.referenceName, vector1Property.value);
59 break;
60 case Vector2ShaderProperty vector2Property:
61 vector2Property.value = (Vector2)newShaderInputValue;
62 if (material) material.SetVector(shaderInputReference.referenceName, vector2Property.value);
63 break;
64 case Vector3ShaderProperty vector3Property:
65 vector3Property.value = (Vector3)newShaderInputValue;
66 if (material) material.SetVector(shaderInputReference.referenceName, vector3Property.value);
67 break;
68 case Vector4ShaderProperty vector4Property:
69 vector4Property.value = (Vector4)newShaderInputValue;
70 if (material) material.SetVector(shaderInputReference.referenceName, vector4Property.value);
71 break;
72 case ColorShaderProperty colorProperty:
73 colorProperty.value = (Color)newShaderInputValue;
74 if (material) material.SetColor(shaderInputReference.referenceName, colorProperty.value);
75 break;
76 case Texture2DShaderProperty texture2DProperty:
77 texture2DProperty.value.texture = (Texture)newShaderInputValue;
78 if (material) material.SetTexture(shaderInputReference.referenceName, texture2DProperty.value.texture);
79 break;
80 case Texture2DArrayShaderProperty texture2DArrayProperty:
81 texture2DArrayProperty.value.textureArray = (Texture2DArray)newShaderInputValue;
82 if (material) material.SetTexture(shaderInputReference.referenceName, texture2DArrayProperty.value.textureArray);
83 break;
84 case Texture3DShaderProperty texture3DProperty:
85 texture3DProperty.value.texture = (Texture3D)newShaderInputValue;
86 if (material) material.SetTexture(shaderInputReference.referenceName, texture3DProperty.value.texture);
87 break;
88 case CubemapShaderProperty cubemapProperty:
89 cubemapProperty.value.cubemap = (Cubemap)newShaderInputValue;
90 if (material) material.SetTexture(shaderInputReference.referenceName, cubemapProperty.value.cubemap);
91 break;
92 case Matrix2ShaderProperty matrix2Property:
93 matrix2Property.value = (Matrix4x4)newShaderInputValue;
94 break;
95 case Matrix3ShaderProperty matrix3Property:
96 matrix3Property.value = (Matrix4x4)newShaderInputValue;
97 break;
98 case Matrix4ShaderProperty matrix4Property:
99 matrix4Property.value = (Matrix4x4)newShaderInputValue;
100 break;
101 case SamplerStateShaderProperty samplerStateProperty:
102 samplerStateProperty.value = (TextureSamplerState)newShaderInputValue;
103 break;
104 case GradientShaderProperty gradientProperty:
105 gradientProperty.value = (Gradient)newShaderInputValue;
106 break;
107 default:
108 throw new ArgumentOutOfRangeException();
109 }
110 }
111
112 public Action<GraphData> modifyGraphDataAction => ChangePropertyValue;
113
114 // Reference to the shader input being modified
115 internal ShaderInput shaderInputReference { get; set; }
116
117 // New value of the shader property
118
119 internal object newShaderInputValue { get; set; }
120 }
121
122 class ChangeDisplayNameAction : IGraphDataAction
123 {
124 void ChangeDisplayName(GraphData graphData)
125 {
126 AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeDisplayNameAction");
127 AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ChangeDisplayNameAction");
128 graphData.owner.RegisterCompleteObjectUndo("Change Display Name");
129 if (newDisplayNameValue != shaderInputReference.displayName)
130 {
131 shaderInputReference.SetDisplayNameAndSanitizeForGraph(graphData, newDisplayNameValue);
132 }
133 }
134
135 public Action<GraphData> modifyGraphDataAction => ChangeDisplayName;
136
137 // Reference to the shader input being modified
138 internal ShaderInput shaderInputReference { get; set; }
139
140 internal string newDisplayNameValue { get; set; }
141 }
142
143 class ChangeReferenceNameAction : IGraphDataAction
144 {
145 void ChangeReferenceName(GraphData graphData)
146 {
147 AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ChangeReferenceNameAction");
148 AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ChangeReferenceNameAction");
149 // The Undos are currently handled in ShaderInputPropertyDrawer but we want to move that out from there and handle here
150 //graphData.owner.RegisterCompleteObjectUndo("Change Reference Name");
151 if (newReferenceNameValue != shaderInputReference.overrideReferenceName)
152 {
153 graphData.SanitizeGraphInputReferenceName(shaderInputReference, newReferenceNameValue);
154 }
155 }
156
157 public Action<GraphData> modifyGraphDataAction => ChangeReferenceName;
158
159 // Reference to the shader input being modified
160 internal ShaderInput shaderInputReference { get; set; }
161
162 internal string newReferenceNameValue { get; set; }
163 }
164
165 class ResetReferenceNameAction : IGraphDataAction
166 {
167 void ResetReferenceName(GraphData graphData)
168 {
169 AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out ResetReferenceNameAction");
170 AssertHelpers.IsNotNull(shaderInputReference, "ShaderInputReference is null while carrying out ResetReferenceNameAction");
171 graphData.owner.RegisterCompleteObjectUndo("Reset Reference Name");
172 shaderInputReference.overrideReferenceName = null;
173 }
174
175 public Action<GraphData> modifyGraphDataAction => ResetReferenceName;
176
177 // Reference to the shader input being modified
178 internal ShaderInput shaderInputReference { get; set; }
179 }
180
181 class DeleteShaderInputAction : IGraphDataAction
182 {
183 void DeleteShaderInput(GraphData graphData)
184 {
185 AssertHelpers.IsNotNull(graphData, "GraphData is null while carrying out DeleteShaderInputAction");
186 AssertHelpers.IsNotNull(shaderInputsToDelete, "ShaderInputsToDelete is null while carrying out DeleteShaderInputAction");
187 // This is called by MaterialGraphView currently, no need to repeat it here, though ideally it would live here
188 //graphData.owner.RegisterCompleteObjectUndo("Delete Graph Input(s)");
189
190 foreach (var shaderInput in shaderInputsToDelete)
191 {
192 graphData.RemoveGraphInput(shaderInput);
193 }
194 }
195
196 public Action<GraphData> modifyGraphDataAction => DeleteShaderInput;
197
198 // Reference to the shader input(s) being deleted
199 internal IList<ShaderInput> shaderInputsToDelete { get; set; } = new List<ShaderInput>();
200 }
201
202 class ShaderInputViewController : SGViewController<ShaderInput, ShaderInputViewModel>
203 {
204 // Exposed for PropertyView
205 internal GraphData graphData => DataStore.State;
206
207 internal ShaderInputViewController(ShaderInput shaderInput, ShaderInputViewModel inViewModel, GraphDataStore graphDataStore)
208 : base(shaderInput, inViewModel, graphDataStore)
209 {
210 InitializeViewModel();
211
212 m_SgBlackboardField = new SGBlackboardField(ViewModel);
213 m_SgBlackboardField.controller = this;
214
215 m_BlackboardRowView = new SGBlackboardRow(m_SgBlackboardField, null);
216 m_BlackboardRowView.expanded = SessionState.GetBool($"Unity.ShaderGraph.Input.{shaderInput.objectId}.isExpanded", false);
217 }
218
219 void InitializeViewModel()
220 {
221 if (Model == null)
222 {
223 AssertHelpers.Fail("Could not initialize shader input view model as shader input was null.");
224 return;
225 }
226 ViewModel.model = Model;
227 ViewModel.isSubGraph = DataStore.State.isSubGraph;
228 ViewModel.isInputExposed = (DataStore.State.isSubGraph || Model.isExposed);
229 ViewModel.inputName = Model.displayName;
230 switch (Model)
231 {
232 case AbstractShaderProperty shaderProperty:
233 ViewModel.inputTypeName = shaderProperty.GetPropertyTypeString();
234 // Handles upgrade fix for deprecated old Color property
235 shaderProperty.onBeforeVersionChange += (_) => graphData.owner.RegisterCompleteObjectUndo($"Change {shaderProperty.displayName} Version");
236 break;
237 case ShaderKeyword shaderKeyword:
238 ViewModel.inputTypeName = shaderKeyword.keywordType + " Keyword";
239 ViewModel.inputTypeName = shaderKeyword.isBuiltIn ? "Built-in " + ViewModel.inputTypeName : ViewModel.inputTypeName;
240 break;
241 case ShaderDropdown shaderDropdown:
242 ViewModel.inputTypeName = "Dropdown";
243 break;
244 }
245
246 ViewModel.requestModelChangeAction = this.RequestModelChange;
247 }
248
249 SGBlackboardRow m_BlackboardRowView;
250 SGBlackboardField m_SgBlackboardField;
251
252 internal SGBlackboardRow BlackboardItemView => m_BlackboardRowView;
253
254 protected override void RequestModelChange(IGraphDataAction changeAction)
255 {
256 DataStore.Dispatch(changeAction);
257 }
258
259 // Called by GraphDataStore.Subscribe after the model has been changed
260 protected override void ModelChanged(GraphData graphData, IGraphDataAction changeAction)
261 {
262 switch (changeAction)
263 {
264 case ChangeExposedFlagAction changeExposedFlagAction:
265 // ModelChanged is called overzealously on everything
266 // but we only care if the action pertains to our Model
267 if (changeExposedFlagAction.shaderInputReference == Model)
268 {
269 ViewModel.isInputExposed = Model.generatePropertyBlock;
270 if (changeExposedFlagAction.oldIsExposedValue != changeExposedFlagAction.newIsExposedValue)
271 DirtyNodes(ModificationScope.Graph);
272 m_SgBlackboardField.UpdateFromViewModel();
273 }
274 break;
275
276 case ChangePropertyValueAction changePropertyValueAction:
277 if (changePropertyValueAction.shaderInputReference == Model)
278 {
279 DirtyNodes(ModificationScope.Graph);
280 m_SgBlackboardField.MarkDirtyRepaint();
281 }
282 break;
283
284 case ResetReferenceNameAction resetReferenceNameAction:
285 if (resetReferenceNameAction.shaderInputReference == Model)
286 {
287 DirtyNodes(ModificationScope.Graph);
288 }
289 break;
290
291 case ChangeReferenceNameAction changeReferenceNameAction:
292 if (changeReferenceNameAction.shaderInputReference == Model)
293 {
294 DirtyNodes(ModificationScope.Graph);
295 }
296 break;
297
298 case ChangeDisplayNameAction changeDisplayNameAction:
299 if (changeDisplayNameAction.shaderInputReference == Model)
300 {
301 ViewModel.inputName = Model.displayName;
302 DirtyNodes(ModificationScope.Layout);
303 m_SgBlackboardField.UpdateFromViewModel();
304 }
305 break;
306 }
307 }
308
309 // TODO: This should communicate to node controllers instead of searching for the nodes themselves everytime, but that's going to take a while...
310 internal void DirtyNodes(ModificationScope modificationScope = ModificationScope.Node)
311 {
312 foreach (var observer in Model.InputObservers)
313 {
314 observer.OnShaderInputUpdated(modificationScope);
315 }
316
317 switch (Model)
318 {
319 case AbstractShaderProperty property:
320 var graphEditorView = m_BlackboardRowView.GetFirstAncestorOfType<GraphEditorView>();
321 if (graphEditorView == null)
322 return;
323 var colorManager = graphEditorView.colorManager;
324 var nodes = graphEditorView.graphView.Query<MaterialNodeView>().ToList();
325
326 colorManager.SetNodesDirty(nodes);
327 colorManager.UpdateNodeViews(nodes);
328 break;
329 case ShaderKeyword keyword:
330 // Cant determine if Sub Graphs contain the keyword so just update them
331 foreach (var node in DataStore.State.GetNodes<SubGraphNode>())
332 {
333 node.Dirty(modificationScope);
334 }
335
336 break;
337 case ShaderDropdown dropdown:
338 // Cant determine if Sub Graphs contain the dropdown so just update them
339 foreach (var node in DataStore.State.GetNodes<SubGraphNode>())
340 {
341 node.Dirty(modificationScope);
342 }
343
344 break;
345 default:
346 throw new ArgumentOutOfRangeException();
347 }
348 }
349
350 public override void Dispose()
351 {
352 if (m_SgBlackboardField == null)
353 return;
354
355 Model?.ClearObservers();
356
357 base.Dispose();
358 Cleanup();
359
360 BlackboardItemView.Dispose();
361 m_SgBlackboardField.Dispose();
362
363 m_BlackboardRowView = null;
364 m_SgBlackboardField = null;
365 }
366 }
367}