This commit is contained in:
SoulliesOfficial
2026-07-17 17:46:16 -04:00
parent b00ac27e3a
commit 40fa80cd70
1178 changed files with 51090 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class BlockItem : ShaderGUIItem
{
protected readonly ShaderGUIRootItem SharedRootItem;
private readonly ShaderGUIFoldOutHelper _foldOutHelper;
private readonly Func<GUIContent> _contentProvider;
protected virtual GUIStyle TitleStyle => EditorStyles.label;
protected virtual bool DrawSeparatorLine => false;
public string FoldOutPropertyName { get; }
public BlockItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string foldOutPropertyName,
Func<GUIContent> contentProvider) : base(rootItem, parentItem)
{
SharedRootItem = rootItem;
FoldOutPropertyName = foldOutPropertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
GuiContent = _contentProvider();
_foldOutHelper = new ShaderGUIFoldOutHelper(rootItem, foldOutPropertyName);
}
public override void OnGUI()
{
DrawLeadingSpace();
GetRect();
_foldOutHelper.DrawFoldOut(LabelRect);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent, TitleStyle);
}
DrawResetButton();
EditorGUI.indentLevel++;
if (_foldOutHelper.BeginFadeGroup())
{
DrawBlock();
}
_foldOutHelper.EndFadedGroup();
EditorGUI.indentLevel--;
if (DrawSeparatorLine)
{
Rect rect = ApplyGlobalRectCompensation(LayoutRect(1));
EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));
}
}
protected virtual void DrawLeadingSpace()
{
}
public override void DrawBlock()
{
for (int i = 0; i < ChildrenItemList.Count; i++)
{
ChildrenItemList[i].OnGUI();
}
}
}
public class BigBlockItem : BlockItem
{
protected override GUIStyle TitleStyle => EditorStyles.boldLabel;
protected override bool DrawSeparatorLine => true;
public BigBlockItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string foldOutPropertyName,
Func<GUIContent> contentProvider) : base(rootItem, parentItem, foldOutPropertyName, contentProvider)
{
}
protected override void DrawLeadingSpace()
{
LayoutSpace();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d07eceb7cee2d8b46ab0f6f62c4f82e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,190 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ColorItem : ShaderGUIItem
{
private const float ColorFieldLeftInset = EditorGUIIndentWidth;
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public ColorItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect(false);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
Color color = PropertyInfo.Property.colorValue;
bool hdr = ShaderGUIUnityCompat.HasHdrFlag(PropertyInfo.Property);
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Rect colorRect = GetLabeledColorFieldRect(ControlRect);
bool animatedScope = BeginAnimatedPropertyBackground(colorRect, PropertyInfo.Property);
using (new EditorGUIIndentLevelScope(0))
{
color = EditorGUI.ColorField(colorRect, GUIContent.none, color, true, true, hdr);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
PropertyInfo.Property.colorValue = color;
OnEndChange();
}
}
DrawResetButton();
DrawBlock();
}
internal static Rect GetNoLabelColorFieldRect(Rect rect)
{
rect.x += ColorFieldLeftInset;
rect.width = Mathf.Max(0f, rect.width - ColorFieldLeftInset);
return rect;
}
internal static Rect GetLabeledColorFieldRect(Rect rect)
{
float leftPadding = EditorStyles.colorField.padding.left + ColorFieldLeftInset +1f;
rect.x -= leftPadding;
rect.width += leftPadding;
return rect;
}
}
public class ColorLineItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly bool _showLabel;
private readonly Func<bool> _isVisible;
public ColorLineItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
bool showLabel,
Func<GUIContent> contentProvider = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_showLabel = showLabel;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _showLabel ? _contentProvider() : GUIContent.none;
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
Draw(ApplyGlobalRectCompensation(LayoutRect()));
DrawBlock();
}
public void Draw(Rect rect)
{
BaseRect = rect;
if (_showLabel)
{
SplitLineRect(rect, out LabelRect, out ControlRect, out ResetRect, false);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
}
else
{
LabelRect = new Rect(rect.x, rect.y, 0f, rect.height);
SplitControlAndResetRect(rect, out ControlRect, out ResetRect, false);
}
MaterialProperty property = PropertyInfo.Property;
Color color = property.colorValue;
bool hdr = ShaderGUIUnityCompat.HasHdrFlag(property);
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Rect colorRect = _showLabel
? ColorItem.GetLabeledColorFieldRect(ControlRect)
: ColorItem.GetNoLabelColorFieldRect(ControlRect);
bool animatedScope = BeginAnimatedPropertyBackground(colorRect, property);
using (new EditorGUIIndentLevelScope(0))
{
color = EditorGUI.ColorField(colorRect, GUIContent.none, color, true, true, hdr);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
property.colorValue = color;
OnEndChange();
}
}
DrawResetButton();
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = true;
if (PropertyInfo != null)
{
Vector4 defaultColor = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
isDefaultValue = !PropertyInfo.Property.hasMixedValue &&
Approximately(PropertyInfo.Property.colorValue, defaultColor);
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 31b44f82d6184d46b683b144f9b5f517
timeCreated: 1777032000

View File

@@ -0,0 +1,533 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
namespace NBShaderEditor
{
public class GradientItem : ShaderGUIItem
{
private static bool s_UndoRedoRegistered;
private static int s_UndoRedoVersion;
private readonly Func<GUIContent> _contentProvider;
private readonly string[] _colorPropertyNames;
private readonly string[] _alphaPropertyNames;
private readonly int _maxCount;
private readonly bool _hdr;
private readonly ColorSpace _colorSpace;
private readonly Func<bool> _isVisible;
private ShaderPropertyInfo[] _colorPropertyInfos;
private ShaderPropertyInfo[] _alphaPropertyInfos;
private readonly Gradient _gradient = new Gradient();
private GradientColorKey[] _colorKeys = Array.Empty<GradientColorKey>();
private GradientAlphaKey[] _alphaKeys = Array.Empty<GradientAlphaKey>();
private bool _gradientCacheDirty = true;
private int _lastUndoRedoVersion = -1;
public GradientItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string countPropertyName,
int maxCount,
string[] colorPropertyNames,
string[] alphaPropertyNames,
Func<GUIContent> contentProvider,
bool hdr = false,
ColorSpace colorSpace = ColorSpace.Gamma,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = countPropertyName;
_maxCount = Mathf.Max(2, maxCount);
_colorPropertyNames = colorPropertyNames ?? Array.Empty<string>();
_alphaPropertyNames = alphaPropertyNames ?? Array.Empty<string>();
_contentProvider = contentProvider ?? (() => GUIContent.none);
_hdr = hdr;
_colorSpace = colorSpace;
_isVisible = isVisible;
GuiContent = _contentProvider();
EnsureUndoRedoRegistered();
CacheProperties();
InitTriggerByChild();
}
private static void EnsureUndoRedoRegistered()
{
if (s_UndoRedoRegistered)
{
return;
}
Undo.undoRedoPerformed += OnUndoRedoPerformed;
s_UndoRedoRegistered = true;
}
private static void OnUndoRedoPerformed()
{
s_UndoRedoVersion++;
}
private void CacheProperties()
{
_colorPropertyInfos = new ShaderPropertyInfo[_colorPropertyNames.Length];
for (int i = 0; i < _colorPropertyNames.Length; i++)
{
if (RootItem.PropertyInfoDic.TryGetValue(_colorPropertyNames[i], out ShaderPropertyInfo info))
{
_colorPropertyInfos[i] = info;
}
}
_alphaPropertyInfos = new ShaderPropertyInfo[_alphaPropertyNames.Length];
for (int i = 0; i < _alphaPropertyNames.Length; i++)
{
if (RootItem.PropertyInfoDic.TryGetValue(_alphaPropertyNames[i], out ShaderPropertyInfo info))
{
_alphaPropertyInfos[i] = info;
}
}
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
Gradient gradient = GetCachedGradient();
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = GradientPropertyHasMixedValue();
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, PropertyInfo.Property);
using (new EditorGUIIndentLevelScope(0))
{
gradient = EditorGUI.GradientField(ControlRect, GUIContent.none, gradient, _hdr, _colorSpace);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
WriteGradient(gradient);
MarkGradientCacheDirty();
OnEndChange();
}
}
DrawResetButton();
}
private Gradient GetCachedGradient()
{
if (_lastUndoRedoVersion != s_UndoRedoVersion)
{
_lastUndoRedoVersion = s_UndoRedoVersion;
MarkGradientCacheDirty();
}
if (_gradientCacheDirty)
{
ReadGradient();
_gradientCacheDirty = false;
GradientReflectionHelper.RefreshGradientData();
}
return _gradient;
}
private void MarkGradientCacheDirty()
{
_gradientCacheDirty = true;
}
private void ReadGradient()
{
bool hasColorProperties = _colorPropertyInfos.Length > 0;
bool hasAlphaProperties = _alphaPropertyInfos.Length > 0;
bool isBlackAndWhiteGradient = !hasColorProperties && hasAlphaProperties;
GetGradientKeyCounts(out int colorKeyCount, out int alphaKeyCount);
if (_colorKeys.Length != colorKeyCount)
{
_colorKeys = new GradientColorKey[colorKeyCount];
}
if (_alphaKeys.Length != alphaKeyCount)
{
_alphaKeys = new GradientAlphaKey[alphaKeyCount];
}
for (int i = 0; i < colorKeyCount; i++)
{
Color color = Color.white;
float colorTime = colorKeyCount == 1 ? 0f : i / (float)(colorKeyCount - 1);
if (isBlackAndWhiteGradient)
{
TryReadBlackAndWhiteKey(i, ref color, ref colorTime);
}
else if (i < _colorPropertyInfos.Length && _colorPropertyInfos[i] != null)
{
Color packed = _colorPropertyInfos[i].Property.colorValue;
color = packed;
colorTime = packed.a;
}
_colorKeys[i] = new GradientColorKey(color, colorTime);
}
for (int i = 0; i < alphaKeyCount; i++)
{
float alpha = 1f;
float alphaTime = alphaKeyCount == 1 ? 0f : i / (float)(alphaKeyCount - 1);
if (!isBlackAndWhiteGradient && hasAlphaProperties)
{
TryReadAlphaKey(i, ref alpha, ref alphaTime);
}
_alphaKeys[i] = new GradientAlphaKey(alpha, alphaTime);
}
_gradient.SetKeys(_colorKeys, _alphaKeys);
}
private void GetGradientKeyCounts(out int colorKeyCount, out int alphaKeyCount)
{
int countValue = PropertyInfo.Property.intValue;
bool hasColorProperties = _colorPropertyInfos.Length > 0;
bool hasAlphaProperties = _alphaPropertyInfos.Length > 0;
if (hasColorProperties && hasAlphaProperties)
{
colorKeyCount = countValue & 0xFFFF;
alphaKeyCount = countValue >> 16;
}
else
{
colorKeyCount = countValue;
alphaKeyCount = 2;
}
}
private void TryReadBlackAndWhiteKey(int index, ref Color color, ref float time)
{
int vectorIndex = index / 2;
int componentIndex = index % 2;
if (vectorIndex >= _alphaPropertyInfos.Length || _alphaPropertyInfos[vectorIndex] == null)
{
return;
}
Vector4 packed = _alphaPropertyInfos[vectorIndex].Property.vectorValue;
float value = componentIndex == 0 ? packed.x : packed.z;
time = componentIndex == 0 ? packed.y : packed.w;
color = new Color(value, value, value, 1f);
}
private void TryReadAlphaKey(int index, ref float alpha, ref float time)
{
int vectorIndex = index / 2;
int componentIndex = index % 2;
if (vectorIndex >= _alphaPropertyInfos.Length || _alphaPropertyInfos[vectorIndex] == null)
{
return;
}
Vector4 packed = _alphaPropertyInfos[vectorIndex].Property.vectorValue;
if (componentIndex == 0)
{
alpha = packed.x;
time = packed.y;
}
else
{
alpha = packed.z;
time = packed.w;
}
}
private void WriteGradient(Gradient gradient)
{
bool hasColorProperties = _colorPropertyInfos.Length > 0;
bool hasAlphaProperties = _alphaPropertyInfos.Length > 0;
bool isBlackAndWhiteGradient = !hasColorProperties && hasAlphaProperties;
int countPropertyValue = PropertyInfo.Property.intValue;
if (isBlackAndWhiteGradient)
{
int finalColorKeyCount = gradient.colorKeys.Length;
if (finalColorKeyCount <= _maxCount)
{
WriteBlackAndWhiteGradient(gradient, finalColorKeyCount);
PropertyInfo.Property.intValue = finalColorKeyCount;
}
return;
}
if (hasColorProperties)
{
int finalColorKeyCount = gradient.colorKeys.Length;
if (finalColorKeyCount <= _maxCount)
{
WriteColorKeys(gradient, finalColorKeyCount);
countPropertyValue &= 0xFFFF << 16;
countPropertyValue |= finalColorKeyCount;
}
}
if (!isBlackAndWhiteGradient && hasAlphaProperties)
{
int finalAlphaKeyCount = gradient.alphaKeys.Length;
if (finalAlphaKeyCount <= _maxCount)
{
WriteAlphaKeys(gradient, finalAlphaKeyCount);
countPropertyValue &= 0xFFFF;
countPropertyValue |= finalAlphaKeyCount << 16;
}
}
PropertyInfo.Property.intValue = countPropertyValue;
}
private void WriteColorKeys(Gradient gradient, int colorKeyCount)
{
for (int i = 0; i < colorKeyCount && i < _colorPropertyInfos.Length; i++)
{
if (_colorPropertyInfos[i] == null)
{
continue;
}
GradientColorKey key = gradient.colorKeys[i];
_colorPropertyInfos[i].Property.colorValue = new Color(key.color.r, key.color.g, key.color.b, key.time);
}
}
private void WriteBlackAndWhiteGradient(Gradient gradient, int colorKeyCount)
{
int vectorCount = Mathf.CeilToInt(colorKeyCount / 2f);
for (int i = 0; i < vectorCount && i < _alphaPropertyInfos.Length; i++)
{
if (_alphaPropertyInfos[i] == null)
{
continue;
}
Vector4 value = Vector4.zero;
GradientColorKey key0 = gradient.colorKeys[i * 2];
value.x = key0.color.r;
value.y = key0.time;
if (i * 2 + 1 < colorKeyCount)
{
GradientColorKey key1 = gradient.colorKeys[i * 2 + 1];
value.z = key1.color.r;
value.w = key1.time;
}
_alphaPropertyInfos[i].Property.vectorValue = value;
}
}
private void WriteAlphaKeys(Gradient gradient, int alphaKeyCount)
{
int vectorCount = Mathf.CeilToInt(alphaKeyCount / 2f);
for (int i = 0; i < vectorCount && i < _alphaPropertyInfos.Length; i++)
{
if (_alphaPropertyInfos[i] == null)
{
continue;
}
Vector4 value = Vector4.zero;
GradientAlphaKey key0 = gradient.alphaKeys[i * 2];
value.x = key0.alpha;
value.y = key0.time;
if (i * 2 + 1 < alphaKeyCount)
{
GradientAlphaKey key1 = gradient.alphaKeys[i * 2 + 1];
value.z = key1.alpha;
value.w = key1.time;
}
_alphaPropertyInfos[i].Property.vectorValue = value;
}
}
private bool GradientPropertyHasMixedValue()
{
if (PropertyInfo == null || PropertyInfo.Property == null)
{
return false;
}
GetGradientKeyCounts(out int colorKeyCount, out int alphaKeyCount);
bool hasMixedValue = PropertyInfo.Property.hasMixedValue;
for (int i = 0; i < colorKeyCount && i < _colorPropertyInfos.Length; i++)
{
hasMixedValue |= _colorPropertyInfos[i]?.Property.hasMixedValue == true;
}
int alphaPropertyCount = Mathf.CeilToInt(alphaKeyCount / 2f);
for (int i = 0; i < alphaPropertyCount && i < _alphaPropertyInfos.Length; i++)
{
hasMixedValue |= _alphaPropertyInfos[i]?.Property.hasMixedValue == true;
}
return hasMixedValue;
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = IsCountDefault();
for (int i = 0; i < _colorPropertyInfos.Length; i++)
{
isDefaultValue &= IsDefault(_colorPropertyInfos[i]);
}
for (int i = 0; i < _alphaPropertyInfos.Length; i++)
{
isDefaultValue &= IsDefault(_alphaPropertyInfos[i]);
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
ResetCountProperty();
for (int i = 0; i < _colorPropertyInfos.Length; i++)
{
ResetProperty(_colorPropertyInfos[i]);
}
for (int i = 0; i < _alphaPropertyInfos.Length; i++)
{
ResetProperty(_alphaPropertyInfos[i]);
}
PropertyIsDefaultValue = true;
HasModified = false;
MarkGradientCacheDirty();
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
private bool IsCountDefault()
{
if (PropertyInfo == null || PropertyInfo.Property == null || PropertyInfo.Property.hasMixedValue)
{
return false;
}
return PropertyInfo.Property.intValue == GetDefaultInt(PropertyInfo);
}
private void ResetCountProperty()
{
if (PropertyInfo?.Property == null)
{
return;
}
PropertyInfo.Property.intValue = GetDefaultInt(PropertyInfo);
}
private bool IsDefault(ShaderPropertyInfo info)
{
if (info == null || info.Property == null || info.Property.hasMixedValue)
{
return false;
}
switch (ShaderGUIUnityCompat.GetPropertyType(info.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
return Mathf.Approximately(info.Property.floatValue, RootItem.Shader.GetPropertyDefaultFloatValue(info.Index));
case ShaderPropertyType.Int:
return info.Property.intValue == GetDefaultInt(info);
case ShaderPropertyType.Color:
return Approximately(info.Property.colorValue, RootItem.Shader.GetPropertyDefaultVectorValue(info.Index));
case ShaderPropertyType.Vector:
return Approximately(info.Property.vectorValue, RootItem.Shader.GetPropertyDefaultVectorValue(info.Index));
default:
return Mathf.Approximately(info.Property.floatValue, GetDefaultScalar(info));
}
}
private void ResetProperty(ShaderPropertyInfo info)
{
if (info == null || info.Property == null)
{
return;
}
switch (ShaderGUIUnityCompat.GetPropertyType(info.Property))
{
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
info.Property.floatValue = RootItem.Shader.GetPropertyDefaultFloatValue(info.Index);
break;
case ShaderPropertyType.Int:
info.Property.intValue = GetDefaultInt(info);
break;
case ShaderPropertyType.Color:
info.Property.colorValue = RootItem.Shader.GetPropertyDefaultVectorValue(info.Index);
break;
case ShaderPropertyType.Vector:
info.Property.vectorValue = RootItem.Shader.GetPropertyDefaultVectorValue(info.Index);
break;
default:
info.Property.floatValue = GetDefaultScalar(info);
break;
}
}
private float GetDefaultScalar(ShaderPropertyInfo info)
{
try
{
return RootItem.Shader.GetPropertyDefaultFloatValue(info.Index);
}
catch (ArgumentException)
{
return 2f;
}
}
private int GetDefaultInt(ShaderPropertyInfo info)
{
try
{
return GetShaderPropertyDefaultIntValue(RootItem.Shader, info.Index);
}
catch (ArgumentException)
{
return Mathf.RoundToInt(GetDefaultScalar(info));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17810b41ec32488faa9fbceaaf8b8fd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using UnityEditor;
namespace NBShaderEditor
{
public class HelpBoxItem : ShaderGUIItem
{
private readonly MessageType _messageType;
private readonly Func<string> _messageProvider;
private readonly Func<bool> _isVisible;
public HelpBoxItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
Func<string> messageProvider,
MessageType messageType = MessageType.Info,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
_messageProvider = messageProvider ?? (() => string.Empty);
_messageType = messageType;
_isVisible = isVisible;
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
using (ParentControlDisabledScope())
{
DrawLayoutHelpBox(_messageProvider(), _messageType);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c516e0fb3014df8b96bce4e0925cb45
timeCreated: 1777032000

View File

@@ -0,0 +1,51 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class RenderQueueItem : ShaderGUIFloatItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Action _onQueueChanged;
private readonly Func<bool> _isVisible;
public RenderQueueItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Action onQueueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_onQueueChanged = onQueueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void OnEndChange()
{
base.OnEndChange();
_onQueueChanged?.Invoke();
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
_onQueueChanged?.Invoke();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5caca15eb8794602ae6029d798cb5f1f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class SectionLabelItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public SectionLabelItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
LayoutSpace();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(ApplyGlobalRectCompensation(LayoutRect()), GuiContent, EditorStyles.boldLabel);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75c41d424c5e4203992222069d060bc6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,394 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ShaderGUIFloatItem:ShaderGUIItem
{
private readonly Func<bool> _isVisible;
public ShaderGUIFloatItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem, Func<bool> isVisible = null) :
base(rootItem, parentItem: parentItem)
{
_isVisible = isVisible;
base.InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void DrawController()
{
float value = DraggableLabelFloat.Handle(LabelRect, PropertyInfo.Property.floatValue, sensitivity: -1f);//拖动Label控件可以操作Float参数
value = EditorGUI.FloatField(ControlRect, value);
SetFloatIfDifferent(PropertyInfo.Property, value);
}
}
public class ShaderGUISliderItem:ShaderGUIItem
{
public float Min = 0;
public float Max = 1;
public string RangePropertyName;
ShaderPropertyInfo _rangePropertyInfo;
private readonly Func<bool> _isVisible;
private const float SliderFrontGap = 5f;
private const float SliderBackGap = 2f;
private const float RangeFieldWidth = 30f;
public override void InitTriggerByChild()
{
if (RangePropertyName != null)
{
_rangePropertyInfo = RootItem.PropertyInfoDic[RangePropertyName];
Min = _rangePropertyInfo.Property.vectorValue.x;
Max = _rangePropertyInfo.Property.vectorValue.y;
}
base.InitTriggerByChild();
}
public ShaderGUISliderItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem, Func<bool> isVisible = null) :
base(rootItem, parentItem: parentItem)
{
_isVisible = isVisible;
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
if (_rangePropertyInfo == null)
{
base.OnGUI();
return;
}
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
using (new EditorGUIIndentLevelScope(0))
{
DrawController();
}
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
OnEndChange();
}
}
DrawResetButton();
DrawBlock();
}
public override void DrawController()
{
if (_rangePropertyInfo != null)
{
Rect minRect = ControlRect;
minRect.width = RangeFieldWidth;
Rect maxRect = ControlRect;
maxRect.x = ControlRect.xMax - RangeFieldWidth;
maxRect.width = RangeFieldWidth;
Rect sliderRect = ControlRect;
sliderRect.x = minRect.xMax + SliderFrontGap;
sliderRect.width = Mathf.Max(0f, maxRect.x - sliderRect.x - SliderBackGap + EditorGUI.indentLevel * UnityEditorGUIIndentWidth);
RangeVecHasMixedValue(out bool minValueHasMixed,out bool maxValueHasMixed);
Vector4 rangeVector = _rangePropertyInfo.Property.vectorValue;
float min = rangeVector.x;
float max = rangeVector.y;
EditorGUI.showMixedValue = minValueHasMixed;
bool minAnimatedScope = BeginAnimatedPropertyBackground(minRect, _rangePropertyInfo.Property);
min = EditorGUI.FloatField(minRect, min);
EndAnimatedPropertyBackground(minAnimatedScope);
EditorGUI.showMixedValue = maxValueHasMixed;
bool maxAnimatedScope = BeginAnimatedPropertyBackground(maxRect, _rangePropertyInfo.Property);
max = EditorGUI.FloatField(maxRect, max);
EndAnimatedPropertyBackground(maxAnimatedScope);
rangeVector.x = min;
rangeVector.y = max;
SetVectorIfDifferent(_rangePropertyInfo.Property, rangeVector);
float sliderMin = Mathf.Min(min, max);
float sliderMax = Mathf.Max(min, max);
float value = DraggableLabelFloat.Handle(
LabelRect,
PropertyInfo.Property.floatValue,
DraggableLabelFloat.GetSensitivityByRange(sliderMin, sliderMax),
sliderMin,
sliderMax);
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
bool sliderAnimatedScope = BeginAnimatedPropertyBackground(sliderRect, PropertyInfo.Property);
value = SliderNoIndent(sliderRect, value, sliderMin, sliderMax);
EndAnimatedPropertyBackground(sliderAnimatedScope);
SetFloatIfDifferent(PropertyInfo.Property, Mathf.Clamp(value, sliderMin, sliderMax));
EditorGUI.showMixedValue = false;
}
else
{
float value = DraggableLabelFloat.Handle(
LabelRect,
PropertyInfo.Property.floatValue,
DraggableLabelFloat.GetSensitivityByRange(Min, Max),
Min,
Max);
value = EditorGUI.Slider(ControlRect, value,Min,Max);
SetFloatIfDifferent(PropertyInfo.Property, value);
}
}
private static float SliderNoIndent(Rect rect, float value, float min, float max)
{
int indentLevel = EditorGUI.indentLevel;
try
{
EditorGUI.indentLevel = 0;
return EditorGUI.Slider(rect, value, min, max);
}
finally
{
EditorGUI.indentLevel = indentLevel;
}
}
void RangeVecHasMixedValue( out bool minValueHasMixed, out bool maxValueHasMixed)
{
minValueHasMixed = false;
maxValueHasMixed = false;
if (RootItem.Mats.Count > 1)
{
MaterialProperty rangeProperty = _rangePropertyInfo.Property;
float minValue = 0;
float maxValue = 0;
for (int i = 0; i < RootItem.Mats.Count; i++)
{
Vector4 rangeVec = RootItem.Mats[i].GetVector(RangePropertyName);
if (i == 0)
{
minValue = rangeVec.x;
maxValue = rangeVec.y;
}
else
{
if (!Mathf.Approximately(minValue, rangeVec.x))
{
minValueHasMixed = true;
}
if (!Mathf.Approximately(maxValue, rangeVec.y))
{
maxValueHasMixed = true;
}
}
}
}
}
bool RangePropIsDefaultValue()
{
MaterialProperty rangeProperty = _rangePropertyInfo.Property;
return rangeProperty.vectorValue == RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index) && !rangeProperty.hasMixedValue;
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (RangePropertyName != null)
{
if (!isCallByChild) //只有自身Reset了才需要查询自己的状态是否正确
{
bool isDefaultValue = true;
float defaultValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
isDefaultValue = Mathf.Approximately(PropertyInfo.Property.floatValue, defaultValue);
Vector4 defaultRangeVec = RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
isDefaultValue &= _rangePropertyInfo.Property.vectorValue == defaultRangeVec;
base.CheckIsPropertyModified(isCallByChild);
if (isDefaultValue == PropertyIsDefaultValue)
{
return; //如果状态没有改变,就不需要做任何操作
}
else
{
PropertyIsDefaultValue = isDefaultValue;
}
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
else
{
base.CheckIsPropertyModified(isCallByChild);
}
}
public override void ExecuteReset(bool isCallByParent = false)
{
if (RangePropertyName != null)
{
PropertyInfo.Property.floatValue = RootItem.Shader.GetPropertyDefaultFloatValue(PropertyInfo.Index);
_rangePropertyInfo.Property.vectorValue =
RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
PropertyIsDefaultValue = true;
foreach (var childItem in ChildrenItemList)
{
childItem.ExecuteReset(true);
}
HasModified = false;
if (!isCallByParent) //直接由用户触发重置
{
ParentItem?.CheckIsPropertyModified(true);
}
}
else
{
base.ExecuteReset(isCallByParent);
}
}
}
//用于滑动控制Label的方案。
// DraggableLabelFloat.cs
public static class DraggableLabelFloat
{
// 为每个控件实例缓存一次拖拽状态
private static readonly System.Collections.Generic.Dictionary<int, DragState> s_Drag =
new System.Collections.Generic.Dictionary<int, DragState>();
private class DragState
{
public float startValue;
public float dragX; // 累计水平位移(单位:像素)
}
public static float Handle(Rect labelRect, float value, float sensitivity = -1f, float? min = null, float? max = null)
{
if (!GUI.enabled)
{
return value;
}
EditorGUIUtility.AddCursorRect(labelRect, MouseCursor.SlideArrow);
int id = GUIUtility.GetControlID(FocusType.Passive, labelRect);
Event e = Event.current;
switch (e.GetTypeForControl(id))
{
case EventType.MouseDown:
if (labelRect.Contains(e.mousePosition) && e.button == 0)
{
GUIUtility.hotControl = id;
e.Use();
EditorGUIUtility.SetWantsMouseJumping(1);
s_Drag[id] = new DragState
{
startValue = value,
dragX = 0f
};
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id && s_Drag.TryGetValue(id, out var st))
{
// 关键:使用 Event.delta 累加位移,兼容越界/跳回
st.dragX += e.delta.x;
float baseSens = sensitivity > 0f
? sensitivity
: 0.003f * Mathf.Max(1f, Mathf.Abs(st.startValue));
float modifier = 1f;
if (e.shift) modifier *= 0.1f;
if (e.control || e.command) modifier *= 10f;
float accel = 1f + 0.15f * Mathf.Clamp01(Mathf.Abs(st.dragX) / 50f);
float newValue = st.startValue + st.dragX * baseSens * modifier * accel;
if (min.HasValue) newValue = Mathf.Max(min.Value, newValue);
if (max.HasValue) newValue = Mathf.Min(max.Value, newValue);
if (!float.IsNaN(newValue) && !float.IsInfinity(newValue) && !Mathf.Approximately(newValue, value))
{
GUI.changed = true;
value = newValue;
}
e.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
s_Drag.Remove(id);
e.Use();
}
break;
case EventType.KeyDown:
// 可选ESC 取消拖拽并还原
if (GUIUtility.hotControl == id && e.keyCode == KeyCode.Escape && s_Drag.TryGetValue(id, out var st2))
{
value = st2.startValue;
GUI.changed = true;
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
s_Drag.Remove(id);
e.Use();
}
break;
}
return value;
}
public static float GetSensitivityByRange(float min, float max)
{
float range = Mathf.Abs(max - min);
if (float.IsNaN(range) || float.IsInfinity(range) || Mathf.Approximately(range, 0f))
{
return -1f;
}
return range * 0.003f;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b82b6bb5065f449f98cdba68daa4c16f
timeCreated: 1758462240

View File

@@ -0,0 +1,127 @@
using UnityEditor;
using UnityEngine;
using UnityEditor.AnimatedValues;
namespace NBShaderEditor
{
public class ShaderGUIFoldOutHelper
{
private const float FoldOutAnimationSpeed = 10f;
public ShaderGUIFoldOutHelper(ShaderGUIRootItem rootItem, string foldOutPropertyName)
{
_rootItem = rootItem;
_propertyName = foldOutPropertyName;
_propertyInfo = _rootItem.PropertyInfoDic[_propertyName];
bool isOpen = _propertyInfo.Property.floatValue > 0.5f;
animBool.value = isOpen;
animBool.target = isOpen;
animBool.valueChanged.AddListener(rootItem.MatEditor.Repaint);
animBool.speed = FoldOutAnimationSpeed;
}
private ShaderGUIRootItem _rootItem;
private string _propertyName;
private AnimBool animBool = new AnimBool();
private ShaderPropertyInfo _propertyInfo;
public bool BeginFadedGroup(Rect labelRect)
{
DrawFoldOut(labelRect);
return BeginFadeGroup();
}
public bool BeginFadeGroup()
{
return animBool.faded > 0.0001f;
}
public bool DrawFoldOut(Rect labelRect)
{
Rect foldOutRect = MakeFoldOutRect(labelRect);
bool isOpen = _propertyInfo.Property.floatValue > 0.5f;
isOpen = GUI.Toggle(foldOutRect, isOpen, GUIContent.none, EditorStyles.foldout);
SetOpen(isOpen);
return isOpen;
}
public bool DrawFoldOutLabel(Rect labelRect, GUIContent content, GUIStyle style)
{
Rect foldOutRect = MakeFoldOutRect(labelRect);
bool isOpen = _propertyInfo.Property.floatValue > 0.5f;
isOpen = GUI.Toggle(foldOutRect, isOpen, content, style ?? EditorStyles.foldout);
SetOpen(isOpen);
return isOpen;
}
public void SetOpen(bool isOpen)
{
if (animBool.target != isOpen)
{
animBool.target = isOpen;
}
float value = isOpen ? 1f : 0f;
if (!Mathf.Approximately(_propertyInfo.Property.floatValue, value))
{
_propertyInfo.Property.floatValue = value;
}
}
private static Rect MakeFoldOutRect(Rect labelRect)
{
Rect foldOutRect = labelRect;
foldOutRect.x = ShaderGUIItem.GetEditorLabelTextX(labelRect) - ShaderGUIItem.FoldOutArrowWidth;
foldOutRect.width = Mathf.Max(0f, labelRect.xMax - foldOutRect.x);
return foldOutRect;
}
public void EndFadedGroup()
{
}
}
public class ShaderGUIBigBlockItem:ShaderGUIItem
{
ShaderGUIFoldOutHelper _foldOutHelper;
GUIStyle _boldStyle = new GUIStyle(EditorStyles.boldLabel);
public string FoldOutPropertyName { get; set; }
public override void InitTriggerByChild()
{
_foldOutHelper = new ShaderGUIFoldOutHelper(RootItem, FoldOutPropertyName);
base.InitTriggerByChild();
}
public ShaderGUIBigBlockItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) :
base(rootItem, parentItem: parentItem)
{
}
public override void OnGUI()//完全覆写
{
LayoutSpace();
GetRect();
DrawResetButton();
bool isOpen = _foldOutHelper.BeginFadedGroup(LabelRect);
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent,_boldStyle);
}
EditorGUI.indentLevel++;
if (isOpen)
{
DrawBlock();
}
_foldOutHelper.EndFadedGroup();
EditorGUI.indentLevel--;
Rect rect = ApplyGlobalRectCompensation(LayoutRect(1));
EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6228baa804b041658eaebea96655b431
timeCreated: 1758373869

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f948e090bae4a558a52cb56d0b6c9fc
timeCreated: 1758127003

View File

@@ -0,0 +1,894 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public static class ShaderGUILocalization
{
public const string DefaultLanguage = "zh-CN";
public const string EnglishLanguage = "en-US";
private const string TooltipColumnSuffix = "-tip";
private const string TooltipColumnAliasSuffix = "-tooltip";
private static readonly Dictionary<string, LocalizationTable> Tables =
new Dictionary<string, LocalizationTable>(StringComparer.OrdinalIgnoreCase);
private static NBFXLanguageMode s_CachedLanguageMode;
private static string s_CachedPreferredLanguage;
private static bool s_CachedPreferredLanguageValid;
public static void RegisterCsv(string tableName, string csvAssetPath)
{
tableName = NormalizeTableName(tableName);
if (Tables.TryGetValue(tableName, out LocalizationTable table))
{
if (string.Equals(table.CsvAssetPath, csvAssetPath, StringComparison.OrdinalIgnoreCase))
{
return;
}
table.CsvAssetPath = csvAssetPath;
table.Reload();
return;
}
Tables[tableName] = new LocalizationTable(csvAssetPath);
}
public static string GetCurrentLanguage(string tableName)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetCurrentLanguage(table);
}
public static string GetEditorLanguageName()
{
return GetLanguageFromEditor();
}
private static string GetCurrentLanguage(LocalizationTable table)
{
string preferred = GetPreferredLanguageName();
foreach (string language in table.Languages)
{
if (string.Equals(language, preferred, StringComparison.OrdinalIgnoreCase))
{
return language;
}
}
return DefaultLanguage;
}
public static GUIContent MakeContent(
string tableName,
string labelKey,
string labelFallback,
string tooltipKey = null,
string tooltipFallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
var cacheKey = new ContentCacheKey(language, labelKey, labelFallback, tooltipKey, tooltipFallback);
if (table.ContentCache.TryGetValue(cacheKey, out GUIContent cachedContent))
{
return cachedContent;
}
var content = new GUIContent(
GetLocalizedValue(table, labelKey, labelFallback, language),
GetTooltip(table, labelKey, tooltipKey, tooltipFallback, language));
table.ContentCache[cacheKey] = content;
return content;
}
public static GUIContent MakeInspectorContent(string tableName, string key, string fallback, string tip = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
var cacheKey = new InspectorContentCacheKey(language, key, fallback, tip);
if (table.InspectorContentCache.TryGetValue(cacheKey, out GUIContent cachedContent))
{
return cachedContent;
}
string labelKey = "inspector." + key + ".label";
string tooltipKey = "inspector." + key + ".tip";
var content = new GUIContent(
GetLocalizedValue(table, labelKey, fallback, language),
GetTooltip(table, labelKey, tooltipKey, tip, language));
table.InspectorContentCache[cacheKey] = content;
return content;
}
public static string GetInspectorText(string tableName, string key, string fallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
return GetInspectorText(table, key, fallback, language);
}
public static string GetInspectorText(string tableName, string key, string fallback, string language)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetInspectorText(table, key, fallback, language);
}
private static string GetInspectorText(LocalizationTable table, string key, string fallback, string language)
{
var cacheKey = new InspectorTextCacheKey(language, key, fallback);
if (table.InspectorTextCache.TryGetValue(cacheKey, out string cachedText))
{
return cachedText;
}
cachedText = GetLocalizedValue(table, "inspector." + key, fallback, language);
table.InspectorTextCache[cacheKey] = cachedText;
return cachedText;
}
public static string[] GetInspectorOptions(string tableName, string key, string[] fallback)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
return GetInspectorOptions(table, key, fallback, language);
}
public static string[] GetInspectorOptions(string tableName, string key, string[] fallback, string language)
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetInspectorOptions(table, key, fallback, language);
}
private static string[] GetInspectorOptions(LocalizationTable table, string key, string[] fallback, string language)
{
if (fallback == null)
{
return Array.Empty<string>();
}
var cacheKey = new OptionsCacheKey(language, key, fallback);
if (table.InspectorOptionsCache.TryGetValue(cacheKey, out string[] cachedValues))
{
return cachedValues;
}
string keyPrefix = "inspector." + key + ".option";
string[] values = new string[fallback.Length];
for (int i = 0; i < fallback.Length; i++)
{
values[i] = GetLocalizedValue(table, keyPrefix + "." + i, fallback[i], language);
}
table.InspectorOptionsCache[cacheKey] = values;
return values;
}
public static string[] GetOptions(string tableName, string keyPrefix, string[] fallback)
{
if (fallback == null)
{
return Array.Empty<string>();
}
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
string language = GetCurrentLanguage(table);
var cacheKey = new OptionsCacheKey(language, keyPrefix, fallback);
if (table.OptionsCache.TryGetValue(cacheKey, out string[] cachedValues))
{
return cachedValues;
}
string[] values = new string[fallback.Length];
for (int i = 0; i < fallback.Length; i++)
{
values[i] = GetLocalizedValue(table, keyPrefix + "." + i, fallback[i], language);
}
table.OptionsCache[cacheKey] = values;
return values;
}
public static string Get(string tableName, string key, string fallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetLocalizedValue(table, key, fallback, GetCurrentLanguage(table));
}
private static string GetLocalizedValue(LocalizationTable table, string key, string fallback, string language)
{
if (string.IsNullOrEmpty(key))
{
return fallback;
}
if (table.Rows.TryGetValue(key, out Dictionary<string, string> row))
{
if (row.TryGetValue(language, out string localizedValue) && !string.IsNullOrEmpty(localizedValue))
{
return localizedValue;
}
if (row.TryGetValue(DefaultLanguage, out string defaultValue) && !string.IsNullOrEmpty(defaultValue))
{
return defaultValue;
}
}
return fallback;
}
public static string GetTooltip(string tableName, string labelKey, string tooltipKey = null, string fallback = "")
{
LocalizationTable table = GetTable(tableName);
table.EnsureLoaded();
return GetTooltip(table, labelKey, tooltipKey, fallback, GetCurrentLanguage(table));
}
private static string GetTooltip(
LocalizationTable table,
string labelKey,
string tooltipKey,
string fallback,
string language)
{
if (!string.IsNullOrEmpty(labelKey) &&
table.Rows.TryGetValue(labelKey, out Dictionary<string, string> row))
{
if (TryGetTooltip(row, language, out string localizedTooltip))
{
return localizedTooltip;
}
if (TryGetTooltip(row, DefaultLanguage, out string defaultTooltip))
{
return defaultTooltip;
}
}
if (!string.IsNullOrEmpty(tooltipKey))
{
return GetLocalizedValue(table, tooltipKey, fallback, language);
}
return fallback;
}
public static void Reload(string tableName = null)
{
if (string.IsNullOrEmpty(tableName))
{
foreach (LocalizationTable table in Tables.Values)
{
table.Reload();
}
InvalidatePreferredLanguageCache();
return;
}
GetTable(tableName).Reload();
InvalidatePreferredLanguageCache();
}
private static void InvalidatePreferredLanguageCache()
{
s_CachedPreferredLanguageValid = false;
}
private static LocalizationTable GetTable(string tableName)
{
tableName = NormalizeTableName(tableName);
if (!Tables.TryGetValue(tableName, out LocalizationTable table))
{
table = new LocalizationTable(string.Empty);
Tables[tableName] = table;
}
return table;
}
private static string NormalizeTableName(string tableName)
{
return string.IsNullOrEmpty(tableName) ? "default" : tableName;
}
private static bool IsTooltipColumn(string header)
{
return !string.IsNullOrEmpty(header) &&
(header.EndsWith(TooltipColumnSuffix, StringComparison.OrdinalIgnoreCase) ||
header.EndsWith(TooltipColumnAliasSuffix, StringComparison.OrdinalIgnoreCase));
}
private static bool TryGetTooltip(Dictionary<string, string> row, string language, out string tooltip)
{
tooltip = string.Empty;
if (string.IsNullOrEmpty(language))
{
return false;
}
if (row.TryGetValue(language + TooltipColumnSuffix, out tooltip) && !string.IsNullOrEmpty(tooltip))
{
return true;
}
if (row.TryGetValue(language + TooltipColumnAliasSuffix, out tooltip) && !string.IsNullOrEmpty(tooltip))
{
return true;
}
return false;
}
private static string GetPreferredLanguageName()
{
NBFXLanguageMode languageMode = NBFXProjectSettings.LanguageMode;
if (s_CachedPreferredLanguageValid && s_CachedLanguageMode == languageMode)
{
return s_CachedPreferredLanguage;
}
string language;
switch (languageMode)
{
case NBFXLanguageMode.Chinese:
language = DefaultLanguage;
break;
case NBFXLanguageMode.English:
language = EnglishLanguage;
break;
case NBFXLanguageMode.FollowEditor:
default:
language = GetLanguageFromEditor();
break;
}
s_CachedLanguageMode = languageMode;
s_CachedPreferredLanguage = language;
s_CachedPreferredLanguageValid = true;
return language;
}
private static string GetLanguageFromEditor()
{
if (TryGetEditorLanguageName(out string language))
{
return language;
}
return GetSystemLanguageName();
}
private static bool TryGetEditorLanguageName(out string language)
{
language = string.Empty;
Type localizationDatabaseType = typeof(Editor).Assembly.GetType("UnityEditor.LocalizationDatabase");
if (localizationDatabaseType == null)
{
return false;
}
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
string[] memberNames =
{
"currentEditorLanguage",
"CurrentEditorLanguage"
};
foreach (string memberName in memberNames)
{
PropertyInfo propertyInfo = localizationDatabaseType.GetProperty(memberName, flags);
if (propertyInfo != null && TryGetEditorLanguageValue(() => propertyInfo.GetValue(null, null), out language))
{
return true;
}
FieldInfo fieldInfo = localizationDatabaseType.GetField(memberName, flags);
if (fieldInfo != null && TryGetEditorLanguageValue(() => fieldInfo.GetValue(null), out language))
{
return true;
}
}
return false;
}
private static bool TryGetEditorLanguageValue(Func<object> valueGetter, out string language)
{
language = string.Empty;
try
{
return TryMapLanguageValue(valueGetter(), out language);
}
catch
{
return false;
}
}
private static bool TryMapLanguageValue(object value, out string language)
{
language = string.Empty;
if (value == null)
{
return false;
}
if (value is SystemLanguage systemLanguage)
{
language = MapSystemLanguage(systemLanguage);
return true;
}
string languageName = value.ToString();
if (string.IsNullOrEmpty(languageName))
{
return false;
}
string normalizedLanguageName = languageName.Replace("_", "-").ToLowerInvariant();
if (normalizedLanguageName.StartsWith("en", StringComparison.Ordinal) ||
normalizedLanguageName.Contains("english"))
{
language = EnglishLanguage;
return true;
}
if (normalizedLanguageName.StartsWith("zh", StringComparison.Ordinal) ||
normalizedLanguageName.Contains("chinese"))
{
language = DefaultLanguage;
return true;
}
language = DefaultLanguage;
return true;
}
private static string GetSystemLanguageName()
{
return MapSystemLanguage(Application.systemLanguage);
}
private static string MapSystemLanguage(SystemLanguage systemLanguage)
{
switch (systemLanguage)
{
case SystemLanguage.English:
return EnglishLanguage;
case SystemLanguage.Chinese:
case SystemLanguage.ChineseSimplified:
case SystemLanguage.ChineseTraditional:
return DefaultLanguage;
default:
return DefaultLanguage;
}
}
private readonly struct ContentCacheKey : IEquatable<ContentCacheKey>
{
private readonly string _language;
private readonly string _labelKey;
private readonly string _labelFallback;
private readonly string _tooltipKey;
private readonly string _tooltipFallback;
public ContentCacheKey(
string language,
string labelKey,
string labelFallback,
string tooltipKey,
string tooltipFallback)
{
_language = language;
_labelKey = labelKey;
_labelFallback = labelFallback;
_tooltipKey = tooltipKey;
_tooltipFallback = tooltipFallback;
}
public bool Equals(ContentCacheKey other)
{
return StringEquals(_language, other._language) &&
StringEquals(_labelKey, other._labelKey) &&
StringEquals(_labelFallback, other._labelFallback) &&
StringEquals(_tooltipKey, other._tooltipKey) &&
StringEquals(_tooltipFallback, other._tooltipFallback);
}
public override bool Equals(object obj)
{
return obj is ContentCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_labelKey);
hash = hash * 31 + StringHash(_labelFallback);
hash = hash * 31 + StringHash(_tooltipKey);
hash = hash * 31 + StringHash(_tooltipFallback);
return hash;
}
}
}
private readonly struct InspectorContentCacheKey : IEquatable<InspectorContentCacheKey>
{
private readonly string _language;
private readonly string _key;
private readonly string _fallback;
private readonly string _tip;
public InspectorContentCacheKey(string language, string key, string fallback, string tip)
{
_language = language;
_key = key;
_fallback = fallback;
_tip = tip;
}
public bool Equals(InspectorContentCacheKey other)
{
return StringEquals(_language, other._language) &&
StringEquals(_key, other._key) &&
StringEquals(_fallback, other._fallback) &&
StringEquals(_tip, other._tip);
}
public override bool Equals(object obj)
{
return obj is InspectorContentCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_key);
hash = hash * 31 + StringHash(_fallback);
hash = hash * 31 + StringHash(_tip);
return hash;
}
}
}
private readonly struct OptionsCacheKey : IEquatable<OptionsCacheKey>
{
private readonly string _language;
private readonly string _keyPrefix;
private readonly string[] _fallback;
private readonly int _fallbackLength;
public OptionsCacheKey(string language, string keyPrefix, string[] fallback)
{
_language = language;
_keyPrefix = keyPrefix;
_fallback = fallback;
_fallbackLength = fallback != null ? fallback.Length : 0;
}
public bool Equals(OptionsCacheKey other)
{
if (!StringEquals(_language, other._language) ||
!StringEquals(_keyPrefix, other._keyPrefix) ||
_fallbackLength != other._fallbackLength)
{
return false;
}
for (int i = 0; i < _fallbackLength; i++)
{
if (!StringEquals(_fallback[i], other._fallback[i]))
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj is OptionsCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_keyPrefix);
hash = hash * 31 + _fallbackLength;
for (int i = 0; i < _fallbackLength; i++)
{
hash = hash * 31 + StringHash(_fallback[i]);
}
return hash;
}
}
}
private readonly struct InspectorTextCacheKey : IEquatable<InspectorTextCacheKey>
{
private readonly string _language;
private readonly string _key;
private readonly string _fallback;
public InspectorTextCacheKey(string language, string key, string fallback)
{
_language = language;
_key = key;
_fallback = fallback;
}
public bool Equals(InspectorTextCacheKey other)
{
return StringEquals(_language, other._language) &&
StringEquals(_key, other._key) &&
StringEquals(_fallback, other._fallback);
}
public override bool Equals(object obj)
{
return obj is InspectorTextCacheKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 31 + StringHash(_language);
hash = hash * 31 + StringHash(_key);
hash = hash * 31 + StringHash(_fallback);
return hash;
}
}
}
private static bool StringEquals(string a, string b)
{
return string.Equals(a, b, StringComparison.Ordinal);
}
private static int StringHash(string value)
{
return value == null ? 0 : StringComparer.Ordinal.GetHashCode(value);
}
private sealed class LocalizationTable
{
private bool _loaded;
private bool _warningLogged;
public LocalizationTable(string csvAssetPath)
{
CsvAssetPath = csvAssetPath;
Rows = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
Languages = Array.Empty<string>();
ContentCache = new Dictionary<ContentCacheKey, GUIContent>();
InspectorContentCache = new Dictionary<InspectorContentCacheKey, GUIContent>();
InspectorTextCache = new Dictionary<InspectorTextCacheKey, string>();
OptionsCache = new Dictionary<OptionsCacheKey, string[]>();
InspectorOptionsCache = new Dictionary<OptionsCacheKey, string[]>();
}
public string CsvAssetPath { get; set; }
public Dictionary<string, Dictionary<string, string>> Rows { get; private set; }
public string[] Languages { get; private set; }
public Dictionary<ContentCacheKey, GUIContent> ContentCache { get; }
public Dictionary<InspectorContentCacheKey, GUIContent> InspectorContentCache { get; }
public Dictionary<InspectorTextCacheKey, string> InspectorTextCache { get; }
public Dictionary<OptionsCacheKey, string[]> OptionsCache { get; }
public Dictionary<OptionsCacheKey, string[]> InspectorOptionsCache { get; }
public void Reload()
{
_loaded = false;
_warningLogged = false;
Rows = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
Languages = Array.Empty<string>();
ContentCache.Clear();
InspectorContentCache.Clear();
InspectorTextCache.Clear();
OptionsCache.Clear();
InspectorOptionsCache.Clear();
}
public void EnsureLoaded()
{
if (_loaded)
{
return;
}
_loaded = true;
Rows = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
Languages = Array.Empty<string>();
if (string.IsNullOrEmpty(CsvAssetPath))
{
LogWarningOnce("ShaderGUI localization table is not registered.");
return;
}
TextAsset csvAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(CsvAssetPath);
if (csvAsset == null)
{
LogWarningOnce($"ShaderGUI localization csv not found at '{CsvAssetPath}'.");
return;
}
ParseCsv(csvAsset.text);
}
private void ParseCsv(string csvText)
{
List<string> rows = SplitRows(csvText);
if (rows.Count == 0)
{
return;
}
List<string> headers = ParseCsvRow(rows[0]);
if (headers.Count < 2)
{
return;
}
var languages = new List<string>();
for (int i = 1; i < headers.Count; i++)
{
if (!IsTooltipColumn(headers[i]))
{
languages.Add(headers[i]);
}
}
Languages = languages.ToArray();
for (int rowIndex = 1; rowIndex < rows.Count; rowIndex++)
{
List<string> values = ParseCsvRow(rows[rowIndex]);
if (values.Count == 0 || string.IsNullOrWhiteSpace(values[0]))
{
continue;
}
var languageMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
for (int i = 1; i < headers.Count; i++)
{
languageMap[headers[i]] = i < values.Count ? values[i] : string.Empty;
}
Rows[values[0]] = languageMap;
}
}
private static List<string> SplitRows(string csvText)
{
var rows = new List<string>();
if (string.IsNullOrEmpty(csvText))
{
return rows;
}
var builder = new StringBuilder();
bool inQuotes = false;
for (int i = 0; i < csvText.Length; i++)
{
char current = csvText[i];
if (current == '"')
{
if (inQuotes && i + 1 < csvText.Length && csvText[i + 1] == '"')
{
builder.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
continue;
}
if (!inQuotes && (current == '\n' || current == '\r'))
{
if (builder.Length > 0)
{
rows.Add(builder.ToString());
builder.Length = 0;
}
if (current == '\r' && i + 1 < csvText.Length && csvText[i + 1] == '\n')
{
i++;
}
continue;
}
builder.Append(current);
}
if (builder.Length > 0)
{
rows.Add(builder.ToString());
}
return rows;
}
private static List<string> ParseCsvRow(string rowText)
{
var values = new List<string>();
var builder = new StringBuilder();
bool inQuotes = false;
for (int i = 0; i < rowText.Length; i++)
{
char current = rowText[i];
if (current == '"')
{
if (inQuotes && i + 1 < rowText.Length && rowText[i + 1] == '"')
{
builder.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
continue;
}
if (!inQuotes && current == ',')
{
values.Add(builder.ToString());
builder.Length = 0;
continue;
}
builder.Append(current);
}
values.Add(builder.ToString());
return values;
}
private void LogWarningOnce(string message)
{
if (_warningLogged)
{
return;
}
_warningLogged = true;
Debug.LogWarning(message);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5429c73ee5e4590bbbbccee3d5a3a58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,158 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ShaderGUIPopUpItem:ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<string[]> _popupNamesProvider;
private readonly Action<MaterialProperty> _onValueChanged;
private readonly Func<bool> _isVisible;
public ShaderGUIPopUpItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) :
base(rootItem, parentItem: parentItem)
{
// base.InitTriggerByChild();
}
public ShaderGUIPopUpItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Func<string[]> popupNamesProvider,
Action<MaterialProperty> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_popupNamesProvider = popupNamesProvider;
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
PopUpNames = _popupNamesProvider?.Invoke();
InitTriggerByChild();
}
public string[] PopUpNames;
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void DrawController()
{
int value = EditorGUI.Popup(ControlRect, (int)PropertyInfo.Property.floatValue, PopUpNames);
SetFloatIfDifferent(PropertyInfo.Property, value);
}
public override void OnEndChange()
{
base.OnEndChange();
_onValueChanged?.Invoke(PropertyInfo.Property);
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
OnEndChange();
}
}
public class ShaderGUIBitMaskItem : ShaderGUIItem
{
private static readonly string[] EmptyMaskNames = new string[0];
private readonly Func<GUIContent> _contentProvider;
private readonly Func<string[]> _maskNamesProvider;
private readonly Action<MaterialProperty> _onValueChanged;
private readonly Func<bool> _isVisible;
public ShaderGUIBitMaskItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem)
: base(rootItem, parentItem: parentItem)
{
}
public ShaderGUIBitMaskItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Func<string[]> maskNamesProvider,
Action<MaterialProperty> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_maskNamesProvider = maskNamesProvider;
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
MaskNames = _maskNamesProvider?.Invoke();
InitTriggerByChild();
}
public string[] MaskNames;
public int ValidMask = -1;
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
if (_contentProvider != null)
{
GuiContent = _contentProvider();
}
if (_maskNamesProvider != null)
{
MaskNames = _maskNamesProvider();
}
base.OnGUI();
}
public override void DrawController()
{
string[] maskNames = MaskNames ?? EmptyMaskNames;
int validMask = GetValidMask(maskNames);
int mask = Mathf.RoundToInt(PropertyInfo.Property.floatValue) & validMask;
int value = EditorGUI.MaskField(ControlRect, mask, maskNames) & validMask;
SetFloatIfDifferent(PropertyInfo.Property, value);
}
public override void OnEndChange()
{
base.OnEndChange();
_onValueChanged?.Invoke(PropertyInfo.Property);
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
OnEndChange();
}
private int GetValidMask(string[] maskNames)
{
if (ValidMask >= 0)
{
return ValidMask;
}
int optionCount = Mathf.Clamp(maskNames.Length, 0, 30);
return optionCount == 0 ? 0 : (1 << optionCount) - 1;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a4147e85b43d4bcaab43dedf111c41fb
timeCreated: 1758444036

View File

@@ -0,0 +1,288 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using NBShader;
namespace NBShaderEditor
{
public class ShaderPropertyInfo
{
public MaterialProperty Property;
public string Name;
public int Index;
}
public class ShaderGUIRootItem
{
private static int s_RendererCacheVersion;
private static double s_RendererCacheFirstDirtyTime = -1d;
private static double s_RendererCacheDirtyTime;
private const double RendererCacheHierarchyRefreshDelay = 0.35d;
private const double RendererCacheMaxHierarchyRefreshDelay = 2d;
private const float DefaultManualLayoutHeight = 1200f;
public Shader Shader;
public MaterialEditor MatEditor;
public List<Material> Mats;
public Dictionary<string,ShaderPropertyInfo> PropertyInfoDic = new Dictionary<string,ShaderPropertyInfo>();
public List<ShaderFlagsBase> ShaderFlags;//各个继承类各自初始化
public bool IsInit = true;
public Color DefaultBackgroundColor;
public bool ClearUnusedTextureReferencesRequested { get; private set; }
public List<Renderer> RenderersUsingThisMaterial = new List<Renderer>();
public List<ParticleSystemRenderer> ParticleRenderersUsingThisMaterial = new List<ParticleSystemRenderer>();
public bool IsUsedByParticleSystem { get; private set; }
private int _rendererCacheVersion = -1;
private Material _rendererCacheMaterial;
private readonly List<Material> _rendererMaterialBuffer = new List<Material>();
private bool _manualLayoutActive;
private Rect _manualLayoutRect;
private float _manualLayoutY;
private float _manualLayoutUsedHeight;
private float _manualLayoutHeight = DefaultManualLayoutHeight;
static ShaderGUIRootItem()
{
EditorApplication.hierarchyChanged += OnHierarchyChanged;
}
private static void OnHierarchyChanged()
{
double now = EditorApplication.timeSinceStartup;
s_RendererCacheVersion++;
if (s_RendererCacheFirstDirtyTime < 0d)
{
s_RendererCacheFirstDirtyTime = now;
}
s_RendererCacheDirtyTime = now;
}
public virtual void OnGUI(MaterialEditor editor,MaterialProperty[] props)
{
MatEditor = editor;
DefaultBackgroundColor = GUI.backgroundColor;
if (IsInit || ShouldRebuildMaterialTargets(editor.targets))
{
IsInit = true;
Mats = new List<Material>();
foreach (var obj in editor.targets)
{
Mats.Add(obj as Material);
}
InitFlags(Mats);
Shader = Mats[0].shader;
RefreshRendererCacheIfNeeded(true);
}
else
{
RefreshRendererCacheIfNeeded(false);
}
if (ShouldRebuildPropertyInfo(props))
{
PropertyInfoDic.Clear();
for (int i = 0; i < props.Length; i++)
{
ShaderPropertyInfo propInfo = new ShaderPropertyInfo();
propInfo.Property = props[i];
propInfo.Name = props[i].name;
propInfo.Index = i;
PropertyInfoDic.Add(propInfo.Name, propInfo);
}
}
else
{
for (int i = 0; i < props.Length; i++)
{
ShaderPropertyInfo propInfo = PropertyInfoDic[props[i].name];
propInfo.Property = props[i];
propInfo.Index = i;
}
}
try
{
BeginManualLayout();
OnChildOnGUI();
}
finally
{
EndManualLayout();
ClearUnusedTextureReferencesRequested = false;
}
RepaintWhenAnimationModeActive();
IsInit = false;
}
public void RequestClearUnusedTextureReferences()
{
ClearUnusedTextureReferencesRequested = true;
}
public Rect GetControlRect(float height = -1f)
{
float rectHeight = height >= 0f ? height : EditorGUIUtility.singleLineHeight;
if (!_manualLayoutActive)
{
return EditorGUILayout.GetControlRect(false, rectHeight);
}
Rect rect = new Rect(_manualLayoutRect.x, _manualLayoutY, _manualLayoutRect.width, rectHeight);
_manualLayoutY += rectHeight + EditorGUIUtility.standardVerticalSpacing;
_manualLayoutUsedHeight = Mathf.Max(
_manualLayoutUsedHeight,
_manualLayoutY - _manualLayoutRect.y - EditorGUIUtility.standardVerticalSpacing);
return rect;
}
public void Space(float height = -1f)
{
float spaceHeight = height >= 0f ? height : EditorGUIUtility.standardVerticalSpacing;
if (!_manualLayoutActive)
{
EditorGUILayout.Space();
return;
}
_manualLayoutY += spaceHeight;
_manualLayoutUsedHeight = Mathf.Max(_manualLayoutUsedHeight, _manualLayoutY - _manualLayoutRect.y);
}
private void BeginManualLayout()
{
float height = Mathf.Max(EditorGUIUtility.singleLineHeight, _manualLayoutHeight);
_manualLayoutRect = EditorGUILayout.GetControlRect(false, height);
_manualLayoutY = _manualLayoutRect.y;
_manualLayoutUsedHeight = 0f;
_manualLayoutActive = true;
}
private void EndManualLayout()
{
if (!_manualLayoutActive)
{
return;
}
_manualLayoutActive = false;
float usedHeight = Mathf.Max(EditorGUIUtility.singleLineHeight, _manualLayoutUsedHeight);
if (!Mathf.Approximately(_manualLayoutHeight, usedHeight))
{
_manualLayoutHeight = usedHeight;
MatEditor?.Repaint();
}
}
private bool ShouldRebuildPropertyInfo(MaterialProperty[] props)
{
if (PropertyInfoDic.Count != props.Length)
{
return true;
}
for (int i = 0; i < props.Length; i++)
{
if (!PropertyInfoDic.ContainsKey(props[i].name))
{
return true;
}
}
return false;
}
void RepaintWhenAnimationModeActive()
{
if (AnimationMode.InAnimationMode())
{
MatEditor.Repaint();
}
}
private bool ShouldRebuildMaterialTargets(UnityEngine.Object[] targets)
{
if (Mats == null || targets == null || Mats.Count != targets.Length)
{
return true;
}
for (int i = 0; i < targets.Length; i++)
{
if (Mats[i] != (targets[i] as Material))
{
return true;
}
}
return false;
}
private void RefreshRendererCacheIfNeeded(bool force)
{
Material material = Mats != null && Mats.Count > 0 ? Mats[0] : null;
if (!force && _rendererCacheMaterial == material && _rendererCacheVersion == s_RendererCacheVersion)
{
return;
}
double now = EditorApplication.timeSinceStartup;
bool waitingForQuietHierarchy = now - s_RendererCacheDirtyTime < RendererCacheHierarchyRefreshDelay;
bool withinMaxDelay = s_RendererCacheFirstDirtyTime >= 0d &&
now - s_RendererCacheFirstDirtyTime < RendererCacheMaxHierarchyRefreshDelay;
if (!force &&
_rendererCacheMaterial == material &&
waitingForQuietHierarchy &&
withinMaxDelay)
{
return;
}
CacheRenderersUsingThisMaterial(material);
_rendererCacheMaterial = material;
_rendererCacheVersion = s_RendererCacheVersion;
s_RendererCacheFirstDirtyTime = -1d;
}
void CacheRenderersUsingThisMaterial(Material material)
{
Renderer[] renderers = UnityObjectFindCompat.FindAll<Renderer>();
RenderersUsingThisMaterial.Clear();
ParticleRenderersUsingThisMaterial.Clear();
IsUsedByParticleSystem = false;
if (material == null || renderers == null)
{
return;
}
foreach (Renderer renderer in renderers)
{
if (renderer is ParticleSystemRenderer psr &&
(psr.sharedMaterial == material || psr.trailMaterial == material))
{
IsUsedByParticleSystem = true;
ParticleRenderersUsingThisMaterial.Add(psr);
}
_rendererMaterialBuffer.Clear();
renderer.GetSharedMaterials(_rendererMaterialBuffer);
for (int i = 0; i < _rendererMaterialBuffer.Count; i++)
{
if (_rendererMaterialBuffer[i] == material)
{
RenderersUsingThisMaterial.Add(renderer);
break;
}
}
}
_rendererMaterialBuffer.Clear();
}
public virtual void InitFlags(List<Material> mats) { } //各个子类各自实现
public virtual void OnChildOnGUI()
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e57b381204754ae780ab305e5feff475
timeCreated: 1758206205

View File

@@ -0,0 +1,498 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class TextureItem : ShaderGUIItem
{
private readonly string _texturePropertyName;
private readonly TexturePropertyGroupItem _groupItem;
private readonly Func<bool> _isVisible;
private readonly Action<MaterialProperty> _afterDraw;
public TextureItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string texturePropertyName,
Func<GUIContent> contentProvider,
string colorPropertyName = null,
bool drawScaleOffset = true,
Action<MaterialProperty> afterDraw = null,
Func<bool> isVisible = null,
Func<GUIContent> tillingContentProvider = null,
Func<GUIContent> offsetContentProvider = null) : base(rootItem, parentItem)
{
_texturePropertyName = texturePropertyName;
_isVisible = isVisible;
_afterDraw = afterDraw;
_groupItem = new TexturePropertyGroupItem(
rootItem,
this,
texturePropertyName,
colorPropertyName,
contentProvider,
drawScaleOffset,
tillingContentProvider: tillingContentProvider,
offsetContentProvider: offsetContentProvider);
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
_groupItem.ClearTextureIfRequested();
return;
}
_groupItem.OnGUI();
if (_afterDraw != null && RootItem.PropertyInfoDic.TryGetValue(_texturePropertyName, out ShaderPropertyInfo texturePropertyInfo))
{
_afterDraw(texturePropertyInfo.Property);
}
}
}
public class TexturePropertyGroupItem : ShaderGUIItem
{
private readonly TextureObjectItem _textureItem;
private readonly ColorLineItem _colorItem;
private readonly TextureScaleOffsetItem _scaleOffsetItem;
private readonly Func<bool> _isVisible;
public TexturePropertyGroupItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string texturePropertyName,
string colorPropertyName,
Func<GUIContent> contentProvider,
bool drawScaleOffset = true,
Func<bool> isVisible = null,
Func<GUIContent> tillingContentProvider = null,
Func<GUIContent> offsetContentProvider = null) : base(rootItem, parentItem)
{
_isVisible = isVisible;
_textureItem = new TextureObjectItem(rootItem, this, texturePropertyName, contentProvider);
_colorItem = string.IsNullOrEmpty(colorPropertyName)
? null
: new ColorLineItem(rootItem, this, colorPropertyName, false, contentProvider);
_scaleOffsetItem = drawScaleOffset
? new TextureScaleOffsetItem(
rootItem,
this,
texturePropertyName,
false,
tillingContentProvider: tillingContentProvider,
offsetContentProvider: offsetContentProvider)
: null;
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
_textureItem.ClearTextureIfRequested();
return;
}
float singleLineHeight = EditorGUIUtility.singleLineHeight;
float rowGap = EditorGUIUtility.standardVerticalSpacing;
float textureFieldHeight = 3f * singleLineHeight + 2f * rowGap;
float textureContentGap = EditorGUIUtility.standardVerticalSpacing;
Rect textureGroupRect = ApplyGlobalRectCompensation(LayoutRect(textureFieldHeight));
Rect textureLabelRow = new Rect(textureGroupRect.x, textureGroupRect.y, textureGroupRect.width, singleLineHeight);
Rect tillingRow = new Rect(textureGroupRect.x, textureGroupRect.y + singleLineHeight + rowGap, textureGroupRect.width, singleLineHeight);
Rect offsetRow = new Rect(textureGroupRect.x, tillingRow.y + singleLineHeight + rowGap, textureGroupRect.width, singleLineHeight);
Rect indentedTextureLabelRow = ApplyDirectLabelIndentWidth(textureLabelRow);
Rect textureRect = new Rect(indentedTextureLabelRow.x + 2f, textureLabelRow.y, textureFieldHeight, textureFieldHeight);
textureRect.x -= 2f;
textureRect.y += 2f;
float contentX = textureRect.x + textureRect.width + textureContentGap;
Rect textureLabelRect = MoveRectLeftKeepingRight(textureLabelRow, contentX);
SplitLineRect(tillingRow, out _, out Rect tillingVec2Rect, out Rect tillingResetRect, false);
Rect tillingLabelRect = MakeLabelRect(tillingRow, contentX, tillingVec2Rect.x);
tillingVec2Rect = ApplyControlIndentCompensation(tillingVec2Rect);
SplitLineRect(offsetRow, out _, out Rect offsetVec2Rect, out Rect offsetResetRect, false);
Rect offsetLabelRect = MakeLabelRect(offsetRow, contentX, offsetVec2Rect.x);
offsetVec2Rect = ApplyControlIndentCompensation(offsetVec2Rect);
_textureItem.Draw(textureRect, textureLabelRect);
_scaleOffsetItem?.Draw(tillingLabelRect, tillingVec2Rect, tillingResetRect, offsetLabelRect, offsetVec2Rect, offsetResetRect);
_colorItem?.OnGUI();
}
public void ClearTextureIfRequested()
{
_textureItem.ClearTextureIfRequested();
}
private static Rect MoveRectLeftKeepingRight(Rect rect, float x)
{
float xMax = rect.xMax;
rect.x = Mathf.Min(x, xMax);
rect.width = xMax - rect.x;
return rect;
}
private static Rect MakeLabelRect(Rect rowRect, float labelX, float controlX)
{
Rect labelRect = rowRect;
labelRect.x = Mathf.Min(labelX, controlX);
labelRect.width = Mathf.Max(0f, controlX - labelRect.x);
return labelRect;
}
private static Rect ApplyControlIndentCompensation(Rect rect)
{
rect.x -= ControlIndentCompensation;
rect.width += ControlIndentCompensation;
return rect;
}
}
public class TextureObjectItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public TextureObjectItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string texturePropertyName,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = texturePropertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
ClearTextureIfRequested();
return;
}
GetRect();
Draw(ControlRect, LabelRect, ResetRect);
DrawBlock();
}
public void Draw(Rect textureRect, Rect labelAndResetRect)
{
SplitControlAndResetRect(labelAndResetRect, out Rect labelRect, out Rect resetRect, false);
Draw(textureRect, labelRect, resetRect);
}
public void Draw(Rect textureRect, Rect labelRect, Rect resetRect)
{
ControlRect = textureRect;
LabelRect = labelRect;
ResetRect = resetRect;
MaterialProperty property = PropertyInfo.Property;
if (RootItem.ClearUnusedTextureReferencesRequested && (IsParentControlDisabled || !GUI.enabled))
{
ClearTextureIfRequested();
}
using (ParentControlDisabledScope())
{
GUI.Label(LabelRect, GuiContent, EditorStyles.boldLabel);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
Texture texture;
using (new EditorGUIIndentLevelScope(0))
{
texture = (Texture)EditorGUI.ObjectField(ControlRect, property.textureValue, typeof(Texture2D), false);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
property.textureValue = texture;
OnEndChange();
}
}
DrawResetButton();
}
public void ClearTextureIfRequested()
{
if (!RootItem.ClearUnusedTextureReferencesRequested || PropertyInfo == null)
{
return;
}
MaterialProperty property = PropertyInfo.Property;
if (property == null || property.textureValue == null)
{
return;
}
property.textureValue = null;
OnEndChange();
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = PropertyInfo == null ||
(!PropertyInfo.Property.hasMixedValue && PropertyInfo.Property.textureValue == null);
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
if (PropertyInfo != null)
{
PropertyInfo.Property.textureValue = null;
PropertyIsDefaultValue = true;
}
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class TextureScaleOffsetItem : ShaderGUIItem
{
private static readonly GUIContent TillingContent = new GUIContent("Tilling");
private static readonly GUIContent OffsetContent = new GUIContent("Offset");
private static readonly Vector4 DefaultScaleOffset = new Vector4(1f, 1f, 0f, 0f);
private readonly bool _isVectorProperty;
private readonly Func<bool> _isVisible;
private readonly Func<GUIContent> _tillingContentProvider;
private readonly Func<GUIContent> _offsetContentProvider;
private readonly GUIContent _tillingContent;
private readonly GUIContent _offsetContent;
public TextureScaleOffsetItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
bool isVectorProperty,
Func<bool> isVisible = null,
Func<GUIContent> tillingContentProvider = null,
Func<GUIContent> offsetContentProvider = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_isVectorProperty = isVectorProperty;
_isVisible = isVisible;
_tillingContentProvider = tillingContentProvider ?? (() => TillingContent);
_offsetContentProvider = offsetContentProvider ?? (() => OffsetContent);
_tillingContent = _tillingContentProvider();
_offsetContent = _offsetContentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
Rect tillingLabelRect = LabelRect;
Rect tillingVec2Rect = ControlRect;
Rect tillingResetRect = ResetRect;
GetRect();
Rect offsetLabelRect = LabelRect;
Rect offsetVec2Rect = ControlRect;
Rect offsetResetRect = ResetRect;
Draw(tillingLabelRect, tillingVec2Rect, tillingResetRect, offsetLabelRect, offsetVec2Rect, offsetResetRect, true);
DrawBlock();
}
public void Draw(
Rect tillingLabelRect,
Rect tillingVec2Rect,
Rect tillingResetRect,
Rect offsetLabelRect,
Rect offsetVec2Rect,
Rect offsetResetRect,
bool useEditorLabelField = false)
{
MaterialProperty property = PropertyInfo.Property;
Vector4 scaleOffset = GetScaleOffset(property);
DrawLabel(tillingLabelRect, _tillingContent, useEditorLabelField);
using (ParentControlDisabledScope())
{
Vector2 tilling = new Vector2(scaleOffset.x, scaleOffset.y);
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
bool tillingAnimatedScope = BeginAnimatedPropertyBackground(tillingVec2Rect, property);
using (new EditorGUIIndentLevelScope(0))
{
tilling = EditorGUI.Vector2Field(tillingVec2Rect, GUIContent.none, tilling);
}
EndAnimatedPropertyBackground(tillingAnimatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
scaleOffset.x = tilling.x;
scaleOffset.y = tilling.y;
Apply(scaleOffset);
}
bool tillingModified = property.hasMixedValue || tilling != Vector2.one;
if (GUI.Button(tillingResetRect, tillingModified ? "R" : string.Empty, tillingModified ? GUI.skin.button : GUI.skin.label))
{
scaleOffset.x = 1f;
scaleOffset.y = 1f;
Apply(scaleOffset);
}
}
DrawLabel(offsetLabelRect, _offsetContent, useEditorLabelField);
using (ParentControlDisabledScope())
{
Vector2 offset = new Vector2(scaleOffset.z, scaleOffset.w);
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
bool offsetAnimatedScope = BeginAnimatedPropertyBackground(offsetVec2Rect, property);
using (new EditorGUIIndentLevelScope(0))
{
offset = EditorGUI.Vector2Field(offsetVec2Rect, GUIContent.none, offset);
}
EndAnimatedPropertyBackground(offsetAnimatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
scaleOffset.z = offset.x;
scaleOffset.w = offset.y;
Apply(scaleOffset);
}
bool offsetModified = property.hasMixedValue || offset != Vector2.zero;
if (GUI.Button(offsetResetRect, offsetModified ? "R" : string.Empty, offsetModified ? GUI.skin.button : GUI.skin.label))
{
scaleOffset.z = 0f;
scaleOffset.w = 0f;
Apply(scaleOffset);
}
}
}
private void DrawLabel(Rect rect, GUIContent content, bool useEditorLabelField)
{
using (ParentControlDisabledScope())
{
if (useEditorLabelField)
{
EditorGUI.LabelField(rect, content);
return;
}
GUI.Label(rect, content);
}
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
bool isDefaultValue = true;
if (PropertyInfo != null)
{
MaterialProperty property = PropertyInfo.Property;
isDefaultValue = !property.hasMixedValue && Approximately(GetScaleOffset(property), DefaultScaleOffset);
}
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
if (PropertyInfo != null)
{
SetScaleOffset(PropertyInfo.Property, DefaultScaleOffset);
PropertyIsDefaultValue = true;
}
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
private Vector4 GetScaleOffset(MaterialProperty property)
{
return _isVectorProperty ? property.vectorValue : property.textureScaleAndOffset;
}
private void Apply(Vector4 scaleOffset)
{
SetScaleOffset(PropertyInfo.Property, scaleOffset);
OnEndChange();
}
private void SetScaleOffset(MaterialProperty property, Vector4 scaleOffset)
{
if (_isVectorProperty)
{
property.vectorValue = scaleOffset;
}
else
{
property.textureScaleAndOffset = scaleOffset;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7f026fa752b843a0b469f9306b2ff461
timeCreated: 1777032000

View File

@@ -0,0 +1,58 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class ToggleItem : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
private readonly Action<bool> _onValueChanged;
public ToggleItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Action<bool> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
base.OnGUI();
}
public override void DrawController()
{
bool value = PropertyInfo.Property.floatValue > 0.5f;
value = EditorGUI.Toggle(ControlRect, value);
SetFloatIfDifferent(PropertyInfo.Property, value ? 1f : 0f);
}
public override void OnEndChange()
{
base.OnEndChange();
_onValueChanged?.Invoke(PropertyInfo.Property.floatValue > 0.5f);
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
_onValueChanged?.Invoke(PropertyInfo.Property.floatValue > 0.5f);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: afba064c4c70401bbb7133de835d2d35
timeCreated: 1777032000

View File

@@ -0,0 +1,610 @@
using System;
using UnityEditor;
using UnityEngine;
namespace NBShaderEditor
{
public class Vector2LineItem : ShaderGUIItem
{
private readonly bool _firstLine;
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public Vector2LineItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
bool firstLine,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_firstLine = firstLine;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
MaterialProperty property = PropertyInfo.Property;
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Vector4 vector = property.vectorValue;
Vector2 value = _firstLine ? new Vector2(vector.x, vector.y) : new Vector2(vector.z, vector.w);
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.Vector2Field(ControlRect, GUIContent.none, value);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
if (_firstLine)
{
vector.x = value.x;
vector.y = value.y;
}
else
{
vector.z = value.x;
vector.w = value.y;
}
property.vectorValue = vector;
OnEndChange();
}
}
DrawResetButton();
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (PropertyInfo == null)
{
base.CheckIsPropertyModified(isCallByChild);
return;
}
if (!isCallByChild)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
Vector4 currentValue = PropertyInfo.Property.vectorValue;
bool isDefaultValue = _firstLine
? Mathf.Approximately(currentValue.x, defaultValue.x) && Mathf.Approximately(currentValue.y, defaultValue.y)
: Mathf.Approximately(currentValue.z, defaultValue.z) && Mathf.Approximately(currentValue.w, defaultValue.w);
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
Vector4 currentValue = PropertyInfo.Property.vectorValue;
if (_firstLine)
{
currentValue.x = defaultValue.x;
currentValue.y = defaultValue.y;
}
else
{
currentValue.z = defaultValue.z;
currentValue.w = defaultValue.w;
}
PropertyInfo.Property.vectorValue = currentValue;
PropertyIsDefaultValue = true;
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class VectorComponentItem : ShaderGUIItem
{
private readonly int _componentIndex;
private readonly bool _isSlider;
private readonly float _min;
private readonly float _max;
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
public VectorComponentItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
int componentIndex,
Func<GUIContent> contentProvider,
bool isSlider,
float min = 0f,
float max = 1f,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_componentIndex = componentIndex;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isSlider = isSlider;
_min = min;
_max = max;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
MaterialProperty property = PropertyInfo.Property;
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Vector4 vector = property.vectorValue;
float value = GetValue(vector);
value = _isSlider
? DraggableLabelFloat.Handle(
LabelRect,
value,
DraggableLabelFloat.GetSensitivityByRange(_min, _max),
_min,
_max)
: DraggableLabelFloat.Handle(LabelRect, value);
if (_isSlider)
{
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.Slider(ControlRect, value, _min, _max);
}
EndAnimatedPropertyBackground(animatedScope);
}
else
{
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.FloatField(ControlRect, value);
}
EndAnimatedPropertyBackground(animatedScope);
}
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
SetValue(ref vector, value);
property.vectorValue = vector;
OnEndChange();
}
}
DrawResetButton();
}
private float GetValue(Vector4 vector)
{
switch (_componentIndex)
{
case 0: return vector.x;
case 1: return vector.y;
case 2: return vector.z;
case 3: return vector.w;
default: return vector.x;
}
}
private void SetValue(ref Vector4 vector, float value)
{
switch (_componentIndex)
{
case 0:
vector.x = value;
break;
case 1:
vector.y = value;
break;
case 2:
vector.z = value;
break;
case 3:
vector.w = value;
break;
}
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (PropertyInfo == null)
{
base.CheckIsPropertyModified(isCallByChild);
return;
}
if (!isCallByChild)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
bool isDefaultValue = Mathf.Approximately(GetValue(PropertyInfo.Property.vectorValue), GetValue(defaultValue));
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (var childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
Vector4 vector = PropertyInfo.Property.vectorValue;
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
SetValue(ref vector, GetValue(defaultValue));
PropertyInfo.Property.vectorValue = vector;
PropertyIsDefaultValue = true;
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class VectorComponentRangeSliderItem : ShaderGUIItem
{
private readonly int _componentIndex;
private readonly Func<GUIContent> _contentProvider;
private readonly string _rangePropertyName;
private readonly Func<bool> _isVisible;
private ShaderPropertyInfo _rangePropertyInfo;
private const float SliderFrontGap = 5f;
private const float SliderBackGap = 2f;
public VectorComponentRangeSliderItem(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
int componentIndex,
string rangePropertyName,
Func<GUIContent> contentProvider,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_componentIndex = componentIndex;
_rangePropertyName = rangePropertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void InitTriggerByChild()
{
if (!string.IsNullOrEmpty(_rangePropertyName))
{
_rangePropertyInfo = RootItem.PropertyInfoDic[_rangePropertyName];
}
base.InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.BeginChangeCheck();
using (new EditorGUIIndentLevelScope(0))
{
DrawController();
}
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
OnEndChange();
}
}
DrawResetButton();
}
public override void DrawController()
{
Vector4 vector = PropertyInfo.Property.vectorValue;
Vector4 range = _rangePropertyInfo.Property.vectorValue;
float min = range.x;
float max = range.y;
float sliderMin = Mathf.Min(min, max);
float sliderMax = Mathf.Max(min, max);
float value = GetValue(vector);
float rangeFieldWidth = EditorGUIUtility.fieldWidth;
Rect minRect = ControlRect;
minRect.width = rangeFieldWidth;
Rect maxRect = ControlRect;
maxRect.x = ControlRect.xMax - rangeFieldWidth;
maxRect.width = rangeFieldWidth;
Rect sliderRect = ControlRect;
sliderRect.x = minRect.xMax + SliderFrontGap;
sliderRect.width = Mathf.Max(0f, maxRect.x - sliderRect.x - SliderBackGap + EditorGUI.indentLevel * UnityEditorGUIIndentWidth);
RangeVecHasMixedValue(out bool minValueHasMixed, out bool maxValueHasMixed);
EditorGUI.showMixedValue = minValueHasMixed;
bool minAnimatedScope = BeginAnimatedPropertyBackground(minRect, _rangePropertyInfo.Property);
min = EditorGUI.FloatField(minRect, min);
EndAnimatedPropertyBackground(minAnimatedScope);
EditorGUI.showMixedValue = maxValueHasMixed;
bool maxAnimatedScope = BeginAnimatedPropertyBackground(maxRect, _rangePropertyInfo.Property);
max = EditorGUI.FloatField(maxRect, max);
EndAnimatedPropertyBackground(maxAnimatedScope);
range.x = min;
range.y = max;
SetVectorIfDifferent(_rangePropertyInfo.Property, range);
sliderMin = Mathf.Min(min, max);
sliderMax = Mathf.Max(min, max);
value = DraggableLabelFloat.Handle(
LabelRect,
value,
DraggableLabelFloat.GetSensitivityByRange(sliderMin, sliderMax),
sliderMin,
sliderMax);
EditorGUI.showMixedValue = PropertyInfo.Property.hasMixedValue;
bool sliderAnimatedScope = BeginAnimatedPropertyBackground(sliderRect, PropertyInfo.Property);
value = SliderNoIndent(sliderRect, value, sliderMin, sliderMax);
EndAnimatedPropertyBackground(sliderAnimatedScope);
SetValue(ref vector, Mathf.Clamp(value, sliderMin, sliderMax));
SetVectorIfDifferent(PropertyInfo.Property, vector);
EditorGUI.showMixedValue = false;
}
private static float SliderNoIndent(Rect rect, float value, float min, float max)
{
int indentLevel = EditorGUI.indentLevel;
try
{
EditorGUI.indentLevel = 0;
return EditorGUI.Slider(rect, value, min, max);
}
finally
{
EditorGUI.indentLevel = indentLevel;
}
}
private void RangeVecHasMixedValue(out bool minValueHasMixed, out bool maxValueHasMixed)
{
minValueHasMixed = false;
maxValueHasMixed = false;
if (RootItem.Mats.Count <= 1)
{
return;
}
float firstMin = 0f;
float firstMax = 0f;
for (int i = 0; i < RootItem.Mats.Count; i++)
{
Vector4 range = RootItem.Mats[i].GetVector(_rangePropertyName);
if (i == 0)
{
firstMin = range.x;
firstMax = range.y;
continue;
}
if (!Mathf.Approximately(firstMin, range.x))
{
minValueHasMixed = true;
}
if (!Mathf.Approximately(firstMax, range.y))
{
maxValueHasMixed = true;
}
}
}
private float GetValue(Vector4 vector)
{
switch (_componentIndex)
{
case 0: return vector.x;
case 1: return vector.y;
case 2: return vector.z;
case 3: return vector.w;
default: return vector.x;
}
}
private void SetValue(ref Vector4 vector, float value)
{
switch (_componentIndex)
{
case 0:
vector.x = value;
break;
case 1:
vector.y = value;
break;
case 2:
vector.z = value;
break;
case 3:
vector.w = value;
break;
}
}
public override void CheckIsPropertyModified(bool isCallByChild = false)
{
if (!isCallByChild)
{
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
Vector4 defaultRange = RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
bool isDefaultValue = Mathf.Approximately(GetValue(PropertyInfo.Property.vectorValue), GetValue(defaultValue)) &&
_rangePropertyInfo.Property.vectorValue == defaultRange;
if (isDefaultValue == PropertyIsDefaultValue)
{
return;
}
PropertyIsDefaultValue = isDefaultValue;
}
HasModified = !PropertyIsDefaultValue;
foreach (ShaderGUIItem childItem in ChildrenItemList)
{
HasModified |= childItem.HasModified;
}
ParentItem?.CheckIsPropertyModified(true);
}
public override void ExecuteReset(bool isCallByParent = false)
{
Vector4 vector = PropertyInfo.Property.vectorValue;
Vector4 defaultValue = RootItem.Shader.GetPropertyDefaultVectorValue(PropertyInfo.Index);
SetValue(ref vector, GetValue(defaultValue));
PropertyInfo.Property.vectorValue = vector;
_rangePropertyInfo.Property.vectorValue = RootItem.Shader.GetPropertyDefaultVectorValue(_rangePropertyInfo.Index);
PropertyIsDefaultValue = true;
HasModified = false;
if (!isCallByParent)
{
ParentItem?.CheckIsPropertyModified(true);
}
}
}
public class Vector3Item : ShaderGUIItem
{
private readonly Func<GUIContent> _contentProvider;
private readonly Func<bool> _isVisible;
private readonly Action<Vector3> _onValueChanged;
public Vector3Item(
ShaderGUIRootItem rootItem,
ShaderGUIItem parentItem,
string propertyName,
Func<GUIContent> contentProvider,
Action<Vector3> onValueChanged = null,
Func<bool> isVisible = null) : base(rootItem, parentItem)
{
PropertyName = propertyName;
_contentProvider = contentProvider ?? (() => GUIContent.none);
_onValueChanged = onValueChanged;
_isVisible = isVisible;
GuiContent = _contentProvider();
InitTriggerByChild();
}
public override void OnGUI()
{
if (_isVisible != null && !_isVisible())
{
return;
}
GetRect();
MaterialProperty property = PropertyInfo.Property;
using (ParentControlDisabledScope())
{
EditorGUI.LabelField(LabelRect, GuiContent);
}
using (ParentControlDisabledScope())
{
EditorGUI.showMixedValue = property.hasMixedValue;
EditorGUI.BeginChangeCheck();
Vector4 vector = property.vectorValue;
Vector3 value = new Vector3(vector.x, vector.y, vector.z);
bool animatedScope = BeginAnimatedPropertyBackground(ControlRect, property);
using (new EditorGUIIndentLevelScope(0))
{
value = EditorGUI.Vector3Field(ControlRect, GUIContent.none, value);
}
EndAnimatedPropertyBackground(animatedScope);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
vector.x = value.x;
vector.y = value.y;
vector.z = value.z;
property.vectorValue = vector;
OnEndChange();
_onValueChanged?.Invoke(value);
}
}
DrawResetButton();
}
public override void ExecuteReset(bool isCallByParent = false)
{
base.ExecuteReset(isCallByParent);
Vector4 vector = PropertyInfo.Property.vectorValue;
_onValueChanged?.Invoke(new Vector3(vector.x, vector.y, vector.z));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 20a960b448954fc29c9197c2357e89ec
timeCreated: 1777032000