Skip to content

Instantly share code, notes, and snippets.

@FleshMobProductions
Created August 19, 2022 18:21
Show Gist options
  • Save FleshMobProductions/72cb17da6a9d03cf7aaa83940e26b751 to your computer and use it in GitHub Desktop.
Save FleshMobProductions/72cb17da6a9d03cf7aaa83940e26b751 to your computer and use it in GitHub Desktop.
Unity MaterialPropertyDrawers to support showing or hiding specific properties in the inspector depending on defined keywords in the material (place scripts inside "Editor" folder)
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
// TODO: check if we can select multiple
// https://answers.unity.com/questions/865321/custom-materialpropertydrawer-with-argument.html
// Pass one or more keywords in the constructor, I'm not sure how values will get separated though or if they will
/// <summary>
/// Usage: [ShowIfKeywordEnabled(_KEYWORD)]
/// Pass one or more shader keywords. The property is only drawn when the material has at least one of the
/// specified keywords enabled. Can take up to 4 different keywords.
/// </summary>
public abstract class ShowIfKeywordBaseDrawer : MaterialPropertyDrawer
{
protected bool doesMaterialHaveArgumentKeyword;
protected string[] keywordArguments;
private GUIContent guiContent;
private MethodInfo internalMethod;
private Type[] methodArgumentTypes;
private object[] methodArguments;
public ShowIfKeywordBaseDrawer()
{
Debug.LogWarning($"{this.GetType()} in material was created without a keyword parameter, it will have no effect");
Initiallize(new string[0]);
}
public ShowIfKeywordBaseDrawer(string keyword1)
{
Initiallize(keyword1);
}
public ShowIfKeywordBaseDrawer(string keyword1, string keyword2)
{
Initiallize(keyword1, keyword2);
}
public ShowIfKeywordBaseDrawer(string keyword1, string keyword2, string keyword3)
{
Initiallize(keyword1, keyword2, keyword3);
}
public ShowIfKeywordBaseDrawer(string keyword1, string keyword2, string keyword3, string keyword4)
{
Initiallize(keyword1, keyword2, keyword3, keyword4);
}
public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
{
ValidateHasArgumentKeyword(editor);
// editor.GetPropertyHeight(prop) crashes the inspector, maybe because it tries to invoke this method,
// resulting in an recursive method call error
return ShouldBeDrawn() ? MaterialEditor.GetDefaultPropertyHeight(prop) : 0;
}
public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
{
ValidateHasArgumentKeyword(editor);
if (ShouldBeDrawn())
{
// base.OnGUI(position, prop, label, editor); doesn't seem to work for drawing the default OnGUI of the property type
if (internalMethod != null)
{
guiContent.text = label;
methodArguments[0] = position;
methodArguments[1] = prop;
methodArguments[2] = guiContent;
internalMethod.Invoke(editor, methodArguments);
}
}
}
protected abstract bool ShouldBeDrawn();
private void Initiallize(params string[] keywords)
{
keywordArguments = keywords;
guiContent = new GUIContent(string.Empty);
methodArgumentTypes = new[] { typeof(Rect), typeof(MaterialProperty), typeof(GUIContent) };
methodArguments = new object[3];
internalMethod = typeof(MaterialEditor)
.GetMethod("DefaultShaderPropertyInternal", BindingFlags.Instance | BindingFlags.NonPublic,
null,
methodArgumentTypes,
null);
}
protected void ValidateHasArgumentKeyword(MaterialEditor editor)
{
// Drawer is recreated when the material keywords change, so we have to update
// doesMaterialHaveArgumentKeyword on every GUI event
doesMaterialHaveArgumentKeyword = HasRequiredKeyword(editor);
}
private bool HasRequiredKeyword(MaterialEditor editor)
{
if (keywordArguments.Length == 0)
return true;
Material targetMaterial = editor.target as Material;
if (targetMaterial != null)
{
foreach (var keyword in keywordArguments)
{
if (targetMaterial.IsKeywordEnabled(keyword))
return true;
}
}
return false;
}
}
using UnityEditor;
using UnityEngine;
/// <summary>
/// Usage: [ShowIfKeywordDisabled(_KEYWORD)] or [ShowIfKeywordDisabled(_KEYWORD1, _KEYWORD2)]
/// Pass one or more shader keywords. The property is only drawn when the material has none of the
/// specified keywords enabled (all keywords have to be disabled). Can take up to 4 different keywords.
/// </summary>
public class ShowIfKeywordDisabledDrawer : ShowIfKeywordBaseDrawer
{
public ShowIfKeywordDisabledDrawer() : base()
{
}
public ShowIfKeywordDisabledDrawer(string keyword1) : base(keyword1)
{
}
public ShowIfKeywordDisabledDrawer(string keyword1, string keyword2) : base(keyword1, keyword2)
{
}
public ShowIfKeywordDisabledDrawer(string keyword1, string keyword2, string keyword3) : base(keyword1, keyword2, keyword3)
{
}
public ShowIfKeywordDisabledDrawer(string keyword1, string keyword2, string keyword3, string keyword4) : base(keyword1, keyword2, keyword3, keyword4)
{
}
public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
{
return base.GetPropertyHeight(prop, label, editor);
}
public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
{
base.OnGUI(position, prop, label, editor);
}
// Also draw if the user has accidentally no arguments passed
protected override bool ShouldBeDrawn()
{
return keywordArguments.Length == 0 || !doesMaterialHaveArgumentKeyword;
}
}
using UnityEditor;
using UnityEngine;
// TODO: check if we can select multiple
// https://answers.unity.com/questions/865321/custom-materialpropertydrawer-with-argument.html
// Pass one or more keywords in the constructor, I'm not sure how values will get separated though or if they will
/// <summary>
/// Usage: [ShowIfKeywordEnabled(_KEYWORD)] or [ShowIfKeywordEnabled(_KEYWORD1, _KEYWORD2)]
/// Pass one or more shader keywords. The property is only drawn when the material has at least one of the
/// specified keywords enabled. Can take up to 4 different keywords.
/// </summary>
public class ShowIfKeywordEnabledDrawer : ShowIfKeywordBaseDrawer
{
public ShowIfKeywordEnabledDrawer() : base()
{
}
public ShowIfKeywordEnabledDrawer(string keyword1) : base(keyword1)
{
}
public ShowIfKeywordEnabledDrawer(string keyword1, string keyword2) : base(keyword1, keyword2)
{
}
public ShowIfKeywordEnabledDrawer(string keyword1, string keyword2, string keyword3) : base(keyword1, keyword2, keyword3)
{
}
public ShowIfKeywordEnabledDrawer(string keyword1, string keyword2, string keyword3, string keyword4) : base(keyword1, keyword2, keyword3, keyword4)
{
}
public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
{
return base.GetPropertyHeight(prop, label, editor);
}
public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
{
base.OnGUI(position, prop, label, editor);
}
protected override bool ShouldBeDrawn()
{
return doesMaterialHaveArgumentKeyword;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment