499 lines
18 KiB
C#
499 lines
18 KiB
C#
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;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|