Last active
April 17, 2023 08:22
-
-
Save fanhexin/6785098ab1a0dcfe85938da266fff98b to your computer and use it in GitHub Desktop.
Custom unity component to modify material property through MaterialPropertyBlockModifier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using GameFoundation.Extension; | |
using UnityEngine; | |
namespace GameFoundation.Util | |
{ | |
[ExecuteInEditMode] | |
[RequireComponent(typeof(Renderer))] | |
public class MaterialPropertyBlockModifier : MonoBehaviour | |
{ | |
#region NestedClasses | |
public class ShaderProperty<T> | |
{ | |
public string name; | |
public T value; | |
} | |
[Serializable] | |
public class ColorProperty : ShaderProperty<Color> { } | |
[Serializable] | |
public class FloatProperty : ShaderProperty<float> { } | |
#endregion | |
[SerializeField] | |
ColorProperty[] _colorProperties; | |
[SerializeField] | |
FloatProperty[] _floatProperties; | |
void Start() | |
{ | |
var render = GetComponent<MeshRenderer>(); | |
render.ApplyPropertyBlock(mpb => | |
{ | |
SetMpbProperties(_colorProperties, mpb.SetColor); | |
SetMpbProperties(_floatProperties, mpb.SetFloat); | |
}); | |
} | |
void SetMpbProperties<T>(ShaderProperty<T>[] properties, Action<string, T> fn) | |
{ | |
if (null == properties) | |
{ | |
return; | |
} | |
foreach (var item in properties) | |
{ | |
fn(item.name, item.value); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using GameFoundation.Extension; | |
using GameFoundation.Util; | |
using UnityEditor; | |
using UnityEngine; | |
namespace GameFoundation.Editor | |
{ | |
[CanEditMultipleObjects] | |
[CustomEditor(typeof(MaterialPropertyBlockModifier))] | |
public class MaterialPropertyBlockModifierInspector : UnityEditor.Editor | |
{ | |
#region NestedClasses | |
abstract class AbstractPropertyGuiRender<T> | |
{ | |
readonly MaterialPropertyBlockModifierInspector _editor; | |
readonly MaterialPropertyBlockModifier _mpbm; | |
protected readonly MeshRenderer _mr; | |
protected AbstractPropertyGuiRender(MaterialPropertyBlockModifierInspector editor) | |
{ | |
_editor = editor; | |
_mpbm = _editor.target as MaterialPropertyBlockModifier; | |
_mr = _mpbm.GetComponent<MeshRenderer>(); | |
} | |
protected abstract T GetOriginValue(string propertyName); | |
protected abstract void SetValue(MaterialPropertyBlockModifier mpbm, string name, T value); | |
protected abstract T GetValue(SerializedProperty sp); | |
protected abstract void SetValue(SerializedProperty sp, T value); | |
protected abstract T RenderView(string labelName, T value); | |
protected abstract bool PropertyEquals(SerializedProperty property, T value); | |
public void Render(string propertyName, string propertiesName) | |
{ | |
var originValue = GetOriginValue(propertyName); | |
SerializedProperty sp = _editor.serializedObject.FindProperty(propertiesName); | |
int index = sp.IndexOfArrayItem(x => x.FindPropertyRelative("name").stringValue == propertyName); | |
if (index < 0) | |
{ | |
EditorGUI.BeginChangeCheck(); | |
var newValue = RenderView(propertyName, originValue); | |
if (EditorGUI.EndChangeCheck()) | |
{ | |
sp.AddArrayItem(x => | |
{ | |
x.FindPropertyRelative("name").stringValue = propertyName; | |
SetValue(x.FindPropertyRelative("value"), newValue); | |
}); | |
SetValue(_mpbm, propertyName, newValue); | |
_editor.serializedObject.ApplyModifiedProperties(); | |
} | |
return; | |
} | |
SerializedProperty property = sp.GetArrayElementAtIndex(index); | |
SerializedProperty valueProperty = property.FindPropertyRelative("value"); | |
EditorGUI.BeginChangeCheck(); | |
EditorGUILayout.PropertyField(valueProperty, new GUIContent(propertyName)); | |
if (EditorGUI.EndChangeCheck()) | |
{ | |
SetValue(_mpbm, propertyName, GetValue(valueProperty)); | |
_editor.serializedObject.ApplyModifiedProperties(); | |
} | |
if (!PropertyEquals(valueProperty, originValue) && GUILayout.Button("revert")) | |
{ | |
sp.DeleteArrayElementAtIndex(index); | |
SetValue(_mpbm, propertyName, originValue); | |
_editor.serializedObject.ApplyModifiedProperties(); | |
} | |
} | |
} | |
class ColorPropertyGuiRender: AbstractPropertyGuiRender<Color> | |
{ | |
public ColorPropertyGuiRender(MaterialPropertyBlockModifierInspector editor) | |
: base(editor) | |
{ | |
} | |
protected override Color GetOriginValue(string propertyName) | |
{ | |
return _mr.sharedMaterial.GetColor(propertyName); | |
} | |
protected override void SetValue(MaterialPropertyBlockModifier mpbm, string name, Color value) | |
{ | |
_mr.ApplyPropertyBlock(x => x.SetColor(name, value)); | |
} | |
protected override Color GetValue(SerializedProperty sp) | |
{ | |
return sp.colorValue; | |
} | |
protected override void SetValue(SerializedProperty sp, Color value) | |
{ | |
sp.colorValue = value; | |
} | |
protected override Color RenderView(string labelName, Color value) | |
{ | |
return EditorGUILayout.ColorField(labelName, value); | |
} | |
protected override bool PropertyEquals(SerializedProperty property, Color value) | |
{ | |
return property.colorValue == value; | |
} | |
} | |
class FloatPropertyGuiRender: AbstractPropertyGuiRender<float> | |
{ | |
public FloatPropertyGuiRender(MaterialPropertyBlockModifierInspector editor) | |
: base(editor) | |
{ | |
} | |
protected override float GetOriginValue(string propertyName) | |
{ | |
return _mr.sharedMaterial.GetFloat(propertyName); | |
} | |
protected override void SetValue(MaterialPropertyBlockModifier mpbm, string name, float value) | |
{ | |
_mr.ApplyPropertyBlock(x => x.SetFloat(name, value)); | |
} | |
protected override float GetValue(SerializedProperty sp) | |
{ | |
return sp.floatValue; | |
} | |
protected override void SetValue(SerializedProperty sp, float value) | |
{ | |
sp.floatValue = value; | |
} | |
protected override float RenderView(string labelName, float value) | |
{ | |
return EditorGUILayout.FloatField(labelName, value); | |
} | |
protected override bool PropertyEquals(SerializedProperty property, float value) | |
{ | |
return property.floatValue == value; | |
} | |
} | |
#endregion | |
ColorPropertyGuiRender _colorPropertyGuiRender; | |
FloatPropertyGuiRender _floatPropertyGuiRender; | |
Renderer _render; | |
//private TexturePropertyGuiRender _texturePropertyGuiRender; | |
void OnEnable() | |
{ | |
_render = (target as MaterialPropertyBlockModifier).GetComponent<Renderer>(); | |
if (_render.sharedMaterial != null) | |
{ | |
_render.sharedMaterial.hideFlags = HideFlags.NotEditable; | |
} | |
// TODO 不用每次都 new,多实例可复用 | |
_colorPropertyGuiRender = new ColorPropertyGuiRender(this); | |
_floatPropertyGuiRender = new FloatPropertyGuiRender(this); | |
//_texturePropertyGuiRender = new TexturePropertyGuiRender(this); | |
} | |
void OnDestroy() | |
{ | |
if (_render != null && _render.sharedMaterial != null) | |
{ | |
_render.sharedMaterial.hideFlags = HideFlags.None; | |
} | |
} | |
public override void OnInspectorGUI() | |
{ | |
var mpbm = target as MaterialPropertyBlockModifier; | |
var mr = mpbm.GetComponent<MeshRenderer>(); | |
if (mr.sharedMaterial == null) | |
{ | |
return; | |
} | |
var shader = mr.sharedMaterial.shader; | |
for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++) | |
{ | |
string name = ShaderUtil.GetPropertyName(shader, i); | |
ShaderUtil.ShaderPropertyType type = ShaderUtil.GetPropertyType(shader, i); | |
EditorGUILayout.BeginHorizontal(); | |
switch (type) | |
{ | |
case ShaderUtil.ShaderPropertyType.Color: | |
_colorPropertyGuiRender.Render(name, "_colorProperties"); | |
break; | |
case ShaderUtil.ShaderPropertyType.Float: | |
case ShaderUtil.ShaderPropertyType.Range: | |
_floatPropertyGuiRender.Render(name, "_floatProperties"); | |
break; | |
default: | |
break; | |
} | |
EditorGUILayout.EndHorizontal(); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Reflection; | |
using UnityEditor; | |
using UnityEditor.Graphs; | |
using UnityEngine; | |
namespace GameFoundation.Extension | |
{ | |
public static class SupportExtension | |
{ | |
#if UNITY_EDITOR | |
public static void AddArrayItem(this SerializedProperty sp, Action<SerializedProperty> initFn) | |
{ | |
if (!sp.isArray) | |
{ | |
throw new Exception("SerializedProperty must be array type"); | |
} | |
int index = sp.arraySize; | |
sp.InsertArrayElementAtIndex(index); | |
SerializedProperty newItemProperty = sp.GetArrayElementAtIndex(index); | |
if (initFn != null) | |
{ | |
initFn(newItemProperty); | |
} | |
} | |
public static SerializedProperty FindArrayItem(this SerializedProperty sp, Predicate<SerializedProperty> predicate) | |
{ | |
int index = sp.IndexOfArrayItem(predicate); | |
if (index < 0) | |
{ | |
return null; | |
} | |
return sp.GetArrayElementAtIndex(index); | |
} | |
public static int IndexOfArrayItem(this SerializedProperty sp, Predicate<SerializedProperty> predicate) | |
{ | |
for (int i = 0; i < sp.arraySize; i++) | |
{ | |
SerializedProperty item = sp.GetArrayElementAtIndex(i); | |
if (predicate(item)) | |
{ | |
return i; | |
} | |
} | |
return -1; | |
} | |
#endif | |
static MaterialPropertyBlock _mpb; | |
public static void ApplyPropertyBlock(this Renderer renderer, Action<MaterialPropertyBlock> changePropertiesFn) | |
{ | |
if (changePropertiesFn == null) | |
{ | |
return; | |
} | |
if (_mpb == null) | |
{ | |
_mpb = new MaterialPropertyBlock(); | |
} | |
renderer.GetPropertyBlock(_mpb); | |
changePropertiesFn(_mpb); | |
renderer.SetPropertyBlock(_mpb); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you.
This put me in a right direction.