MultiplyNode.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //using System.Reflection;
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEditor.Graphing;
  5. using UnityEngine;
  6. using System.Linq;
  7. namespace UnityEditor.ShaderGraph
  8. {
  9. [Title("Math", "Basic", "Multiply")]
  10. class MultiplyNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction
  11. {
  12. public MultiplyNode()
  13. {
  14. name = "Multiply";
  15. UpdateNodeAfterDeserialization();
  16. }
  17. const int Input1SlotId = 0;
  18. const int Input2SlotId = 1;
  19. const int OutputSlotId = 2;
  20. const string kInput1SlotName = "A";
  21. const string kInput2SlotName = "B";
  22. const string kOutputSlotName = "Out";
  23. enum MultiplyType
  24. {
  25. Vector,
  26. Matrix,
  27. Mixed
  28. }
  29. MultiplyType m_MultiplyType;
  30. public override bool hasPreview
  31. {
  32. get { return m_MultiplyType != MultiplyType.Matrix; }
  33. }
  34. string GetFunctionHeader()
  35. {
  36. return "Unity_Multiply" + "_" + concretePrecision.ToShaderString()
  37. + (this.GetSlots<DynamicVectorMaterialSlot>().Select(s => NodeUtils.GetSlotDimension(s.concreteValueType)).FirstOrDefault() ?? "")
  38. + (this.GetSlots<DynamicMatrixMaterialSlot>().Select(s => NodeUtils.GetSlotDimension(s.concreteValueType)).FirstOrDefault() ?? "");
  39. }
  40. public sealed override void UpdateNodeAfterDeserialization()
  41. {
  42. AddSlot(new DynamicValueMaterialSlot(Input1SlotId, kInput1SlotName, kInput1SlotName, SlotType.Input, Matrix4x4.zero));
  43. AddSlot(new DynamicValueMaterialSlot(Input2SlotId, kInput2SlotName, kInput2SlotName, SlotType.Input, new Matrix4x4(new Vector4(2, 2, 2, 2), new Vector4(2, 2, 2, 2), new Vector4(2, 2, 2, 2), new Vector4(2, 2, 2, 2))));
  44. AddSlot(new DynamicValueMaterialSlot(OutputSlotId, kOutputSlotName, kOutputSlotName, SlotType.Output, Matrix4x4.zero));
  45. RemoveSlotsNameNotMatching(new[] { Input1SlotId, Input2SlotId, OutputSlotId });
  46. }
  47. public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
  48. {
  49. var input1Value = GetSlotValue(Input1SlotId, generationMode);
  50. var input2Value = GetSlotValue(Input2SlotId, generationMode);
  51. var outputValue = GetSlotValue(OutputSlotId, generationMode);
  52. sb.AppendLine("{0} {1};", FindOutputSlot<MaterialSlot>(OutputSlotId).concreteValueType.ToShaderString(), GetVariableNameForSlot(OutputSlotId));
  53. sb.AppendLine("{0}({1}, {2}, {3});", GetFunctionHeader(), input1Value, input2Value, outputValue);
  54. }
  55. string GetFunctionName()
  56. {
  57. return $"Unity_Multiply_{FindSlot<MaterialSlot>(Input1SlotId).concreteValueType.ToShaderString(concretePrecision)}_{FindSlot<MaterialSlot>(Input2SlotId).concreteValueType.ToShaderString(concretePrecision)}";
  58. }
  59. public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
  60. {
  61. registry.ProvideFunction(GetFunctionName(), s =>
  62. {
  63. s.AppendLine("void {0}({1} A, {2} B, out {3} Out)",
  64. GetFunctionHeader(),
  65. FindInputSlot<MaterialSlot>(Input1SlotId).concreteValueType.ToShaderString(),
  66. FindInputSlot<MaterialSlot>(Input2SlotId).concreteValueType.ToShaderString(),
  67. FindOutputSlot<MaterialSlot>(OutputSlotId).concreteValueType.ToShaderString());
  68. using (s.BlockScope())
  69. {
  70. switch (m_MultiplyType)
  71. {
  72. case MultiplyType.Vector:
  73. s.AppendLine("Out = A * B;");
  74. break;
  75. default:
  76. s.AppendLine("Out = mul(A, B);");
  77. break;
  78. }
  79. }
  80. });
  81. }
  82. // Internal validation
  83. // -------------------------------------------------
  84. public override void ValidateNode()
  85. {
  86. var isInError = false;
  87. var errorMessage = k_validationErrorMessage;
  88. var dynamicInputSlotsToCompare = DictionaryPool<DynamicValueMaterialSlot, ConcreteSlotValueType>.Get();
  89. var skippedDynamicSlots = ListPool<DynamicValueMaterialSlot>.Get();
  90. // iterate the input slots
  91. using (var tempSlots = PooledList<MaterialSlot>.Get())
  92. {
  93. GetInputSlots(tempSlots);
  94. foreach (var inputSlot in tempSlots)
  95. {
  96. inputSlot.hasError = false;
  97. // if there is a connection
  98. var edges = owner.GetEdges(inputSlot.slotReference).ToList();
  99. if (!edges.Any())
  100. {
  101. if (inputSlot is DynamicValueMaterialSlot)
  102. skippedDynamicSlots.Add(inputSlot as DynamicValueMaterialSlot);
  103. continue;
  104. }
  105. // get the output details
  106. var outputSlotRef = edges[0].outputSlot;
  107. var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
  108. if (outputNode == null)
  109. continue;
  110. var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
  111. if (outputSlot == null)
  112. continue;
  113. if (outputSlot.hasError)
  114. {
  115. inputSlot.hasError = true;
  116. continue;
  117. }
  118. var outputConcreteType = outputSlot.concreteValueType;
  119. // dynamic input... depends on output from other node.
  120. // we need to compare ALL dynamic inputs to make sure they
  121. // are compatable.
  122. if (inputSlot is DynamicValueMaterialSlot)
  123. {
  124. dynamicInputSlotsToCompare.Add((DynamicValueMaterialSlot)inputSlot, outputConcreteType);
  125. continue;
  126. }
  127. }
  128. m_MultiplyType = GetMultiplyType(dynamicInputSlotsToCompare.Values);
  129. // Resolve dynamics depending on matrix/vector configuration
  130. switch (m_MultiplyType)
  131. {
  132. // If all matrix resolve as per dynamic matrix
  133. case MultiplyType.Matrix:
  134. var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
  135. foreach (var dynamicKvP in dynamicInputSlotsToCompare)
  136. dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
  137. foreach (var skippedSlot in skippedDynamicSlots)
  138. skippedSlot.SetConcreteType(dynamicMatrixType);
  139. break;
  140. // If mixed handle differently:
  141. // Iterate all slots and set their concretes based on their edges
  142. // Find matrix slot and convert its type to a vector type
  143. // Reiterate all slots and set non matrix slots to the vector type
  144. case MultiplyType.Mixed:
  145. foreach (var dynamicKvP in dynamicInputSlotsToCompare)
  146. {
  147. SetConcreteValueTypeFromEdge(dynamicKvP.Key);
  148. }
  149. MaterialSlot matrixSlot = GetMatrixSlot();
  150. ConcreteSlotValueType vectorType = SlotValueHelper.ConvertMatrixToVectorType(matrixSlot.concreteValueType);
  151. foreach (var dynamicKvP in dynamicInputSlotsToCompare)
  152. {
  153. if (dynamicKvP.Key != matrixSlot)
  154. dynamicKvP.Key.SetConcreteType(vectorType);
  155. }
  156. foreach (var skippedSlot in skippedDynamicSlots)
  157. {
  158. skippedSlot.SetConcreteType(vectorType);
  159. }
  160. break;
  161. // If all vector resolve as per dynamic vector
  162. default:
  163. var dynamicVectorType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
  164. foreach (var dynamicKvP in dynamicInputSlotsToCompare)
  165. dynamicKvP.Key.SetConcreteType(dynamicVectorType);
  166. foreach (var skippedSlot in skippedDynamicSlots)
  167. skippedSlot.SetConcreteType(dynamicVectorType);
  168. break;
  169. }
  170. tempSlots.Clear();
  171. GetInputSlots(tempSlots);
  172. var inputError = tempSlots.Any(x => x.hasError);
  173. // configure the output slots now
  174. // their slotType will either be the default output slotType
  175. // or the above dynanic slotType for dynamic nodes
  176. // or error if there is an input error
  177. tempSlots.Clear();
  178. GetOutputSlots(tempSlots);
  179. foreach (var outputSlot in tempSlots)
  180. {
  181. outputSlot.hasError = false;
  182. if (inputError)
  183. {
  184. outputSlot.hasError = true;
  185. continue;
  186. }
  187. if (outputSlot is DynamicValueMaterialSlot)
  188. {
  189. // Apply similar logic to output slot
  190. switch (m_MultiplyType)
  191. {
  192. // As per dynamic matrix
  193. case MultiplyType.Matrix:
  194. var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
  195. (outputSlot as DynamicValueMaterialSlot).SetConcreteType(dynamicMatrixType);
  196. break;
  197. // Mixed configuration
  198. // Find matrix slot and convert type to vector
  199. // Set output concrete to vector
  200. case MultiplyType.Mixed:
  201. MaterialSlot matrixSlot = GetMatrixSlot();
  202. ConcreteSlotValueType vectorType = SlotValueHelper.ConvertMatrixToVectorType(matrixSlot.concreteValueType);
  203. (outputSlot as DynamicValueMaterialSlot).SetConcreteType(vectorType);
  204. break;
  205. // As per dynamic vector
  206. default:
  207. var dynamicVectorType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
  208. (outputSlot as DynamicValueMaterialSlot).SetConcreteType(dynamicVectorType);
  209. break;
  210. }
  211. continue;
  212. }
  213. }
  214. isInError |= inputError;
  215. tempSlots.Clear();
  216. GetOutputSlots(tempSlots);
  217. isInError |= tempSlots.Any(x => x.hasError);
  218. }
  219. isInError |= CalculateNodeHasError(ref errorMessage);
  220. isInError |= ValidateConcretePrecision(ref errorMessage);
  221. hasError = isInError;
  222. if (isInError)
  223. {
  224. ((GraphData) owner).AddValidationError(tempId, errorMessage);
  225. }
  226. else
  227. {
  228. ++version;
  229. }
  230. ListPool<DynamicValueMaterialSlot>.Release(skippedDynamicSlots);
  231. DictionaryPool<DynamicValueMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
  232. }
  233. private MultiplyType GetMultiplyType(IEnumerable<ConcreteSlotValueType> inputTypes)
  234. {
  235. var concreteSlotValueTypes = inputTypes as List<ConcreteSlotValueType> ?? inputTypes.ToList();
  236. int matrixCount = 0;
  237. int vectorCount = 0;
  238. for (int i = 0; i < concreteSlotValueTypes.Count; i++)
  239. {
  240. if (concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector4
  241. || concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector3
  242. || concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector2
  243. || concreteSlotValueTypes[i] == ConcreteSlotValueType.Vector1)
  244. {
  245. vectorCount++;
  246. }
  247. else if (concreteSlotValueTypes[i] == ConcreteSlotValueType.Matrix4
  248. || concreteSlotValueTypes[i] == ConcreteSlotValueType.Matrix3
  249. || concreteSlotValueTypes[i] == ConcreteSlotValueType.Matrix2)
  250. {
  251. matrixCount++;
  252. }
  253. }
  254. if (matrixCount == 2)
  255. return MultiplyType.Matrix;
  256. else if (vectorCount == 2)
  257. return MultiplyType.Vector;
  258. else if (matrixCount == 1)
  259. return MultiplyType.Mixed;
  260. else
  261. return MultiplyType.Vector;
  262. }
  263. private MaterialSlot GetMatrixSlot()
  264. {
  265. List<MaterialSlot> slots = new List<MaterialSlot>();
  266. GetInputSlots(slots);
  267. for (int i = 0; i < slots.Count; i++)
  268. {
  269. var edges = owner.GetEdges(slots[i].slotReference).ToList();
  270. if (!edges.Any())
  271. continue;
  272. var outputNode = owner.GetNodeFromGuid(edges[0].outputSlot.nodeGuid);
  273. var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(edges[0].outputSlot.slotId);
  274. if (outputSlot.concreteValueType == ConcreteSlotValueType.Matrix4
  275. || outputSlot.concreteValueType == ConcreteSlotValueType.Matrix3
  276. || outputSlot.concreteValueType == ConcreteSlotValueType.Matrix2)
  277. return slots[i];
  278. }
  279. return null;
  280. }
  281. private void SetConcreteValueTypeFromEdge(DynamicValueMaterialSlot slot)
  282. {
  283. var edges = owner.GetEdges(slot.slotReference).ToList();
  284. if (!edges.Any())
  285. return;
  286. var outputNode = owner.GetNodeFromGuid(edges[0].outputSlot.nodeGuid);
  287. var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(edges[0].outputSlot.slotId);
  288. slot.SetConcreteType(outputSlot.concreteValueType);
  289. }
  290. }
  291. }