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.Drawing.Controls;
7using UnityEditor.Graphing.Util;
8using UnityEngine.Pool;
9
10namespace UnityEditor.ShaderGraph
11{
12 enum MatrixAxis
13 {
14 Row,
15 Column
16 }
17
18 [Title("Math", "Matrix", "Matrix Split")]
19 class MatrixSplitNode : AbstractMaterialNode, IGeneratesBodyCode
20 {
21 const string kInputSlotName = "In";
22 const string kOutputSlotM0Name = "M0";
23 const string kOutputSlotM1Name = "M1";
24 const string kOutputSlotM2Name = "M2";
25 const string kOutputSlotM3Name = "M3";
26
27 public const int InputSlotId = 0;
28 public const int OutputSlotRId = 1;
29 public const int OutputSlotGId = 2;
30 public const int OutputSlotBId = 3;
31 public const int OutputSlotAId = 4;
32
33 public MatrixSplitNode()
34 {
35 name = "Matrix Split";
36 UpdateNodeAfterDeserialization();
37 }
38
39 [SerializeField]
40 MatrixAxis m_Axis;
41
42 [EnumControl("")]
43 MatrixAxis axis
44 {
45 get { return m_Axis; }
46 set
47 {
48 if (m_Axis.Equals(value))
49 return;
50 m_Axis = value;
51 Dirty(ModificationScope.Graph);
52 }
53 }
54
55 static string[] s_ComponentList = new string[4] { "r", "g", "b", "a" };
56
57 public sealed override void UpdateNodeAfterDeserialization()
58 {
59 AddSlot(new DynamicMatrixMaterialSlot(InputSlotId, kInputSlotName, kInputSlotName, SlotType.Input));
60 AddSlot(new DynamicVectorMaterialSlot(OutputSlotRId, kOutputSlotM0Name, kOutputSlotM0Name, SlotType.Output, Vector4.zero));
61 AddSlot(new DynamicVectorMaterialSlot(OutputSlotGId, kOutputSlotM1Name, kOutputSlotM1Name, SlotType.Output, Vector4.zero));
62 AddSlot(new DynamicVectorMaterialSlot(OutputSlotBId, kOutputSlotM2Name, kOutputSlotM2Name, SlotType.Output, Vector4.zero));
63 AddSlot(new DynamicVectorMaterialSlot(OutputSlotAId, kOutputSlotM3Name, kOutputSlotM3Name, SlotType.Output, Vector4.zero));
64 RemoveSlotsNameNotMatching(new int[] { InputSlotId, OutputSlotRId, OutputSlotGId, OutputSlotBId, OutputSlotAId });
65 }
66
67 static int[] s_OutputSlots = { OutputSlotRId, OutputSlotGId, OutputSlotBId, OutputSlotAId };
68
69 public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
70 {
71 var inputValue = GetSlotValue(InputSlotId, generationMode);
72
73 var inputSlot = FindInputSlot<MaterialSlot>(InputSlotId);
74 var numInputRows = 0;
75 bool useIndentity = false;
76
77 if (inputSlot != null)
78 {
79 numInputRows = SlotValueHelper.GetMatrixDimension(inputSlot.concreteValueType);
80 if (numInputRows > 4)
81 numInputRows = 0;
82
83 if (!owner.GetEdges(inputSlot.slotReference).Any())
84 {
85 numInputRows = 0;
86 useIndentity = true;
87 }
88 }
89
90 int concreteRowCount = useIndentity ? 2 : numInputRows;
91
92 for (var r = 0; r < 4; r++)
93 {
94 string outputValue;
95 if (r >= numInputRows)
96 {
97 outputValue = string.Format("$precision{0}(", concreteRowCount);
98 for (int c = 0; c < concreteRowCount; c++)
99 {
100 if (c != 0)
101 outputValue += ", ";
102 outputValue += Matrix4x4.identity.GetRow(r)[c];
103 }
104 outputValue += ")";
105 }
106 else
107 {
108 switch (m_Axis)
109 {
110 case MatrixAxis.Column:
111 outputValue = string.Format("$precision{0}(", numInputRows);
112 for (int c = 0; c < numInputRows; c++)
113 {
114 if (c != 0)
115 outputValue += ", ";
116 outputValue += string.Format("{0}[{1}].{2}", inputValue, c, s_ComponentList[r]);
117 }
118 outputValue += ")";
119 break;
120 default:
121 outputValue = string.Format("{0}[{1}]", inputValue, r);
122 break;
123 }
124 }
125 sb.AppendLine(string.Format("$precision{0} {1} = {2};", concreteRowCount, GetVariableNameForSlot(s_OutputSlots[r]), outputValue));
126 }
127 }
128
129 public override void EvaluateDynamicMaterialSlots(List<MaterialSlot> inputSlots, List<MaterialSlot> outputSlots)
130 {
131 var dynamicInputSlotsToCompare = DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Get();
132 var skippedDynamicSlots = ListPool<DynamicVectorMaterialSlot>.Get();
133
134 var dynamicMatrixInputSlotsToCompare = DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Get();
135 var skippedDynamicMatrixSlots = ListPool<DynamicMatrixMaterialSlot>.Get();
136
137 // iterate the input slots
138 {
139 foreach (var inputSlot in inputSlots)
140 {
141 inputSlot.hasError = false;
142
143 // if there is a connection
144 var edges = owner.GetEdges(inputSlot.slotReference).ToList();
145 if (!edges.Any())
146 {
147 if (inputSlot is DynamicVectorMaterialSlot)
148 skippedDynamicSlots.Add(inputSlot as DynamicVectorMaterialSlot);
149 if (inputSlot is DynamicMatrixMaterialSlot)
150 skippedDynamicMatrixSlots.Add(inputSlot as DynamicMatrixMaterialSlot);
151 continue;
152 }
153
154 // get the output details
155 var outputSlotRef = edges[0].outputSlot;
156 var outputNode = outputSlotRef.node;
157 if (outputNode == null)
158 continue;
159
160 var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
161 if (outputSlot == null)
162 continue;
163
164 if (outputSlot.hasError)
165 {
166 inputSlot.hasError = true;
167 continue;
168 }
169
170 var outputConcreteType = outputSlot.concreteValueType;
171 // dynamic input... depends on output from other node.
172 // we need to compare ALL dynamic inputs to make sure they
173 // are compatable.
174 if (inputSlot is DynamicVectorMaterialSlot)
175 {
176 dynamicInputSlotsToCompare.Add((DynamicVectorMaterialSlot)inputSlot, outputConcreteType);
177 continue;
178 }
179 else if (inputSlot is DynamicMatrixMaterialSlot)
180 {
181 dynamicMatrixInputSlotsToCompare.Add((DynamicMatrixMaterialSlot)inputSlot, outputConcreteType);
182 continue;
183 }
184 }
185
186 // and now dynamic matrices
187 var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicMatrixInputSlotsToCompare.Values);
188 foreach (var dynamicKvP in dynamicMatrixInputSlotsToCompare)
189 dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
190 foreach (var skippedSlot in skippedDynamicMatrixSlots)
191 skippedSlot.SetConcreteType(dynamicMatrixType);
192
193 // we can now figure out the dynamic slotType
194 // from here set all the
195 var dynamicType = SlotValueHelper.ConvertMatrixToVectorType(dynamicMatrixType);
196 foreach (var dynamicKvP in dynamicInputSlotsToCompare)
197 dynamicKvP.Key.SetConcreteType(dynamicType);
198 foreach (var skippedSlot in skippedDynamicSlots)
199 skippedSlot.SetConcreteType(dynamicType);
200
201 bool inputError = inputSlots.Any(x => x.hasError);
202 if (inputError)
203 {
204 owner.AddConcretizationError(objectId, string.Format("Node {0} had input error", objectId));
205 hasError = true;
206 }
207 // configure the output slots now
208 // their slotType will either be the default output slotType
209 // or the above dynanic slotType for dynamic nodes
210 // or error if there is an input error
211 foreach (var outputSlot in outputSlots)
212 {
213 outputSlot.hasError = false;
214
215 if (inputError)
216 {
217 outputSlot.hasError = true;
218 continue;
219 }
220
221 if (outputSlot is DynamicVectorMaterialSlot)
222 {
223 (outputSlot as DynamicVectorMaterialSlot).SetConcreteType(dynamicType);
224 continue;
225 }
226 else if (outputSlot is DynamicMatrixMaterialSlot)
227 {
228 (outputSlot as DynamicMatrixMaterialSlot).SetConcreteType(dynamicMatrixType);
229 continue;
230 }
231 }
232
233 if (outputSlots.Any(x => x.hasError))
234 {
235 owner.AddConcretizationError(objectId, string.Format("Node {0} had output error", objectId));
236 hasError = true;
237 }
238 }
239
240 CalculateNodeHasError();
241
242 ListPool<DynamicVectorMaterialSlot>.Release(skippedDynamicSlots);
243 DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
244
245 ListPool<DynamicMatrixMaterialSlot>.Release(skippedDynamicMatrixSlots);
246 DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Release(dynamicMatrixInputSlotsToCompare);
247 }
248 }
249}