A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEngine;
5using UnityEditor.Graphing;
6using UnityEditor.ShaderGraph.Internal;
7using UnityEditor.ShaderGraph.Serialization;
8using UnityEditor.ShaderGraph.Drawing.Slots;
9using UnityEngine.UIElements;
10
11namespace UnityEditor.ShaderGraph
12{
13 [Serializable]
14 abstract class MaterialSlot : JsonObject, IDisposable
15 {
16 const string k_NotInit = "Not Initialized";
17
18 [SerializeField]
19 int m_Id;
20
21 [SerializeField]
22 string m_DisplayName = k_NotInit;
23
24 [SerializeField]
25 SlotType m_SlotType = SlotType.Input;
26
27 [SerializeField]
28 bool m_Hidden;
29
30 [SerializeField]
31 string m_ShaderOutputName;
32
33 [SerializeField]
34 ShaderStageCapability m_StageCapability;
35
36 bool m_HasError;
37
38 protected MaterialSlot() { }
39
40 protected MaterialSlot(int slotId, string displayName, string shaderOutputName, SlotType slotType, ShaderStageCapability stageCapability = ShaderStageCapability.All, bool hidden = false)
41 {
42 m_Id = slotId;
43 m_DisplayName = displayName;
44 m_SlotType = slotType;
45 m_Hidden = hidden;
46 m_ShaderOutputName = shaderOutputName;
47 this.stageCapability = stageCapability;
48 }
49
50 internal void SetInternalData(SlotType slotType, string shaderOutputName)
51 {
52 this.m_SlotType = slotType;
53 this.shaderOutputName = shaderOutputName;
54 }
55
56 public bool IsConnectionTestable()
57 {
58 if (owner is SubGraphNode sgNode)
59 {
60 var property = sgNode.GetShaderProperty(id);
61 if (property != null)
62 {
63 return property.isConnectionTestable;
64 }
65 }
66 else if (owner is PropertyNode propertyNode)
67 {
68 return propertyNode.property.isConnectionTestable;
69 }
70 return false;
71 }
72
73 public VisualElement InstantiateCustomControl()
74 {
75 if (!isConnected && IsConnectionTestable())
76 {
77 var sgNode = owner as SubGraphNode;
78 var property = sgNode.GetShaderProperty(id);
79 return new LabelSlotControlView(property.customSlotLabel);
80 }
81 return null;
82 }
83
84 public virtual VisualElement InstantiateControl()
85 {
86 return null;
87 }
88
89 static string ConcreteSlotValueTypeAsString(ConcreteSlotValueType type)
90 {
91 switch (type)
92 {
93 case ConcreteSlotValueType.Vector1:
94 return "(1)";
95 case ConcreteSlotValueType.Vector2:
96 return "(2)";
97 case ConcreteSlotValueType.Vector3:
98 return "(3)";
99 case ConcreteSlotValueType.Vector4:
100 return "(4)";
101 case ConcreteSlotValueType.Boolean:
102 return "(B)";
103 case ConcreteSlotValueType.Matrix2:
104 return "(2x2)";
105 case ConcreteSlotValueType.Matrix3:
106 return "(3x3)";
107 case ConcreteSlotValueType.Matrix4:
108 return "(4x4)";
109 case ConcreteSlotValueType.SamplerState:
110 return "(SS)";
111 case ConcreteSlotValueType.Texture2D:
112 return "(T2)";
113 case ConcreteSlotValueType.Texture2DArray:
114 return "(T2A)";
115 case ConcreteSlotValueType.Texture3D:
116 return "(T3)";
117 case ConcreteSlotValueType.Cubemap:
118 return "(C)";
119 case ConcreteSlotValueType.Gradient:
120 return "(G)";
121 case ConcreteSlotValueType.VirtualTexture:
122 return "(VT)";
123 case ConcreteSlotValueType.PropertyConnectionState:
124 return "(P)";
125 default:
126 return "(E)";
127 }
128 }
129
130 public virtual string displayName
131 {
132 get { return m_DisplayName + ConcreteSlotValueTypeAsString(concreteValueType); }
133 set { m_DisplayName = value; }
134 }
135
136 public string RawDisplayName()
137 {
138 return m_DisplayName;
139 }
140
141 public static MaterialSlot CreateMaterialSlot(SlotValueType type, int slotId, string displayName, string shaderOutputName, SlotType slotType, Vector4 defaultValue, ShaderStageCapability shaderStageCapability = ShaderStageCapability.All, bool hidden = false)
142 {
143 switch (type)
144 {
145 case SlotValueType.SamplerState:
146 return new SamplerStateMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
147 case SlotValueType.DynamicMatrix:
148 return new DynamicMatrixMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
149 case SlotValueType.Matrix4:
150 return new Matrix4MaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
151 case SlotValueType.Matrix3:
152 return new Matrix3MaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
153 case SlotValueType.Matrix2:
154 return new Matrix2MaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
155 case SlotValueType.Texture2D:
156 return slotType == SlotType.Input
157 ? new Texture2DInputMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden)
158 : new Texture2DMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
159 case SlotValueType.Texture2DArray:
160 return slotType == SlotType.Input
161 ? new Texture2DArrayInputMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden)
162 : new Texture2DArrayMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
163 case SlotValueType.Texture3D:
164 return slotType == SlotType.Input
165 ? new Texture3DInputMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden)
166 : new Texture3DMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
167 case SlotValueType.Cubemap:
168 return slotType == SlotType.Input
169 ? new CubemapInputMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden)
170 : new CubemapMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
171 case SlotValueType.VirtualTexture:
172 return slotType == SlotType.Input
173 ? new VirtualTextureInputMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden)
174 : new VirtualTextureMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
175 case SlotValueType.Gradient:
176 return slotType == SlotType.Input
177 ? new GradientInputMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden)
178 : new GradientMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
179 case SlotValueType.DynamicVector:
180 return new DynamicVectorMaterialSlot(slotId, displayName, shaderOutputName, slotType, defaultValue, shaderStageCapability, hidden);
181 case SlotValueType.Vector4:
182 return new Vector4MaterialSlot(slotId, displayName, shaderOutputName, slotType, defaultValue, shaderStageCapability, hidden: hidden);
183 case SlotValueType.Vector3:
184 return new Vector3MaterialSlot(slotId, displayName, shaderOutputName, slotType, defaultValue, shaderStageCapability, hidden: hidden);
185 case SlotValueType.Vector2:
186 return new Vector2MaterialSlot(slotId, displayName, shaderOutputName, slotType, defaultValue, shaderStageCapability, hidden: hidden);
187 case SlotValueType.Vector1:
188 return new Vector1MaterialSlot(slotId, displayName, shaderOutputName, slotType, defaultValue.x, shaderStageCapability, hidden: hidden);
189 case SlotValueType.Dynamic:
190 return new DynamicValueMaterialSlot(slotId, displayName, shaderOutputName, slotType, new Matrix4x4(defaultValue, Vector4.zero, Vector4.zero, Vector4.zero), shaderStageCapability, hidden);
191 case SlotValueType.Boolean:
192 return new BooleanMaterialSlot(slotId, displayName, shaderOutputName, slotType, false, shaderStageCapability, hidden);
193 case SlotValueType.PropertyConnectionState:
194 return new PropertyConnectionStateMaterialSlot(slotId, displayName, shaderOutputName, slotType, shaderStageCapability, hidden);
195 }
196
197 throw new ArgumentOutOfRangeException("type", type, null);
198 }
199
200 public SlotReference slotReference
201 {
202 get { return new SlotReference(owner, m_Id); }
203 }
204
205 public AbstractMaterialNode owner { get; set; }
206
207 // if hidden, the slot does not create a port in the UI
208 public bool hidden
209 {
210 get { return m_Hidden; }
211 set { m_Hidden = value; }
212 }
213
214 public int id
215 {
216 get { return m_Id; }
217 }
218
219 public bool isInputSlot
220 {
221 get { return m_SlotType == SlotType.Input; }
222 }
223
224 public bool isOutputSlot
225 {
226 get { return m_SlotType == SlotType.Output; }
227 }
228
229 public SlotType slotType
230 {
231 get { return m_SlotType; }
232 }
233
234 public bool isConnected
235 {
236 get
237 {
238 // node and graph respectivly
239 if (owner == null || owner.owner == null)
240 return false;
241
242 var graph = owner.owner;
243 var edges = graph.GetEdges(slotReference);
244 return edges.Any();
245 }
246 }
247
248 public abstract bool isDefaultValue { get; }
249
250 public abstract SlotValueType valueType { get; }
251
252 public abstract ConcreteSlotValueType concreteValueType { get; }
253
254 public string shaderOutputName
255 {
256 get { return m_ShaderOutputName; }
257 private set { m_ShaderOutputName = value; }
258 }
259
260 public ShaderStageCapability stageCapability
261 {
262 get { return m_StageCapability; }
263 set { m_StageCapability = value; }
264 }
265
266 public bool hasError
267 {
268 get { return m_HasError; }
269 set { m_HasError = value; }
270 }
271
272 public bool IsUsingDefaultValue()
273 {
274 if (!isConnected && isDefaultValue)
275 return true;
276 else
277 return false;
278 }
279
280 public bool IsCompatibleWith(MaterialSlot otherSlot)
281 {
282 return otherSlot != null
283 && otherSlot.owner != owner
284 && otherSlot.isInputSlot != isInputSlot
285 && !hidden
286 && !otherSlot.hidden
287 && ((isInputSlot
288 ? SlotValueHelper.AreCompatible(valueType, otherSlot.concreteValueType, otherSlot.IsConnectionTestable())
289 : SlotValueHelper.AreCompatible(otherSlot.valueType, concreteValueType, IsConnectionTestable())));
290 }
291
292 public bool IsCompatibleStageWith(MaterialSlot otherSlot)
293 {
294 var startStage = otherSlot.stageCapability;
295 if (startStage == ShaderStageCapability.All || otherSlot.owner is SubGraphNode)
296 startStage = NodeUtils.GetEffectiveShaderStageCapability(otherSlot, true)
297 & NodeUtils.GetEffectiveShaderStageCapability(otherSlot, false);
298 return IsCompatibleStageWith(startStage);
299 }
300
301 internal bool IsCompatibleStageWith(ShaderStageCapability capability)
302 {
303 return capability == ShaderStageCapability.All || stageCapability == ShaderStageCapability.All || stageCapability == capability;
304 }
305
306 public string GetDefaultValue(GenerationMode generationMode, ConcretePrecision concretePrecision)
307 {
308 string defaultValue = GetDefaultValue(generationMode);
309 return defaultValue.Replace(PrecisionUtil.Token, concretePrecision.ToShaderString());
310 }
311
312 public virtual string GetDefaultValue(GenerationMode generationMode)
313 {
314 var matOwner = owner as AbstractMaterialNode;
315 if (matOwner == null)
316 throw new Exception(string.Format("Slot {0} either has no owner, or the owner is not a {1}", this, typeof(AbstractMaterialNode)));
317
318 if (generationMode.IsPreview() && matOwner.isActive)
319 return matOwner.GetVariableNameForSlot(id);
320
321 return ConcreteSlotValueAsVariable();
322 }
323
324 protected virtual string ConcreteSlotValueAsVariable()
325 {
326 return "error";
327 }
328
329 public abstract void AddDefaultProperty(PropertyCollector properties, GenerationMode generationMode);
330
331 public virtual void GetPreviewProperties(List<PreviewProperty> properties, string name)
332 {
333 properties.Add(default(PreviewProperty));
334 }
335
336 public virtual void AppendHLSLParameterDeclaration(ShaderStringBuilder sb, string paramName)
337 {
338 sb.Append(concreteValueType.ToShaderString());
339 sb.Append(" ");
340 sb.Append(paramName);
341 }
342
343 public abstract void CopyValuesFrom(MaterialSlot foundSlot);
344
345 public bool Equals(MaterialSlot other)
346 {
347 return m_Id == other.m_Id && owner == other.owner;
348 }
349
350 public override bool Equals(object obj)
351 {
352 if (ReferenceEquals(null, obj)) return false;
353 if (ReferenceEquals(this, obj)) return true;
354 if (obj.GetType() != this.GetType()) return false;
355 return Equals((MaterialSlot)obj);
356 }
357
358 public override int GetHashCode()
359 {
360 unchecked
361 {
362 return (m_Id * 397) ^ (owner != null ? owner.GetHashCode() : 0);
363 }
364 }
365
366 public void Dispose()
367 {
368 owner = null;
369 }
370
371 // this tracks old CustomFunctionNode slots that are expecting the old bare resource inputs
372 // rather than the new structure-based inputs
373 internal virtual bool bareResource { get { return false; } set { } }
374
375 public virtual void CopyDefaultValue(MaterialSlot other)
376 {
377 m_Id = other.m_Id;
378 m_DisplayName = other.m_DisplayName;
379 m_SlotType = other.m_SlotType;
380 m_Hidden = other.m_Hidden;
381 m_ShaderOutputName = other.m_ShaderOutputName;
382 m_StageCapability = other.m_StageCapability;
383 }
384 }
385}