NB_FX
This commit is contained in:
@@ -0,0 +1,517 @@
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user