A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityEditor.Graphing;
4using UnityEditor.ShaderGraph.Drawing;
5using UnityEditor.ShaderGraph.Serialization;
6using UnityEngine;
7
8namespace UnityEditor.ShaderGraph.Internal
9{
10 public abstract class ShaderInput : JsonObject
11 {
12 [SerializeField]
13 SerializableGuid m_Guid = new SerializableGuid();
14
15 internal Guid guid => m_Guid.guid;
16
17 internal void OverrideGuid(string namespaceId, string name) { m_Guid.guid = GenerateNamespaceUUID(namespaceId, name); }
18
19 [SerializeField]
20 string m_Name;
21
22 public string displayName
23 {
24 get
25 {
26 if (string.IsNullOrEmpty(m_Name))
27 return $"{concreteShaderValueType}_{objectId}";
28 return m_Name;
29 }
30 set
31 {
32 // this is a raw set of the display name
33 // if you want to a fully graph-connected set-and-sanitize-and-update,
34 // call SetDisplayNameAndSanitizeForGraph() instead
35 m_Name = value;
36 }
37 }
38
39 internal void AddObserver(IShaderInputObserver observer)
40 {
41 m_InputObservers.Add(observer);
42 }
43
44 internal void RemoveObserver(IShaderInputObserver observer)
45 {
46 m_InputObservers.Remove(observer);
47 }
48
49 internal void ClearObservers()
50 {
51 m_InputObservers.Clear();
52 }
53
54 internal HashSet<IShaderInputObserver> InputObservers => m_InputObservers;
55
56 HashSet<IShaderInputObserver> m_InputObservers = new();
57
58 // sanitizes the desired name according to the current graph, and assigns it as the display name
59 // also calls the update trigger to update other bits of the graph UI that use the name
60 internal void SetDisplayNameAndSanitizeForGraph(GraphData graphData, string desiredName = null)
61 {
62 string originalDisplayName = displayName;
63
64 // if no desired name passed in, sanitize the current name
65 if (desiredName == null)
66 desiredName = originalDisplayName;
67
68 var sanitizedName = graphData.SanitizeGraphInputName(this, desiredName);
69 bool changed = (originalDisplayName != sanitizedName);
70
71 // only assign if it was changed
72 if (changed)
73 m_Name = sanitizedName;
74
75 // update the default reference name
76 UpdateDefaultReferenceName(graphData);
77 }
78
79 internal void SetReferenceNameAndSanitizeForGraph(GraphData graphData, string desiredRefName = null)
80 {
81 string originalRefName = referenceName;
82
83 // if no desired ref name, use the current name
84 if (string.IsNullOrEmpty(desiredRefName))
85 desiredRefName = originalRefName;
86
87 // sanitize and deduplicate the desired name
88 var sanitizedRefName = graphData.SanitizeGraphInputReferenceName(this, desiredRefName);
89
90 // check if the final result is different from the current name
91 bool changed = (originalRefName != sanitizedRefName);
92
93 // if changed, then set the new name up as an override
94 if (changed)
95 overrideReferenceName = sanitizedRefName;
96 }
97
98 // resets the reference name to a "default" value (deduplicated against existing reference names)
99 // returns the new default reference name
100 internal string ResetReferenceName(GraphData graphData)
101 {
102 overrideReferenceName = null;
103
104 // because we are clearing an override, we must force a sanitization pass on the default ref name
105 // as there may now be collisions that didn't previously exist
106 UpdateDefaultReferenceName(graphData, true);
107
108 return referenceName;
109 }
110
111 internal void UpdateDefaultReferenceName(GraphData graphData, bool forceSanitize = false)
112 {
113 if (m_DefaultRefNameVersion <= 0)
114 return; // old version is updated in the getter
115
116 if (forceSanitize ||
117 string.IsNullOrEmpty(m_DefaultReferenceName) ||
118 (m_RefNameGeneratedByDisplayName != displayName))
119 {
120 // Make sure all reference names are consistently auto-generated with a pre-pended underscore (if they can be renamed)
121 var targetRefName = displayName;
122 if (this.isReferenceRenamable && !targetRefName.StartsWith("_"))
123 targetRefName = "_" + targetRefName;
124
125 m_DefaultReferenceName = graphData.SanitizeGraphInputReferenceName(this, targetRefName);
126 m_RefNameGeneratedByDisplayName = displayName;
127 }
128 }
129
130 const int k_LatestDefaultRefNameVersion = 1;
131
132 // this is used to know whether this shader input is using:
133 // 0) the "old" default reference naming scheme (type + GUID)
134 // 1) the new default reference naming scheme (make it similar to the display name)
135 [SerializeField]
136 int m_DefaultRefNameVersion = k_LatestDefaultRefNameVersion;
137
138 [SerializeField]
139 string m_RefNameGeneratedByDisplayName; // used to tell what was the display name used to generate the default reference name
140
141 [SerializeField]
142 string m_DefaultReferenceName; // NOTE: this can be NULL for old graphs, or newly created properties
143
144 public string referenceName
145 {
146 get
147 {
148 if (string.IsNullOrEmpty(overrideReferenceName))
149 {
150 if (m_DefaultRefNameVersion == 0)
151 {
152 if (string.IsNullOrEmpty(m_DefaultReferenceName))
153 m_DefaultReferenceName = GetOldDefaultReferenceName();
154 return m_DefaultReferenceName;
155 }
156 else // version 1
157 {
158 // default reference name is updated elsewhere in the new naming scheme
159 return m_DefaultReferenceName;
160 }
161 }
162 return overrideReferenceName;
163 }
164 }
165
166 public virtual string referenceNameForEditing => referenceName;
167
168 public override void OnBeforeDeserialize()
169 {
170 // if serialization doesn't write to m_DefaultRefNameVersion, then it is an old shader input, and should use the old default naming scheme
171 m_DefaultRefNameVersion = 0;
172 base.OnBeforeDeserialize();
173 }
174
175 // This is required to handle Material data serialized with "_Color_GUID" reference names
176 // m_DefaultReferenceName expects to match the material data and previously used PropertyType
177 // ColorShaderProperty is the only case where PropertyType doesn't match ConcreteSlotValueType
178 public virtual string GetOldDefaultReferenceName()
179 {
180 return $"{concreteShaderValueType.ToString()}_{objectId}";
181 }
182
183 // returns true if this shader input is CURRENTLY using the old default reference name
184 public bool IsUsingOldDefaultRefName()
185 {
186 return string.IsNullOrEmpty(overrideReferenceName) && (m_DefaultRefNameVersion == 0);
187 }
188
189 // returns true if this shader input is CURRENTLY using the new default reference name
190 public bool IsUsingNewDefaultRefName()
191 {
192 return string.IsNullOrEmpty(overrideReferenceName) && (m_DefaultRefNameVersion >= 1);
193 }
194
195 // upgrades the default reference name to use the new naming scheme
196 internal string UpgradeDefaultReferenceName(GraphData graphData)
197 {
198 m_DefaultRefNameVersion = k_LatestDefaultRefNameVersion;
199 m_DefaultReferenceName = null;
200 m_RefNameGeneratedByDisplayName = null;
201 UpdateDefaultReferenceName(graphData, true); // make sure to sanitize the new default
202 return referenceName;
203 }
204
205 [SerializeField]
206 string m_OverrideReferenceName;
207
208 internal string overrideReferenceName
209 {
210 get => m_OverrideReferenceName;
211 set => m_OverrideReferenceName = value;
212 }
213
214 [SerializeField]
215 bool m_GeneratePropertyBlock = true;
216
217 internal bool generatePropertyBlock // this is basically the "exposed" toggle
218 {
219 get => m_GeneratePropertyBlock;
220 set => m_GeneratePropertyBlock = value;
221 }
222
223 internal virtual bool isExposed => isExposable && generatePropertyBlock;
224
225 public virtual bool allowedInSubGraph
226 {
227 get { return true; }
228 }
229
230 public virtual bool allowedInMainGraph
231 {
232 get { return true; }
233 }
234
235 internal abstract ConcreteSlotValueType concreteShaderValueType { get; }
236
237 internal abstract bool isExposable { get; }
238 internal virtual bool isAlwaysExposed => false;
239
240 // this controls whether the UI allows the user to rename the display and reference names
241 internal abstract bool isRenamable { get; }
242 internal virtual bool isReferenceRenamable => isRenamable;
243
244 internal virtual bool isCustomSlotAllowed => true;
245
246 [SerializeField]
247 bool m_UseCustomSlotLabel = false;
248
249 [SerializeField]
250 string m_CustomSlotLabel;
251
252 internal bool useCustomSlotLabel
253 {
254 get => m_UseCustomSlotLabel;
255 set => m_UseCustomSlotLabel = value;
256 }
257
258 internal string customSlotLabel
259 {
260 get => m_CustomSlotLabel;
261 set => m_CustomSlotLabel = value;
262 }
263
264 [SerializeField]
265 protected int m_DismissedVersion = 0;
266 public int dismissedUpdateVersion { get => m_DismissedVersion; set => m_DismissedVersion = value; }
267
268 internal bool isConnectionTestable
269 {
270 get => m_UseCustomSlotLabel;
271 }
272
273 static internal string GetConnectionStateVariableName(string variableName)
274 {
275 return variableName + "_IsConnected";
276 }
277
278 internal abstract ShaderInput Copy();
279
280 internal virtual void OnBeforePasteIntoGraph(GraphData graph) { }
281 }
282}