Files
ichni_Official/Packages/NB_FX/XuanXuanRenderUtility/Editor/ShaderGUIItems/ShaderGUIItem.cs
SoulliesOfficial 40fa80cd70 NB_FX
2026-07-17 17:46:16 -04:00

518 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
namespace NBShaderEditor
{
public enum MixedBool
{
False = 0,
True = 1,
Mixed = -1
}
public class ShaderGUIItem
{
public const float MinLabelWidth = 120f;
public const float LabelWidthRatio = 0.4f;
public const float GlobalRectXOffset = -15f;
public const float GlobalRectWidthExpansion = 15f;
public const float UnityEditorGUIIndentWidth = 15f;
public const float EditorGUIIndentWidth = 10f;
public const float FoldOutArrowWidth = UnityEditorGUIIndentWidth;
public const float ControlResetGap = 3f;
public const float ControlIndentCompensation = 10f;
public ShaderPropertyInfo PropertyInfo;
public ShaderGUIItem ParentItem;
public List<ShaderGUIItem> ChildrenItemList = new List<ShaderGUIItem>();
public ShaderGUIRootItem RootItem;
public string PropertyName;
public GUIContent GuiContent;
private static int _parentControlDisabledDepth;
public virtual void InitTriggerByChild()//根Child一定要做这个事情
{
if ( PropertyInfo == null && PropertyName != null)
{
PropertyInfo = RootItem.PropertyInfoDic[PropertyName];
}
CheckIsPropertyModified();
}
public ShaderGUIItem(ShaderGUIRootItem rtItem,ShaderGUIItem parentItem=null)
{
RootItem = rtItem;
if (parentItem != null)
{
ParentItem = parentItem;
if (!parentItem.ChildrenItemList.Contains(this))
{
ParentItem.ChildrenItemList.Add(this);
}
}
}
public Rect BaseRect;
public Rect LabelRect;
public Rect ControlRect;
public Rect ResetRect;
private static readonly GUIContent TempHelpContent = new GUIContent();
public static float ResetButtonSize => EditorGUIUtility.singleLineHeight;
#if !UNITY_2022_1_OR_NEWER
private static Material s_ShaderDefaultIntValueMaterial;
#endif
public static int GetShaderPropertyDefaultIntValue(Shader shader, int propertyIndex)
{
#if UNITY_2022_1_OR_NEWER
return shader.GetPropertyDefaultIntValue(propertyIndex);
#else
if (s_ShaderDefaultIntValueMaterial == null ||
s_ShaderDefaultIntValueMaterial.shader != shader)
{
if (s_ShaderDefaultIntValueMaterial != null)
{
UnityEngine.Object.DestroyImmediate(s_ShaderDefaultIntValueMaterial);
}
// Unity 2021.3 has no int default API; use one hidden material as the shader default-value reader.
s_ShaderDefaultIntValueMaterial = new Material(shader)
{
hideFlags = HideFlags.HideAndDontSave
};
}
return s_ShaderDefaultIntValueMaterial.GetInteger(shader.GetPropertyName(propertyIndex));
#endif
}
public virtual void GetRect(bool applyControlIndentCompensation = true)
{
BaseRect = ApplyGlobalRectCompensation(RootItem.GetControlRect());
SplitLineRect(BaseRect, out LabelRect, out ControlRect, out ResetRect, applyControlIndentCompensation);
}
public static Rect ApplyGlobalRectCompensation(Rect rect)
{
rect.x += GlobalRectXOffset;
rect.width = Mathf.Max(0f, rect.width + GlobalRectWidthExpansion);
return rect;
}
public static Rect ApplyLabelIndentWidth(Rect rect)
{
float indentDelta = EditorGUI.indentLevel * (EditorGUIIndentWidth - UnityEditorGUIIndentWidth);
rect.x += indentDelta;
rect.width = Mathf.Max(0f, rect.width - indentDelta);
return rect;
}
public static Rect ApplyDirectLabelIndentWidth(Rect rect)
{
float indentOffset = EditorGUI.indentLevel * EditorGUIIndentWidth;
rect.x += indentOffset;
rect.width = Mathf.Max(0f, rect.width - indentOffset);
return rect;
}
public static float GetEditorLabelTextX(Rect labelRect)
{
return labelRect.x + EditorGUI.indentLevel * UnityEditorGUIIndentWidth;
}
public static float GetLabelWidth(Rect baseRect)
{
return Mathf.Max(MinLabelWidth, baseRect.width * LabelWidthRatio);
}
public static void SplitLineRect(
Rect baseRect,
out Rect labelRect,
out Rect controlRect,
out Rect resetRect,
bool applyControlIndentCompensation = true)
{
float labelWidth = GetLabelWidth(baseRect);
labelRect = baseRect;
labelRect.width = labelWidth;
labelRect = ApplyLabelIndentWidth(labelRect);
Rect controlAndResetRect = baseRect;
controlAndResetRect.x += labelWidth;
controlAndResetRect.width = Mathf.Max(0f, controlAndResetRect.width - labelWidth);
SplitControlAndResetRect(controlAndResetRect, out controlRect, out resetRect, applyControlIndentCompensation);
}
protected static GUIContent LocalizedContent(
string tableName,
string labelKey,
string labelFallback,
string tooltipKey = null,
string tooltipFallback = "")
{
return ShaderGUILocalization.MakeContent(tableName, labelKey, labelFallback, tooltipKey, tooltipFallback);
}
protected static GUIContent LocalizedInspectorContent(string tableName, string key, string fallback, string tip = "")
{
return ShaderGUILocalization.MakeInspectorContent(tableName, key, fallback, tip);
}
protected static string LocalizedText(string tableName, string key, string fallback = "")
{
return ShaderGUILocalization.GetInspectorText(tableName, key, fallback);
}
protected static string[] LocalizedOptions(string tableName, string key, string[] fallback)
{
return ShaderGUILocalization.GetInspectorOptions(tableName, key, fallback);
}
public static void SplitControlAndResetRect(
Rect baseRect,
out Rect controlRect,
out Rect resetRect,
bool applyControlIndentCompensation = true)
{
resetRect = baseRect;
resetRect.x = baseRect.xMax - ResetButtonSize;
resetRect.width = ResetButtonSize;
float controlIndentCompensation = applyControlIndentCompensation ? ControlIndentCompensation : 0f;
controlRect = baseRect;
controlRect.x -= controlIndentCompensation;
controlRect.width = Mathf.Max(0f, baseRect.width - ResetButtonSize - ControlResetGap + controlIndentCompensation);
}
protected Rect LayoutRect(float height = -1f)
{
return RootItem.GetControlRect(height);
}
protected void LayoutSpace(float height = -1f)
{
RootItem.Space(height);
}
protected void DrawLayoutHelpBox(string message, MessageType messageType)
{
TempHelpContent.text = message;
float width = Mathf.Max(1f, EditorGUIUtility.currentViewWidth + GlobalRectWidthExpansion + GlobalRectXOffset);
float height = Mathf.Max(
EditorGUIUtility.singleLineHeight * 2f,
EditorStyles.helpBox.CalcHeight(TempHelpContent, width));
Rect rect = ApplyGlobalRectCompensation(LayoutRect(height));
EditorGUI.HelpBox(rect, message, messageType);
TempHelpContent.text = string.Empty;
}
public virtual void OnGUI()
{
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
bool animatedPropertyScope = BeginAnimatedPropertyBackground(BaseRect, PropertyInfo.Property);
using (new EditorGUIIndentLevelScope(0))
{
DrawController();
}
EndAnimatedPropertyBackground(animatedPropertyScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
OnEndChange();
}
}
DrawResetButton();
DrawBlock();
}
public void DrawResetButton()//如果重写OnGUI一定要记得调用DrawResetButton
{
CheckIsPropertyModified();
_resetButtonContent.text = HasModified ? "R" : "";
_resetButtonStyle = HasModified ? GUI.skin.button : GUI.skin.label;
using (ParentControlDisabledScope())
{
if (GUI.Button(ResetRect, _resetButtonContent, _resetButtonStyle))
{
ExecuteReset();
}
}
}
public virtual void DrawController()
{
}
protected static bool SetFloatIfDifferent(MaterialProperty property, float value)
{
if (property == null || Mathf.Approximately(property.floatValue, value))
{
return false;
}
property.floatValue = value;
return true;
}
protected static bool SetVectorIfDifferent(MaterialProperty property, Vector4 value)
{
if (property == null || Approximately(property.vectorValue, value))
{
return false;
}
property.vectorValue = value;
return true;
}
protected static bool SetColorIfDifferent(MaterialProperty property, Color value)
{
if (property == null)
{
return false;
}
Color current = property.colorValue;
if (Mathf.Approximately(current.r, value.r) &&
Mathf.Approximately(current.g, value.g) &&
Mathf.Approximately(current.b, value.b) &&
Mathf.Approximately(current.a, value.a))
{
return false;
}
property.colorValue = value;
return true;
}
protected readonly struct EditorGUIIndentLevelScope : System.IDisposable
{
private readonly int _indentLevel;
public EditorGUIIndentLevelScope(int indentLevel)
{
_indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = indentLevel;
}
public void Dispose()
{
EditorGUI.indentLevel = _indentLevel;
}
}
protected bool IsParentControlDisabled => _parentControlDisabledDepth > 0;
protected EditorGUI.DisabledScope ParentControlDisabledScope(bool disabled = false)
{
return new EditorGUI.DisabledScope(IsParentControlDisabled || disabled);
}
protected readonly struct InheritedControlDisabledScope : System.IDisposable
{
private readonly bool _disabled;
public InheritedControlDisabledScope(bool disabled)
{
_disabled = disabled;
if (_disabled)
{
_parentControlDisabledDepth++;
}
}
public void Dispose()
{
if (_disabled)
{
_parentControlDisabledDepth = Mathf.Max(0, _parentControlDisabledDepth - 1);
}
}
}
public virtual void DrawBlock()
{
}
public virtual void OnEndChange()
{
CheckIsPropertyModified();
}
private readonly Dictionary<string, string> _animationPropertyPathDic = new Dictionary<string, string>();
protected bool BeginAnimatedPropertyBackground(Rect totalPosition, MaterialProperty property)
{
if (property == null || RootItem?.MatEditor == null)
{
return false;
}
RootItem.MatEditor.BeginAnimatedCheck(totalPosition, property);
return true;
}
protected void EndAnimatedPropertyBackground(bool scopeActive)
{
if (scopeActive && RootItem?.MatEditor != null)
{
RootItem.MatEditor.EndAnimatedCheck();
}
}
public bool IsPropertyAnimated(string propertyName, params string[] componentNames)
{
if (propertyName == null) return false;
if (AnimationMode.InAnimationMode())
{
foreach (var r in RootItem.RenderersUsingThisMaterial)
{
if (componentNames != null && componentNames.Length > 0)
{
foreach (string componentName in componentNames)
{
if (AnimationMode.IsPropertyAnimated(r, GetAnimationPropertyPath(propertyName, componentName)))
{
return true;
}
}
}
else if (AnimationMode.IsPropertyAnimated(r, GetAnimationPropertyPath(propertyName, string.Empty)))
{
return true;
}
}
}
return false;
}
private string GetAnimationPropertyPath(string propertyName, string componentName)
{
string key = string.IsNullOrEmpty(componentName) ? propertyName : propertyName + "." + componentName;
if (!_animationPropertyPathDic.TryGetValue(key, out string propertyPath))
{
propertyPath = string.IsNullOrEmpty(componentName)
? "material." + propertyName
: "material." + propertyName + "." + componentName;
_animationPropertyPathDic.Add(key, propertyPath);
}
return propertyPath;
}
#region ResetLogic
private GUIContent _resetButtonContent = new GUIContent("R","重置当前属性及子集属性(如有)");
private GUIStyle _resetButtonStyle;
public bool HasModified = false;
public bool PropertyIsDefaultValue = true;
public virtual void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)//只有自身Reset了才需要查询自己的状态是否正确
{
bool isDefaultValue = true;
if (PropertyInfo != null)
{
switch (ShaderGUIUnityCompat.GetPropertyType(PropertyInfo.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
float defaultValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
isDefaultValue = Mathf.Approximately(PropertyInfo.Property.floatValue, defaultValue);
break;
case ShaderPropertyType.Color:
Vector4 defaultColor = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
isDefaultValue = Approximately(PropertyInfo.Property.colorValue, defaultColor);
break;
case ShaderPropertyType.Vector:
Vector4 defaultVector = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
isDefaultValue = Approximately(PropertyInfo.Property.vectorValue, defaultVector);
break;
}
}
else
{
isDefaultValue = true;//如果没有Property则是看子集的情况
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;//如果状态没有改变,就不需要做任何操作
}
else
{
PropertyIsDefaultValue = isDefaultValue;
}
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public virtual void ExecuteReset(bool isCallByParent = false)
{
if (PropertyInfo != null)
{
switch (ShaderGUIUnityCompat.GetPropertyType(PropertyInfo.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
float defaultValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
PropertyInfo.Property.floatValue = defaultValue;
break;
case ShaderPropertyType.Color:
PropertyInfo.Property.colorValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
break;
case ShaderPropertyType.Vector:
PropertyInfo.Property.vectorValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
break;
}
PropertyIsDefaultValue = true;
}
foreach (var childItem in ChildrenItemList)
{
childItem.ExecuteReset(true);
}
HasModified = false;
if (!isCallByParent)//直接由用户触发重置
{
ParentItem?.CheckIsPropertyModified(true);
}
}
protected static bool Approximately(Vector4 a, Vector4 b)
{
return Mathf.Approximately(a.x, b.x) &&
Mathf.Approximately(a.y, b.y) &&
Mathf.Approximately(a.z, b.z) &&
Mathf.Approximately(a.w, b.w);
}
#endregion
}
}