A game about forced loneliness, made by TACStudios
at master 293 lines 15 kB view raw
1using System; 2using System.Linq; 3using System.Collections.Generic; 4using UnityEngine; 5using UnityEditor.Graphing; 6using UnityEditor.ShaderGraph.Drawing; 7using UnityEditor.ShaderGraph.Internal; 8using UnityEditor.ShaderGraph.Serialization; 9 10namespace UnityEditor.ShaderGraph 11{ 12 [Serializable] 13 [Title("Input", "Property")] 14 class PropertyNode : AbstractMaterialNode, IGeneratesBodyCode, IOnAssetEnabled, IShaderInputObserver 15 { 16 public PropertyNode() 17 { 18 name = "Property"; 19 UpdateNodeAfterDeserialization(); 20 } 21 22 // Property-Types 23 public override string documentationURL => UnityEngine.Rendering.ShaderGraph.Documentation.GetPageLink("Property-Types"); 24 25 public override void UpdateNodeAfterDeserialization() 26 { 27 base.UpdateNodeAfterDeserialization(); 28 29 if (owner == null) 30 return; 31 32 if (property is Vector1ShaderProperty vector1ShaderProperty && vector1ShaderProperty.floatType == FloatType.Slider) 33 { 34 // Previously, the Slider vector1 property allowed the min value to be greater than the max 35 // We no longer want to support that behavior so if such a property is encountered, swap the values 36 if (vector1ShaderProperty.rangeValues.x > vector1ShaderProperty.rangeValues.y) 37 { 38 vector1ShaderProperty.rangeValues = new Vector2(vector1ShaderProperty.rangeValues.y, vector1ShaderProperty.rangeValues.x); 39 Dirty(ModificationScope.Graph); 40 } 41 } 42 } 43 44 [SerializeField] 45 JsonRef<AbstractShaderProperty> m_Property; 46 47 public AbstractShaderProperty property 48 { 49 get { return m_Property; } 50 set 51 { 52 if (m_Property == value) 53 return; 54 55 m_Property = value; 56 AddOutputSlot(); 57 Dirty(ModificationScope.Topological); 58 } 59 } 60 61 // this node's precision is always controlled by the property precision 62 public override bool canSetPrecision => false; 63 64 public void UpdateNodeDisplayName(string newDisplayName) 65 { 66 MaterialSlot foundSlot = FindSlot<MaterialSlot>(OutputSlotId); 67 68 if (foundSlot != null) 69 foundSlot.displayName = newDisplayName; 70 } 71 72 public void OnEnable() 73 { 74 AddOutputSlot(); 75 } 76 77 public const int OutputSlotId = 0; 78 79 void AddOutputSlot() 80 { 81 if (property is MultiJsonInternal.UnknownShaderPropertyType uspt) 82 { 83 // keep existing slots, don't modify them 84 return; 85 } 86 switch (property.concreteShaderValueType) 87 { 88 case ConcreteSlotValueType.Boolean: 89 AddSlot(new BooleanMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, false)); 90 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 91 break; 92 case ConcreteSlotValueType.Vector1: 93 AddSlot(new Vector1MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, 0)); 94 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 95 break; 96 case ConcreteSlotValueType.Vector2: 97 AddSlot(new Vector2MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, Vector4.zero)); 98 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 99 break; 100 case ConcreteSlotValueType.Vector3: 101 AddSlot(new Vector3MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, Vector4.zero)); 102 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 103 break; 104 case ConcreteSlotValueType.Vector4: 105 AddSlot(new Vector4MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, Vector4.zero)); 106 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 107 break; 108 case ConcreteSlotValueType.Matrix2: 109 AddSlot(new Matrix2MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 110 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 111 break; 112 case ConcreteSlotValueType.Matrix3: 113 AddSlot(new Matrix3MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 114 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 115 break; 116 case ConcreteSlotValueType.Matrix4: 117 AddSlot(new Matrix4MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 118 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 119 break; 120 case ConcreteSlotValueType.Texture2D: 121 AddSlot(new Texture2DMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 122 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 123 break; 124 case ConcreteSlotValueType.Texture2DArray: 125 AddSlot(new Texture2DArrayMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 126 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 127 break; 128 case ConcreteSlotValueType.Texture3D: 129 AddSlot(new Texture3DMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 130 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 131 break; 132 case ConcreteSlotValueType.Cubemap: 133 AddSlot(new CubemapMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 134 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 135 break; 136 case ConcreteSlotValueType.SamplerState: 137 AddSlot(new SamplerStateMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 138 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 139 break; 140 case ConcreteSlotValueType.Gradient: 141 AddSlot(new GradientMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 142 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 143 break; 144 case ConcreteSlotValueType.VirtualTexture: 145 AddSlot(new VirtualTextureMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output)); 146 RemoveSlotsNameNotMatching(new[] { OutputSlotId }); 147 break; 148 default: 149 throw new ArgumentOutOfRangeException(); 150 } 151 } 152 153 public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode mode) 154 { 155 // preview is always generating a full shader, even when previewing within a subgraph 156 bool isGeneratingSubgraph = owner.isSubGraph && (mode != GenerationMode.Preview); 157 158 switch (property.propertyType) 159 { 160 case PropertyType.Boolean: 161 sb.AppendLine($"$precision {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 162 break; 163 case PropertyType.Float: 164 sb.AppendLine($"$precision {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 165 break; 166 case PropertyType.Vector2: 167 sb.AppendLine($"$precision2 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 168 break; 169 case PropertyType.Vector3: 170 sb.AppendLine($"$precision3 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 171 break; 172 case PropertyType.Vector4: 173 sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 174 break; 175 case PropertyType.Color: 176 switch (property.sgVersion) 177 { 178 case 0: 179 case 2: 180 sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 181 break; 182 case 1: 183 case 3: 184 //Exposed color properties get put into the correct space automagikally by Unity UNLESS tagged as HDR, then they just get passed in as is. 185 //for consistency with other places in the editor, we assume HDR colors are in linear space, and correct for gamma space here 186 if ((property as ColorShaderProperty).colorMode == ColorMode.HDR) 187 { 188 sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = IsGammaSpace() ? LinearToSRGB({property.GetHLSLVariableName(isGeneratingSubgraph, mode)}) : {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 189 } 190 else 191 { 192 sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 193 } 194 break; 195 default: 196 throw new Exception($"Unknown Color Property Version on property {property.displayName}"); 197 } 198 break; 199 case PropertyType.Matrix2: 200 sb.AppendLine($"$precision2x2 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 201 break; 202 case PropertyType.Matrix3: 203 sb.AppendLine($"$precision3x3 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 204 break; 205 case PropertyType.Matrix4: 206 sb.AppendLine($"$precision4x4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 207 break; 208 case PropertyType.Texture2D: 209 sb.AppendLine($"UnityTexture2D {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 210 break; 211 case PropertyType.Texture3D: 212 sb.AppendLine($"UnityTexture3D {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 213 break; 214 case PropertyType.Texture2DArray: 215 sb.AppendLine($"UnityTexture2DArray {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 216 break; 217 case PropertyType.Cubemap: 218 sb.AppendLine($"UnityTextureCube {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 219 break; 220 case PropertyType.SamplerState: 221 sb.AppendLine($"UnitySamplerState {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 222 break; 223 case PropertyType.Gradient: 224 if (mode == GenerationMode.Preview) 225 sb.AppendLine($"Gradient {GetVariableNameForSlot(OutputSlotId)} = {GradientUtil.GetGradientForPreview(property.GetHLSLVariableName(isGeneratingSubgraph, mode))};"); 226 else 227 sb.AppendLine($"Gradient {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};"); 228 break; 229 } 230 231 if (property.isConnectionTestable) 232 { 233 // If in a subgraph, the value will be read from a function parameter. 234 // If generating preview mode code, we always inline the value, according to code gen requirements. 235 // The parent graph always sets the explicit value to be passed to a subgraph function. 236 sb.AppendLine("bool {0} = {1};", GetConnectionStateVariableNameForSlot(OutputSlotId), (mode == GenerationMode.Preview || !isGeneratingSubgraph) ? (IsSlotConnected(OutputSlotId) ? "true" : "false") : property.GetConnectionStateHLSLVariableName()); 237 } 238 } 239 240 public override string GetVariableNameForSlot(int slotId) 241 { 242 // TODO: we should switch VirtualTexture away from the macro-based variables and towards using the same approach as Texture2D 243 switch (property.propertyType) 244 { 245 case PropertyType.VirtualTexture: 246 return property.GetHLSLVariableName(owner.isSubGraph, GenerationMode.ForReals); 247 } 248 249 return base.GetVariableNameForSlot(slotId); 250 } 251 252 public string GetConnectionStateVariableNameForSlot(int slotId) 253 { 254 return ShaderInput.GetConnectionStateVariableName(GetVariableNameForSlot(slotId)); 255 } 256 257 protected override void CalculateNodeHasError() 258 { 259 if (property == null || !owner.properties.Any(x => x == property)) 260 { 261 owner.AddConcretizationError(objectId, "Property Node has no associated Blackboard property."); 262 } 263 else if (property is MultiJsonInternal.UnknownShaderPropertyType) 264 { 265 owner.AddValidationError(objectId, "Property is of unknown type, a package may be missing.", Rendering.ShaderCompilerMessageSeverity.Warning); 266 } 267 } 268 269 public override void UpdatePrecision(List<MaterialSlot> inputSlots) 270 { 271 // Get precision from Property 272 if (property == null) 273 { 274 owner.AddConcretizationError(objectId, string.Format("No matching poperty found on owner for node {0}", objectId)); 275 hasError = true; 276 return; 277 } 278 279 // this node's precision is always controlled by the property precision 280 precision = property.precision; 281 282 graphPrecision = precision.ToGraphPrecision(GraphPrecision.Graph); 283 concretePrecision = graphPrecision.ToConcrete(owner.graphDefaultConcretePrecision); 284 } 285 286 public void OnShaderInputUpdated(ModificationScope modificationScope) 287 { 288 if(modificationScope == ModificationScope.Layout) 289 UpdateNodeDisplayName(property.displayName); 290 Dirty(modificationScope); 291 } 292 } 293}