using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEditor.Graphing; using UnityEditor.ShaderGraph.Drawing; using UnityEditor.ShaderGraph.Serialization; namespace UnityEditor.ShaderGraph { [Serializable] [Title("Utility", "Dropdown")] class DropdownNode : AbstractMaterialNode, IOnAssetEnabled, IGeneratesBodyCode, IShaderInputObserver { internal const int k_MinEnumEntries = 2; public DropdownNode() { UpdateNodeAfterDeserialization(); } [SerializeField] JsonRef m_Dropdown; public ShaderDropdown dropdown { get { return m_Dropdown; } set { if (m_Dropdown == value) return; m_Dropdown = value; UpdateNode(); Dirty(ModificationScope.Topological); } } public override bool canSetPrecision => false; public override bool hasPreview => true; public const int OutputSlotId = 0; public override bool allowedInMainGraph { get => false; } public void UpdateNodeDisplayName(string newDisplayName) { MaterialSlot foundSlot = FindSlot(OutputSlotId); if (foundSlot != null) foundSlot.displayName = newDisplayName; } public void OnEnable() { UpdateNode(); } public void UpdateNode() { name = dropdown.displayName; UpdatePorts(); } void UpdatePorts() { // Get slots List inputSlots = new List(); GetInputSlots(inputSlots); // Store the edges Dictionary> edgeDict = new Dictionary>(); foreach (MaterialSlot slot in inputSlots) edgeDict.Add(slot, (List)slot.owner.owner.GetEdges(slot.slotReference)); // Remove old slots for (int i = 0; i < inputSlots.Count; i++) { RemoveSlot(inputSlots[i].id); } // Add output slot AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero)); // Add input slots int[] slotIds = new int[dropdown.entries.Count + 1]; slotIds[dropdown.entries.Count] = OutputSlotId; for (int i = 0; i < dropdown.entries.Count; i++) { // Get slot based on entry id MaterialSlot slot = inputSlots.Where(x => x.id == dropdown.entries[i].id && x.RawDisplayName() == dropdown.entries[i].displayName && x.shaderOutputName == dropdown.entries[i].displayName).FirstOrDefault(); if (slot == null) { slot = new DynamicVectorMaterialSlot(dropdown.entries[i].id, dropdown.entries[i].displayName, dropdown.entries[i].displayName, SlotType.Input, Vector4.zero); } AddSlot(slot); slotIds[i] = dropdown.entries[i].id; } RemoveSlotsNameNotMatching(slotIds); // Reconnect the edges foreach (KeyValuePair> entry in edgeDict) { foreach (IEdge edge in entry.Value) { owner.Connect(edge.outputSlot, edge.inputSlot); } } ValidateNode(); } public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode) { var outputSlot = FindOutputSlot(OutputSlotId); bool isGeneratingSubgraph = owner.isSubGraph && (generationMode != GenerationMode.Preview); if (generationMode == GenerationMode.Preview || !isGeneratingSubgraph) { sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)};")); var value = GetSlotValue(GetSlotIdForActiveSelection(), generationMode); sb.AppendLine(string.Format($"{GetVariableNameForSlot(OutputSlotId)} = {value};")); } else { // Iterate all entries in the dropdown for (int i = 0; i < dropdown.entries.Count; i++) { if (i == 0) { sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)};")); sb.AppendLine($"if ({m_Dropdown.value.referenceName} == {i})"); } else { sb.AppendLine($"else if ({m_Dropdown.value.referenceName} == {i})"); } { sb.AppendLine("{"); sb.IncreaseIndent(); var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair(dropdown, i)), generationMode); sb.AppendLine(string.Format($"{GetVariableNameForSlot(OutputSlotId)} = {value};")); sb.DecreaseIndent(); sb.AppendLine("}"); } if (i == dropdown.entries.Count - 1) { sb.AppendLine($"else"); sb.AppendLine("{"); sb.IncreaseIndent(); var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair(dropdown, 0)), generationMode); sb.AppendLine(string.Format($"{GetVariableNameForSlot(OutputSlotId)} = {value};")); sb.DecreaseIndent(); sb.AppendLine("}"); } } } } public int GetSlotIdForPermutation(KeyValuePair permutation) { return permutation.Key.entries[permutation.Value].id; } public int GetSlotIdForActiveSelection() { return dropdown.entries[dropdown.value].id; } protected override void CalculateNodeHasError() { if (dropdown == null || !owner.dropdowns.Any(x => x == dropdown)) { owner.AddConcretizationError(objectId, "Dropdown Node has no associated dropdown."); hasError = true; } } public void OnShaderInputUpdated(ModificationScope modificationScope) { UpdateNode(); Dirty(modificationScope); if(modificationScope == ModificationScope.Layout) UpdateNodeDisplayName(dropdown.displayName); } } }