Singularity/Library/PackageCache/com.unity.shadergraph@12.1.11/Editor/Data/Nodes/Input/PropertyNode.cs
2024-05-06 11:45:45 -07:00

285 lines
15 KiB
C#

using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Internal;
using UnityEditor.ShaderGraph.Serialization;
namespace UnityEditor.ShaderGraph
{
[Serializable]
[Title("Input", "Property")]
class PropertyNode : AbstractMaterialNode, IGeneratesBodyCode, IOnAssetEnabled
{
public PropertyNode()
{
name = "Property";
UpdateNodeAfterDeserialization();
}
public override void UpdateNodeAfterDeserialization()
{
base.UpdateNodeAfterDeserialization();
if (owner == null)
return;
if (property is Vector1ShaderProperty vector1ShaderProperty && vector1ShaderProperty.floatType == FloatType.Slider)
{
// Previously, the Slider vector1 property allowed the min value to be greater than the max
// We no longer want to support that behavior so if such a property is encountered, swap the values
if (vector1ShaderProperty.rangeValues.x > vector1ShaderProperty.rangeValues.y)
{
vector1ShaderProperty.rangeValues = new Vector2(vector1ShaderProperty.rangeValues.y, vector1ShaderProperty.rangeValues.x);
Dirty(ModificationScope.Graph);
}
}
}
[SerializeField]
JsonRef<AbstractShaderProperty> m_Property;
public AbstractShaderProperty property
{
get { return m_Property; }
set
{
if (m_Property == value)
return;
m_Property = value;
// Set callback association for display name updates
m_Property.value.displayNameUpdateTrigger += UpdateNodeDisplayName;
AddOutputSlot();
Dirty(ModificationScope.Topological);
}
}
// this node's precision is always controlled by the property precision
public override bool canSetPrecision => false;
public void UpdateNodeDisplayName(string newDisplayName)
{
MaterialSlot foundSlot = FindSlot<MaterialSlot>(OutputSlotId);
if (foundSlot != null)
foundSlot.displayName = newDisplayName;
}
public void OnEnable()
{
AddOutputSlot();
}
public const int OutputSlotId = 0;
void AddOutputSlot()
{
if (property is MultiJsonInternal.UnknownShaderPropertyType uspt)
{
// keep existing slots, don't modify them
return;
}
switch (property.concreteShaderValueType)
{
case ConcreteSlotValueType.Boolean:
AddSlot(new BooleanMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, false));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Vector1:
AddSlot(new Vector1MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, 0));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Vector2:
AddSlot(new Vector2MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, Vector4.zero));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Vector3:
AddSlot(new Vector3MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, Vector4.zero));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Vector4:
AddSlot(new Vector4MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output, Vector4.zero));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Matrix2:
AddSlot(new Matrix2MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Matrix3:
AddSlot(new Matrix3MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Matrix4:
AddSlot(new Matrix4MaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Texture2D:
AddSlot(new Texture2DMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Texture2DArray:
AddSlot(new Texture2DArrayMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Texture3D:
AddSlot(new Texture3DMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Cubemap:
AddSlot(new CubemapMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.SamplerState:
AddSlot(new SamplerStateMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.Gradient:
AddSlot(new GradientMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
case ConcreteSlotValueType.VirtualTexture:
AddSlot(new VirtualTextureMaterialSlot(OutputSlotId, property.displayName, "Out", SlotType.Output));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode mode)
{
// preview is always generating a full shader, even when previewing within a subgraph
bool isGeneratingSubgraph = owner.isSubGraph && (mode != GenerationMode.Preview);
switch (property.propertyType)
{
case PropertyType.Boolean:
sb.AppendLine($"$precision {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Float:
sb.AppendLine($"$precision {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Vector2:
sb.AppendLine($"$precision2 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Vector3:
sb.AppendLine($"$precision3 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Vector4:
sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Color:
switch (property.sgVersion)
{
case 0:
case 2:
sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case 1:
case 3:
//Exposed color properties get put into the correct space automagikally by Unity UNLESS tagged as HDR, then they just get passed in as is.
//for consistency with other places in the editor, we assume HDR colors are in linear space, and correct for gamma space here
if ((property as ColorShaderProperty).colorMode == ColorMode.HDR)
{
sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = IsGammaSpace() ? LinearToSRGB({property.GetHLSLVariableName(isGeneratingSubgraph, mode)}) : {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
}
else
{
sb.AppendLine($"$precision4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
}
break;
default:
throw new Exception($"Unknown Color Property Version on property {property.displayName}");
}
break;
case PropertyType.Matrix2:
sb.AppendLine($"$precision2x2 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Matrix3:
sb.AppendLine($"$precision3x3 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Matrix4:
sb.AppendLine($"$precision4x4 {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Texture2D:
sb.AppendLine($"UnityTexture2D {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Texture3D:
sb.AppendLine($"UnityTexture3D {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Texture2DArray:
sb.AppendLine($"UnityTexture2DArray {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Cubemap:
sb.AppendLine($"UnityTextureCube {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.SamplerState:
sb.AppendLine($"UnitySamplerState {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
case PropertyType.Gradient:
if (mode == GenerationMode.Preview)
sb.AppendLine($"Gradient {GetVariableNameForSlot(OutputSlotId)} = {GradientUtil.GetGradientForPreview(property.GetHLSLVariableName(isGeneratingSubgraph, mode))};");
else
sb.AppendLine($"Gradient {GetVariableNameForSlot(OutputSlotId)} = {property.GetHLSLVariableName(isGeneratingSubgraph, mode)};");
break;
}
if (property.isConnectionTestable)
{
// If in a subgraph, the value will be read from a function parameter.
// If generating preview mode code, we always inline the value, according to code gen requirements.
// The parent graph always sets the explicit value to be passed to a subgraph function.
sb.AppendLine("bool {0} = {1};", GetConnectionStateVariableNameForSlot(OutputSlotId), (mode == GenerationMode.Preview || !isGeneratingSubgraph) ? (IsSlotConnected(OutputSlotId) ? "true" : "false") : property.GetConnectionStateHLSLVariableName());
}
}
public override string GetVariableNameForSlot(int slotId)
{
// TODO: we should switch VirtualTexture away from the macro-based variables and towards using the same approach as Texture2D
switch (property.propertyType)
{
case PropertyType.VirtualTexture:
return property.GetHLSLVariableName(owner.isSubGraph, GenerationMode.ForReals);
}
return base.GetVariableNameForSlot(slotId);
}
public string GetConnectionStateVariableNameForSlot(int slotId)
{
return ShaderInput.GetConnectionStateVariableName(GetVariableNameForSlot(slotId));
}
protected override void CalculateNodeHasError()
{
if (property == null || !owner.properties.Any(x => x == property))
{
owner.AddConcretizationError(objectId, "Property Node has no associated Blackboard property.");
}
else if (property is MultiJsonInternal.UnknownShaderPropertyType)
{
owner.AddValidationError(objectId, "Property is of unknown type, a package may be missing.", Rendering.ShaderCompilerMessageSeverity.Warning);
}
}
public override void UpdatePrecision(List<MaterialSlot> inputSlots)
{
// Get precision from Property
if (property == null)
{
owner.AddConcretizationError(objectId, string.Format("No matching poperty found on owner for node {0}", objectId));
hasError = true;
return;
}
// this node's precision is always controlled by the property precision
precision = property.precision;
graphPrecision = precision.ToGraphPrecision(GraphPrecision.Graph);
concretePrecision = graphPrecision.ToConcrete(owner.graphDefaultConcretePrecision);
}
}
}