A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Reflection;
5using JetBrains.Annotations;
6using UnityEngine;
7using UnityEditor.Graphing;
8using UnityEditor.ShaderGraph.Internal;
9
10namespace UnityEditor.ShaderGraph
11{
12 abstract class CodeFunctionNode : AbstractMaterialNode
13 , IGeneratesBodyCode
14 , IGeneratesFunction
15 , IMayRequireNormal
16 , IMayRequireTangent
17 , IMayRequireBitangent
18 , IMayRequireMeshUV
19 , IMayRequireScreenPosition
20 , IMayRequireNDCPosition
21 , IMayRequirePixelPosition
22 , IMayRequireViewDirection
23 , IMayRequirePosition
24 , IMayRequirePositionPredisplacement
25 , IMayRequireVertexColor
26 {
27 [NonSerialized]
28 private List<SlotAttribute> m_Slots = new List<SlotAttribute>();
29
30 public override bool hasPreview
31 {
32 get { return true; }
33 }
34
35 protected CodeFunctionNode()
36 {
37 UpdateNodeAfterDeserialization();
38 }
39
40 protected struct Boolean
41 { }
42
43 protected struct Vector1
44 { }
45
46 protected struct Texture2D
47 { }
48
49 protected struct Texture2DArray
50 { }
51
52 protected struct Texture3D
53 { }
54
55 protected struct SamplerState
56 { }
57
58 protected struct Gradient
59 { }
60
61 protected struct DynamicDimensionVector
62 { }
63
64 protected struct ColorRGBA
65 { }
66
67 protected struct ColorRGB
68 { }
69
70 protected struct Matrix3x3
71 { }
72
73 protected struct Matrix2x2
74 { }
75
76 protected struct DynamicDimensionMatrix
77 { }
78
79 protected struct PropertyConnectionState
80 { }
81
82 protected enum Binding
83 {
84 None,
85 ObjectSpaceNormal,
86 ObjectSpaceTangent,
87 ObjectSpaceBitangent,
88 ObjectSpacePosition,
89 ViewSpaceNormal,
90 ViewSpaceTangent,
91 ViewSpaceBitangent,
92 ViewSpacePosition,
93 WorldSpaceNormal,
94 WorldSpaceTangent,
95 WorldSpaceBitangent,
96 WorldSpacePosition,
97 TangentSpaceNormal,
98 TangentSpaceTangent,
99 TangentSpaceBitangent,
100 TangentSpacePosition,
101 MeshUV0,
102 MeshUV1,
103 MeshUV2,
104 MeshUV3,
105 ScreenPosition,
106 ObjectSpaceViewDirection,
107 ViewSpaceViewDirection,
108 WorldSpaceViewDirection,
109 TangentSpaceViewDirection,
110 VertexColor,
111 }
112
113 [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
114 protected class SlotAttribute : Attribute
115 {
116 public int slotId { get; private set; }
117 public Binding binding { get; private set; }
118 public bool hidden { get; private set; }
119 public Vector4? defaultValue { get; private set; }
120 public ShaderStageCapability stageCapability { get; private set; }
121
122 public SlotAttribute(int mSlotId, Binding mImplicitBinding, ShaderStageCapability mStageCapability = ShaderStageCapability.All)
123 {
124 slotId = mSlotId;
125 binding = mImplicitBinding;
126 defaultValue = null;
127 stageCapability = mStageCapability;
128 }
129
130 public SlotAttribute(int mSlotId, Binding mImplicitBinding, bool mHidden, ShaderStageCapability mStageCapability = ShaderStageCapability.All)
131 {
132 slotId = mSlotId;
133 binding = mImplicitBinding;
134 hidden = mHidden;
135 defaultValue = null;
136 stageCapability = mStageCapability;
137 }
138
139 public SlotAttribute(int mSlotId, Binding mImplicitBinding, float defaultX, float defaultY, float defaultZ, float defaultW, ShaderStageCapability mStageCapability = ShaderStageCapability.All)
140 {
141 slotId = mSlotId;
142 binding = mImplicitBinding;
143 defaultValue = new Vector4(defaultX, defaultY, defaultZ, defaultW);
144 stageCapability = mStageCapability;
145 }
146 }
147
148 protected abstract MethodInfo GetFunctionToConvert();
149
150 private static SlotValueType ConvertTypeToSlotValueType(ParameterInfo p)
151 {
152 Type t = p.ParameterType;
153 if (p.ParameterType.IsByRef)
154 t = p.ParameterType.GetElementType();
155
156 if (t == typeof(Boolean))
157 {
158 return SlotValueType.Boolean;
159 }
160 if (t == typeof(Vector1))
161 {
162 return SlotValueType.Vector1;
163 }
164 if (t == typeof(Vector2))
165 {
166 return SlotValueType.Vector2;
167 }
168 if (t == typeof(Vector3))
169 {
170 return SlotValueType.Vector3;
171 }
172 if (t == typeof(Vector4))
173 {
174 return SlotValueType.Vector4;
175 }
176 if (t == typeof(Color))
177 {
178 return SlotValueType.Vector4;
179 }
180 if (t == typeof(ColorRGBA))
181 {
182 return SlotValueType.Vector4;
183 }
184 if (t == typeof(ColorRGB))
185 {
186 return SlotValueType.Vector3;
187 }
188 if (t == typeof(Texture2D))
189 {
190 return SlotValueType.Texture2D;
191 }
192 if (t == typeof(Texture2DArray))
193 {
194 return SlotValueType.Texture2DArray;
195 }
196 if (t == typeof(Texture3D))
197 {
198 return SlotValueType.Texture3D;
199 }
200 if (t == typeof(Cubemap))
201 {
202 return SlotValueType.Cubemap;
203 }
204 if (t == typeof(Gradient))
205 {
206 return SlotValueType.Gradient;
207 }
208 if (t == typeof(SamplerState))
209 {
210 return SlotValueType.SamplerState;
211 }
212 if (t == typeof(DynamicDimensionVector))
213 {
214 return SlotValueType.DynamicVector;
215 }
216 if (t == typeof(Matrix4x4))
217 {
218 return SlotValueType.Matrix4;
219 }
220 if (t == typeof(Matrix3x3))
221 {
222 return SlotValueType.Matrix3;
223 }
224 if (t == typeof(Matrix2x2))
225 {
226 return SlotValueType.Matrix2;
227 }
228 if (t == typeof(DynamicDimensionMatrix))
229 {
230 return SlotValueType.DynamicMatrix;
231 }
232 if (t == typeof(PropertyConnectionState))
233 {
234 return SlotValueType.PropertyConnectionState;
235 }
236
237 throw new ArgumentException("Unsupported type " + t);
238 }
239
240 public sealed override void UpdateNodeAfterDeserialization()
241 {
242 var method = GetFunctionToConvert();
243
244 if (method == null)
245 throw new ArgumentException("Mapped method is null on node" + this);
246
247 if (method.ReturnType != typeof(string))
248 throw new ArgumentException("Mapped function should return string");
249
250 // validate no duplicates
251 var slotAtributes = method.GetParameters().Select(GetSlotAttribute).ToList();
252 if (slotAtributes.Any(x => x == null))
253 throw new ArgumentException("Missing SlotAttribute on " + method.Name);
254
255 if (slotAtributes.GroupBy(x => x.slotId).Any(x => x.Count() > 1))
256 throw new ArgumentException("Duplicate SlotAttribute on " + method.Name);
257
258 List<MaterialSlot> slots = new List<MaterialSlot>();
259 foreach (var par in method.GetParameters())
260 {
261 var attribute = GetSlotAttribute(par);
262 var name = GraphUtil.ConvertCamelCase(par.Name, true);
263
264 MaterialSlot s;
265 if (attribute.binding == Binding.None && !par.IsOut && par.ParameterType == typeof(Color))
266 s = new ColorRGBAMaterialSlot(attribute.slotId, name, par.Name, SlotType.Input, attribute.defaultValue ?? Vector4.zero, stageCapability: attribute.stageCapability, hidden: attribute.hidden);
267 else if (attribute.binding == Binding.None && !par.IsOut && par.ParameterType == typeof(ColorRGBA))
268 s = new ColorRGBAMaterialSlot(attribute.slotId, name, par.Name, SlotType.Input, attribute.defaultValue ?? Vector4.zero, stageCapability: attribute.stageCapability, hidden: attribute.hidden);
269 else if (attribute.binding == Binding.None && !par.IsOut && par.ParameterType == typeof(ColorRGB))
270 s = new ColorRGBMaterialSlot(attribute.slotId, name, par.Name, SlotType.Input, attribute.defaultValue ?? Vector4.zero, ColorMode.Default, stageCapability: attribute.stageCapability, hidden: attribute.hidden);
271 else if (attribute.binding == Binding.None || par.IsOut)
272 s = MaterialSlot.CreateMaterialSlot(
273 ConvertTypeToSlotValueType(par),
274 attribute.slotId,
275 name,
276 par.Name,
277 par.IsOut ? SlotType.Output : SlotType.Input,
278 attribute.defaultValue ?? Vector4.zero,
279 shaderStageCapability: attribute.stageCapability,
280 hidden: attribute.hidden);
281 else
282 s = CreateBoundSlot(attribute.binding, attribute.slotId, name, par.Name, attribute.stageCapability, attribute.hidden);
283 slots.Add(s);
284
285 m_Slots.Add(attribute);
286 }
287 foreach (var slot in slots)
288 {
289 AddSlot(slot);
290 }
291 RemoveSlotsNameNotMatching(slots.Select(x => x.id), true);
292 }
293
294 private static MaterialSlot CreateBoundSlot(Binding attributeBinding, int slotId, string displayName, string shaderOutputName, ShaderStageCapability shaderStageCapability, bool hidden = false)
295 {
296 switch (attributeBinding)
297 {
298 case Binding.ObjectSpaceNormal:
299 return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object, shaderStageCapability, hidden);
300 case Binding.ObjectSpaceTangent:
301 return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object, shaderStageCapability, hidden);
302 case Binding.ObjectSpaceBitangent:
303 return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object, shaderStageCapability, hidden);
304 case Binding.ObjectSpacePosition:
305 return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object, shaderStageCapability, hidden);
306 case Binding.ViewSpaceNormal:
307 return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View, shaderStageCapability, hidden);
308 case Binding.ViewSpaceTangent:
309 return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View, shaderStageCapability, hidden);
310 case Binding.ViewSpaceBitangent:
311 return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View, shaderStageCapability, hidden);
312 case Binding.ViewSpacePosition:
313 return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View, shaderStageCapability, hidden);
314 case Binding.WorldSpaceNormal:
315 return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World, shaderStageCapability, hidden);
316 case Binding.WorldSpaceTangent:
317 return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World, shaderStageCapability, hidden);
318 case Binding.WorldSpaceBitangent:
319 return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World, shaderStageCapability, hidden);
320 case Binding.WorldSpacePosition:
321 return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World, shaderStageCapability, hidden);
322 case Binding.TangentSpaceNormal:
323 return new NormalMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent, shaderStageCapability, hidden);
324 case Binding.TangentSpaceTangent:
325 return new TangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent, shaderStageCapability, hidden);
326 case Binding.TangentSpaceBitangent:
327 return new BitangentMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent, shaderStageCapability, hidden);
328 case Binding.TangentSpacePosition:
329 return new PositionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent, shaderStageCapability, hidden);
330 case Binding.MeshUV0:
331 return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.UV0, shaderStageCapability, hidden);
332 case Binding.MeshUV1:
333 return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.UV1, shaderStageCapability, hidden);
334 case Binding.MeshUV2:
335 return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.UV2, shaderStageCapability, hidden);
336 case Binding.MeshUV3:
337 return new UVMaterialSlot(slotId, displayName, shaderOutputName, UVChannel.UV3, shaderStageCapability, hidden);
338 case Binding.ScreenPosition:
339 return new ScreenPositionMaterialSlot(slotId, displayName, shaderOutputName, ScreenSpaceType.Default, shaderStageCapability, hidden);
340 case Binding.ObjectSpaceViewDirection:
341 return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Object, shaderStageCapability, hidden);
342 case Binding.ViewSpaceViewDirection:
343 return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.View, shaderStageCapability, hidden);
344 case Binding.WorldSpaceViewDirection:
345 return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.World, shaderStageCapability, hidden);
346 case Binding.TangentSpaceViewDirection:
347 return new ViewDirectionMaterialSlot(slotId, displayName, shaderOutputName, CoordinateSpace.Tangent, shaderStageCapability, hidden);
348 case Binding.VertexColor:
349 return new VertexColorMaterialSlot(slotId, displayName, shaderOutputName, shaderStageCapability, hidden);
350 default:
351 throw new ArgumentOutOfRangeException("attributeBinding", attributeBinding, null);
352 }
353 }
354
355 public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
356 {
357 using (var tempSlots = PooledList<MaterialSlot>.Get())
358 {
359 GetOutputSlots(tempSlots);
360 foreach (var outSlot in tempSlots)
361 {
362 sb.AppendLine(outSlot.concreteValueType.ToShaderString(PrecisionUtil.Token) + " " + GetVariableNameForSlot(outSlot.id) + ";");
363 }
364
365 string call = GetFunctionName() + "(";
366 bool first = true;
367 tempSlots.Clear();
368 GetSlots(tempSlots);
369 tempSlots.Sort((slot1, slot2) => slot1.id.CompareTo(slot2.id));
370 foreach (var slot in tempSlots)
371 {
372 if (!first)
373 {
374 call += ", ";
375 }
376 first = false;
377
378 if (slot.isInputSlot)
379 call += GetSlotValue(slot.id, generationMode);
380 else
381 call += GetVariableNameForSlot(slot.id);
382 }
383 call += ");";
384
385 sb.AppendLine(call);
386 }
387 }
388
389 private string GetFunctionName()
390 {
391 var function = GetFunctionToConvert();
392 return function.Name + (function.IsStatic ? string.Empty : "_" + objectId) + "_$precision"
393 + (this.GetSlots<DynamicVectorMaterialSlot>().Select(s => NodeUtils.GetSlotDimension(s.concreteValueType)).FirstOrDefault() ?? "")
394 + (this.GetSlots<DynamicMatrixMaterialSlot>().Select(s => NodeUtils.GetSlotDimension(s.concreteValueType)).FirstOrDefault() ?? "");
395 }
396
397 private string GetFunctionHeader()
398 {
399 string header = "void " + GetFunctionName() + "(";
400
401 using (var tempSlots = PooledList<MaterialSlot>.Get())
402 {
403 GetSlots(tempSlots);
404 tempSlots.Sort((slot1, slot2) => slot1.id.CompareTo(slot2.id));
405 var first = true;
406 foreach (var slot in tempSlots)
407 {
408 if (!first)
409 header += ", ";
410
411 first = false;
412
413 if (slot.isOutputSlot)
414 header += "out ";
415
416 // always use generic precisions for parameters, they will get concretized by the system
417 header += slot.concreteValueType.ToShaderString(PrecisionUtil.Token) + " " + slot.shaderOutputName;
418 }
419
420 header += ")";
421 }
422
423 return header;
424 }
425
426 private static object GetDefault(Type type)
427 {
428 return type.IsValueType ? Activator.CreateInstance(type) : null;
429 }
430
431 private string GetFunctionBody(MethodInfo info)
432 {
433 var args = new List<object>();
434 foreach (var param in info.GetParameters())
435 args.Add(GetDefault(param.ParameterType));
436
437 var result = info.Invoke(this, args.ToArray()) as string;
438
439 if (string.IsNullOrEmpty(result))
440 return string.Empty;
441
442 // stomp any newline differences that might try to sneak in via this path
443 result = result.Replace("\r\n", "\n");
444
445 using (var tempSlots = PooledList<MaterialSlot>.Get())
446 {
447 GetSlots(tempSlots);
448 foreach (var slot in tempSlots)
449 {
450 var toReplace = string.Format("{{slot{0}dimension}}", slot.id);
451 var replacement = NodeUtils.GetSlotDimension(slot.concreteValueType);
452 result = result.Replace(toReplace, replacement);
453 }
454 }
455
456 return result;
457 }
458
459 public virtual void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
460 {
461 registry.ProvideFunction(GetFunctionName(), s =>
462 {
463 s.AppendLine(GetFunctionHeader());
464 var functionBody = GetFunctionBody(GetFunctionToConvert());
465 var lines = functionBody.Trim('\r', '\n', '\t', ' ');
466 s.AppendLines(lines);
467 });
468 }
469
470 private static SlotAttribute GetSlotAttribute([NotNull] ParameterInfo info)
471 {
472 var attrs = info.GetCustomAttributes(typeof(SlotAttribute), false).OfType<SlotAttribute>().ToList();
473 return attrs.FirstOrDefault();
474 }
475
476 public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
477 {
478 var binding = NeededCoordinateSpace.None;
479 using (var tempSlots = PooledList<MaterialSlot>.Get())
480 {
481 GetInputSlots(tempSlots);
482 foreach (var slot in tempSlots)
483 binding |= slot.RequiresNormal();
484 return binding;
485 }
486 }
487
488 public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability)
489 {
490 var binding = NeededCoordinateSpace.None;
491 using (var tempSlots = PooledList<MaterialSlot>.Get())
492 {
493 GetInputSlots(tempSlots);
494 foreach (var slot in tempSlots)
495 binding |= slot.RequiresViewDirection();
496 return binding;
497 }
498 }
499
500 public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
501 {
502 using (var tempSlots = PooledList<MaterialSlot>.Get())
503 {
504 GetInputSlots(tempSlots);
505 var binding = NeededCoordinateSpace.None;
506 foreach (var slot in tempSlots)
507 binding |= slot.RequiresPosition();
508 return binding;
509 }
510 }
511
512 public NeededCoordinateSpace RequiresPositionPredisplacement(ShaderStageCapability stageCapability)
513 {
514 using (var tempSlots = PooledList<MaterialSlot>.Get())
515 {
516 GetInputSlots(tempSlots);
517 var binding = NeededCoordinateSpace.None;
518 foreach (var slot in tempSlots)
519 binding |= slot.RequiresPositionPredisplacement();
520 return binding;
521 }
522 }
523
524 public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
525 {
526 using (var tempSlots = PooledList<MaterialSlot>.Get())
527 {
528 GetInputSlots(tempSlots);
529 var binding = NeededCoordinateSpace.None;
530 foreach (var slot in tempSlots)
531 binding |= slot.RequiresTangent();
532 return binding;
533 }
534 }
535
536 public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
537 {
538 using (var tempSlots = PooledList<MaterialSlot>.Get())
539 {
540 GetInputSlots(tempSlots);
541 var binding = NeededCoordinateSpace.None;
542 foreach (var slot in tempSlots)
543 binding |= slot.RequiresBitangent();
544 return binding;
545 }
546 }
547
548 public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
549 {
550 using (var tempSlots = PooledList<MaterialSlot>.Get())
551 {
552 GetInputSlots(tempSlots);
553 foreach (var slot in tempSlots)
554 {
555 if (slot.RequiresMeshUV(channel))
556 return true;
557 }
558
559 return false;
560 }
561 }
562
563 public bool RequiresScreenPosition(ShaderStageCapability stageCapability)
564 {
565 using (var tempSlots = PooledList<MaterialSlot>.Get())
566 {
567 GetInputSlots(tempSlots);
568 foreach (var slot in tempSlots)
569 {
570 if (slot.RequiresScreenPosition(stageCapability))
571 return true;
572 }
573 return false;
574 }
575 }
576
577 public bool RequiresNDCPosition(ShaderStageCapability stageCapability)
578 {
579 using (var tempSlots = PooledList<MaterialSlot>.Get())
580 {
581 GetInputSlots(tempSlots);
582 foreach (var slot in tempSlots)
583 {
584 if (slot.RequiresNDCPosition(stageCapability))
585 return true;
586 }
587 return false;
588 }
589 }
590
591 public bool RequiresPixelPosition(ShaderStageCapability stageCapability)
592 {
593 using (var tempSlots = PooledList<MaterialSlot>.Get())
594 {
595 GetInputSlots(tempSlots);
596 foreach (var slot in tempSlots)
597 {
598 if (slot.RequiresPixelPosition(stageCapability))
599 return true;
600 }
601 return false;
602 }
603 }
604
605 public bool RequiresVertexColor(ShaderStageCapability stageCapability)
606 {
607 using (var tempSlots = PooledList<MaterialSlot>.Get())
608 {
609 GetInputSlots(tempSlots);
610 foreach (var slot in tempSlots)
611 {
612 if (slot.RequiresVertexColor())
613 return true;
614 }
615
616 return false;
617 }
618 }
619 }
620}