A game about forced loneliness, made by TACStudios
1using System;
2using System.Linq;
3using UnityEngine;
4using UnityEditor.Graphing;
5using UnityEditor.ShaderGraph.Internal;
6using System.Collections.Generic;
7using System.Text.RegularExpressions;
8using UnityEngine.Rendering.ShaderGraph;
9
10namespace UnityEditor.ShaderGraph
11{
12 class BlockNode : AbstractMaterialNode
13 , IMayRequireNormal
14 , IMayRequireTangent
15 , IMayRequireBitangent
16 , IMayRequireMeshUV
17 , IMayRequireScreenPosition
18 , IMayRequireNDCPosition
19 , IMayRequirePixelPosition
20 , IMayRequireViewDirection
21 , IMayRequirePosition
22 , IMayRequirePositionPredisplacement
23 , IMayRequireVertexColor
24 {
25 [SerializeField]
26 string m_SerializedDescriptor;
27
28 [NonSerialized]
29 ContextData m_ContextData;
30
31 [NonSerialized]
32 BlockFieldDescriptor m_Descriptor;
33
34 public override string displayName
35 {
36 get
37 {
38 string displayName = "";
39 if (m_Descriptor != null)
40 {
41 displayName = m_Descriptor.shaderStage.ToString();
42 if (!string.IsNullOrEmpty(displayName))
43 displayName += " ";
44 displayName += m_Descriptor.displayName;
45 }
46
47 return displayName;
48 }
49 }
50
51 public override bool canCutNode => false;
52 public override bool canCopyNode => false;
53
54 public override string documentationURL => Documentation.GetPageLink("Block-Node");
55
56 // Because the GraphData is deserialized after its child elements
57 // the descriptor list is not built (and owner is not set)
58 // at the time of node deserialization
59 // Therefore we need to deserialize this element at GraphData.OnAfterDeserialize
60 public string serializedDescriptor => m_SerializedDescriptor;
61
62 public ContextData contextData
63 {
64 get => m_ContextData;
65 set => m_ContextData = value;
66 }
67
68 public int index => contextData.blocks.IndexOf(this);
69
70 public BlockFieldDescriptor descriptor
71 {
72 get => m_Descriptor;
73 set => m_Descriptor = value;
74 }
75
76 const string k_CustomBlockDefaultName = "CustomInterpolator";
77
78 internal enum CustomBlockType { Float = 1, Vector2 = 2, Vector3 = 3, Vector4 = 4 }
79
80 internal bool isCustomBlock { get => m_Descriptor?.isCustom ?? false; }
81
82 internal string customName
83 {
84 get => m_Descriptor.name;
85 set => OnCustomBlockFieldModified(value, customWidth);
86 }
87
88 internal CustomBlockType customWidth
89 {
90 get => (CustomBlockType)ControlToWidth(m_Descriptor.control);
91 set => OnCustomBlockFieldModified(customName, value);
92 }
93
94 public void Init(BlockFieldDescriptor fieldDescriptor)
95 {
96 m_Descriptor = fieldDescriptor;
97
98 // custom blocks can be "copied" via a custom Field Descriptor, we'll use the CI name instead though.
99 name = !isCustomBlock
100 ? $"{fieldDescriptor.tag}.{fieldDescriptor.name}"
101 : $"{BlockFields.VertexDescription.name}.{k_CustomBlockDefaultName}";
102
103 // TODO: This exposes the MaterialSlot API
104 // TODO: This needs to be removed but is currently required by HDRP for DiffusionProfileInputMaterialSlot
105 if (m_Descriptor is CustomSlotBlockFieldDescriptor customSlotDescriptor)
106 {
107 var newSlot = customSlotDescriptor.createSlot();
108 AddSlot(newSlot);
109 RemoveSlotsNameNotMatching(new int[] { 0 });
110 return;
111 }
112
113 AddSlotFromControlType();
114 }
115
116 internal void InitCustomDefault()
117 {
118 Init(MakeCustomBlockField(k_CustomBlockDefaultName, CustomBlockType.Vector4));
119 }
120
121 private void AddSlotFromControlType(bool attemptToModifyExisting = true)
122 {
123 // TODO: this should really just use callbacks like the CustomSlotBlockFieldDescriptor. then we wouldn't need this switch to make a copy
124 var stageCapability = m_Descriptor.shaderStage.GetShaderStageCapability();
125 switch (descriptor.control)
126 {
127 case PositionControl positionControl:
128 AddSlot(new PositionMaterialSlot(0, descriptor.displayName, descriptor.name, positionControl.space, stageCapability), attemptToModifyExisting);
129 break;
130 case NormalControl normalControl:
131 AddSlot(new NormalMaterialSlot(0, descriptor.displayName, descriptor.name, normalControl.space, stageCapability), attemptToModifyExisting);
132 break;
133 case TangentControl tangentControl:
134 AddSlot(new TangentMaterialSlot(0, descriptor.displayName, descriptor.name, tangentControl.space, stageCapability), attemptToModifyExisting);
135 break;
136 case VertexColorControl vertexColorControl:
137 AddSlot(new VertexColorMaterialSlot(0, descriptor.displayName, descriptor.name, stageCapability), attemptToModifyExisting);
138 break;
139 case ColorControl colorControl:
140 var colorMode = colorControl.hdr ? ColorMode.HDR : ColorMode.Default;
141 AddSlot(new ColorRGBMaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, colorControl.value, colorMode, stageCapability), attemptToModifyExisting);
142 break;
143 case ColorRGBAControl colorRGBAControl:
144 AddSlot(new ColorRGBAMaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, colorRGBAControl.value, stageCapability), attemptToModifyExisting);
145 break;
146 case FloatControl floatControl:
147 AddSlot(new Vector1MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, floatControl.value, stageCapability), attemptToModifyExisting);
148 break;
149 case Vector2Control vector2Control:
150 AddSlot(new Vector2MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, vector2Control.value, stageCapability), attemptToModifyExisting);
151 break;
152 case Vector3Control vector3Control:
153 AddSlot(new Vector3MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, vector3Control.value, stageCapability), attemptToModifyExisting);
154 break;
155 case Vector4Control vector4Control:
156 AddSlot(new Vector4MaterialSlot(0, descriptor.displayName, descriptor.name, SlotType.Input, vector4Control.value, stageCapability), attemptToModifyExisting);
157 break;
158 }
159 RemoveSlotsNameNotMatching(new int[] { 0 });
160 }
161
162 public override string GetVariableNameForNode()
163 {
164 // Temporary block nodes have temporary guids that cannot be used to set preview data
165 // Since each block is unique anyway we just omit the guid
166 return NodeUtils.GetHLSLSafeName(name);
167 }
168
169 public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
170 {
171 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
172 return NeededCoordinateSpace.None;
173
174 if (m_Descriptor.control == null)
175 return NeededCoordinateSpace.None;
176
177 var requirements = m_Descriptor.control.GetRequirements();
178 return requirements.requiresNormal;
179 }
180
181 public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability)
182 {
183 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
184 return NeededCoordinateSpace.None;
185
186 if (m_Descriptor.control == null)
187 return NeededCoordinateSpace.None;
188
189 var requirements = m_Descriptor.control.GetRequirements();
190 return requirements.requiresViewDir;
191 }
192
193 public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
194 {
195 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
196 return NeededCoordinateSpace.None;
197
198 if (m_Descriptor.control == null)
199 return NeededCoordinateSpace.None;
200
201 var requirements = m_Descriptor.control.GetRequirements();
202 return requirements.requiresPosition;
203 }
204
205 public NeededCoordinateSpace RequiresPositionPredisplacement(ShaderStageCapability stageCapability)
206 {
207 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
208 return NeededCoordinateSpace.None;
209
210 if (m_Descriptor.control == null)
211 return NeededCoordinateSpace.None;
212
213 var requirements = m_Descriptor.control.GetRequirements();
214 return requirements.requiresPositionPredisplacement;
215 }
216
217 public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
218 {
219 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
220 return NeededCoordinateSpace.None;
221
222 if (m_Descriptor.control == null)
223 return NeededCoordinateSpace.None;
224
225 var requirements = m_Descriptor.control.GetRequirements();
226 return requirements.requiresTangent;
227 }
228
229 public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
230 {
231 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
232 return NeededCoordinateSpace.None;
233
234 if (m_Descriptor.control == null)
235 return NeededCoordinateSpace.None;
236
237 var requirements = m_Descriptor.control.GetRequirements();
238 return requirements.requiresBitangent;
239 }
240
241 public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
242 {
243 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
244 return false;
245
246 if (m_Descriptor.control == null)
247 return false;
248
249 var requirements = m_Descriptor.control.GetRequirements();
250 return requirements.requiresMeshUVs.Contains(channel);
251 }
252
253 public bool RequiresScreenPosition(ShaderStageCapability stageCapability)
254 {
255 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
256 return false;
257
258 if (m_Descriptor.control == null)
259 return false;
260
261 var requirements = m_Descriptor.control.GetRequirements();
262 return requirements.requiresScreenPosition;
263 }
264
265 public bool RequiresNDCPosition(ShaderStageCapability stageCapability)
266 {
267 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
268 return false;
269
270 if (m_Descriptor.control == null)
271 return false;
272
273 var requirements = m_Descriptor.control.GetRequirements();
274 return requirements.requiresNDCPosition;
275 }
276
277 public bool RequiresPixelPosition(ShaderStageCapability stageCapability)
278 {
279 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
280 return false;
281
282 if (m_Descriptor.control == null)
283 return false;
284
285 var requirements = m_Descriptor.control.GetRequirements();
286 return requirements.requiresPixelPosition;
287 }
288
289 public bool RequiresVertexColor(ShaderStageCapability stageCapability)
290 {
291 if (stageCapability != m_Descriptor.shaderStage.GetShaderStageCapability())
292 return false;
293
294 if (m_Descriptor.control == null)
295 return false;
296
297 var requirements = m_Descriptor.control.GetRequirements();
298 return requirements.requiresVertexColor;
299 }
300
301 private void OnCustomBlockFieldModified(string name, CustomBlockType width)
302 {
303 if (!isCustomBlock)
304 {
305 Debug.LogWarning(String.Format("{0} is not a custom interpolator.", this.name));
306 return;
307 }
308
309 m_Descriptor = MakeCustomBlockField(name, width);
310
311 // TODO: Preserve the original slot's value and try to reapply after the slot is updated.
312 AddSlotFromControlType(false);
313
314 owner?.ValidateGraph();
315 }
316
317 public override void OnBeforeSerialize()
318 {
319 base.OnBeforeSerialize();
320 if (descriptor != null)
321 {
322 if (isCustomBlock)
323 {
324 int width = ControlToWidth(m_Descriptor.control);
325 m_SerializedDescriptor = $"{m_Descriptor.tag}.{m_Descriptor.name}#{width}";
326 }
327 else
328 {
329 m_SerializedDescriptor = $"{m_Descriptor.tag}.{m_Descriptor.name}";
330 }
331 }
332 }
333
334 public override void OnAfterDeserialize()
335 {
336 // TODO: Go find someone to tell @esme not to do this.
337 if (m_SerializedDescriptor.Contains("#"))
338 {
339 string descName = k_CustomBlockDefaultName;
340 CustomBlockType descWidth = CustomBlockType.Vector4;
341 var descTag = BlockFields.VertexDescription.name;
342
343 name = $"{descTag}.{descName}";
344
345 var wsplit = m_SerializedDescriptor.Split(new char[] { '#', '.' });
346
347 try
348 {
349 descWidth = (CustomBlockType)int.Parse(wsplit[2]);
350 }
351 catch
352 {
353 Debug.LogWarning(String.Format("Bad width found while deserializing custom interpolator {0}, defaulting to 4.", m_SerializedDescriptor));
354 descWidth = CustomBlockType.Vector4;
355 }
356
357 IControl control;
358 try { control = (IControl)FindSlot<MaterialSlot>(0).InstantiateControl(); }
359 catch { control = WidthToControl((int)descWidth); }
360
361 descName = NodeUtils.ConvertToValidHLSLIdentifier(wsplit[1]);
362 m_Descriptor = new BlockFieldDescriptor(descTag, descName, "", control, ShaderStage.Vertex, isCustom: true);
363 }
364 }
365
366 #region CustomInterpolatorHelpers
367 private static BlockFieldDescriptor MakeCustomBlockField(string name, CustomBlockType width)
368 {
369 name = NodeUtils.ConvertToValidHLSLIdentifier(name);
370 var referenceName = name;
371 var define = "";
372 IControl control = WidthToControl((int)width);
373 var tag = BlockFields.VertexDescription.name;
374
375 return new BlockFieldDescriptor(tag, referenceName, define, control, ShaderStage.Vertex, isCustom: true);
376 }
377
378 private static IControl WidthToControl(int width)
379 {
380 switch (width)
381 {
382 case 1: return new FloatControl(default(float));
383 case 2: return new Vector2Control(default(Vector2));
384 case 3: return new Vector3Control(default(Vector3));
385 case 4: return new Vector4Control(default(Vector4));
386 default: return null;
387 }
388 }
389
390 private static int ControlToWidth(IControl control)
391 {
392 switch (control)
393 {
394 case FloatControl a: return 1;
395 case Vector2Control b: return 2;
396 case Vector3Control c: return 3;
397 case Vector4Control d: return 4;
398 default: return -1;
399 }
400 }
401
402 #endregion
403 }
404}