Created
September 16, 2023 15:18
-
-
Save koturn/6e036ef61119ce1759966252436f452c to your computer and use it in GitHub Desktop.
Unityのシェーダーで[Toggle]が付与されているプロパティのキーワードをC#側から切り替えるやつ(シェーダー側の定義が主)
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.Expressions; | |
using System.Reflection; | |
using UnityEditor; | |
using UnityEngine; | |
namespace Koturn | |
{ | |
/// <summary> | |
/// Provides utility method about MaterialToggleUIDrawer. | |
/// </summary> | |
public static class ToggleKeywordUtil | |
{ | |
/// <summary> | |
/// Cache of reflection result of following lambda. | |
/// </summary> | |
/// <remarks><seealso cref="CreateToggleKeywordDelegate"/></remarks> | |
private static Action<Shader, MaterialProperty, bool> _toggleKeyword; | |
/// <summary> | |
/// Cache of reflection result of following lambda. | |
/// </summary> | |
/// <remarks><seealso cref="CreateToggleKeywordExDelegate"/></remarks> | |
private static Action<Material, string, bool> _toggleKeywordEx; | |
/// <summary> | |
/// Enable or disable keyword of <see cref="MaterialProperty"/> which has MaterialToggleUIDrawer. | |
/// </summary> | |
/// <param name="shader">Target <see cref="Shader"/>.</param> | |
/// <param name="prop">Target <see cref="MaterialProperty"/>.</param> | |
public static void ToggleKeyword(Shader shader, MaterialProperty prop) | |
{ | |
ToggleKeyword(shader, prop, ToBool(prop.floatValue)); | |
} | |
/// <summary> | |
/// Enable or disable keyword of <see cref="MaterialProperty"/> which has MaterialToggleUIDrawer. | |
/// </summary> | |
/// <param name="shader">Target <see cref="Shader"/>.</param> | |
/// <param name="prop">Target <see cref="MaterialProperty"/>.</param> | |
/// <param name="isOn">True to enable (define) keyword, false to disable (undefine) keyword.</param> | |
public static void ToggleKeyword(Shader shader, MaterialProperty prop, bool isOn) | |
{ | |
try | |
{ | |
(_toggleKeyword ?? (_toggleKeyword = CreateSetKeywordDelegate()))(shader, prop, isOn); | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError(ex.ToString()); | |
} | |
} | |
/// <summary> | |
/// Enable or disable keyword of <see cref="Material"/> which has MaterialToggleDrawer. | |
/// </summary> | |
/// <param name="material">Target <see cref="Material"/>.</param> | |
/// <param name="propName">Name of toggle property.</param> | |
/// <param name="isOn">True to enable (define) keyword, false to disable (undefine) keyword.</param> | |
public static void ToggleKeyword(Material material, string propName) | |
{ | |
if (material.HasProperty(propName)) | |
{ | |
ToggleKeyword(material, propName, ToBool(material.GetFloat(propName))); | |
} | |
else | |
{ | |
throw new ArgumentException("Could not find Property: '" + propName + "'"); | |
} | |
} | |
/// <summary> | |
/// Enable or disable keyword of <see cref="Material"/> which has MaterialToggleDrawer. | |
/// </summary> | |
/// <param name="material">Target <see cref="Material"/>.</param> | |
/// <param name="propName">Name of toggle property.</param> | |
/// <param name="isOn">True to enable (define) keyword, false to disable (undefine) keyword.</param> | |
public static void ToggleKeyword(Material material, string propName, bool isOn) | |
{ | |
try | |
{ | |
(_toggleKeywordEx ?? (_toggleKeywordEx = CreateToggleKeywordExDelegate()))(material, propName, isOn); | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError(ex.ToString()); | |
} | |
} | |
/// <summary> | |
/// <para>Create delegate of reflection results about UnityEditor.MaterialToggleUIDrawer.</para> | |
/// <code> | |
/// (Shader shader, MaterialProperty prop, bool isOn) => | |
/// { | |
/// MaterialPropertyHandler mph = UnityEditor.MaterialPropertyHandler.GetHandler(shader, prop.name); | |
/// if (mph is null) | |
/// { | |
/// throw new ArgumentException("Specified MaterialProperty does not have UnityEditor.MaterialPropertyHandler"); | |
/// } | |
/// MaterialToggleUIDrawer mpud = mph.propertyDrawer as MaterialToggleUIDrawer; | |
/// if (mpud is null) | |
/// { | |
/// throw new ArgumentException("Specified MaterialProperty does not have UnityEditor.MaterialToggleUIDrawer"); | |
/// } | |
/// mpud.SetKeyword(prop, isOn); | |
/// } | |
/// </code> | |
/// </summary> | |
private static Action<Shader, MaterialProperty, bool> CreateToggleKeywordDelegate() | |
{ | |
// Get assembly from public class. | |
var asm = Assembly.GetAssembly(typeof(UnityEditor.MaterialPropertyDrawer)); | |
// Get type of UnityEditor.MaterialPropertyHandler which is the internal class. | |
var typeMph = asm.GetType("UnityEditor.MaterialPropertyHandler") | |
?? throw new InvalidOperationException("Type not found: UnityEditor.MaterialPropertyHandler"); | |
var typeMtud = asm.GetType("UnityEditor.MaterialToggleUIDrawer") | |
?? throw new InvalidOperationException("Type not found: UnityEditor.MaterialToggleUIDrawer"); | |
var ciArgumentException = typeof(ArgumentException).GetConstructor(new[] {typeof(string)}); | |
var pShader = Expression.Parameter(typeof(Shader), "shader"); | |
var pMaterialPropertyHandler = Expression.Parameter(typeMph, "mph"); | |
var pMaterialToggleUIDrawer = Expression.Parameter(typeMtud, "mtud"); | |
var pMaterialProperty = Expression.Parameter(typeof(MaterialProperty), "mp"); | |
var pIsOn = Expression.Parameter(typeof(bool), "isOn"); | |
var cNull = Expression.Constant(null); | |
return Expression.Lambda<Action<Shader, MaterialProperty, bool>>( | |
Expression.Block( | |
new[] | |
{ | |
pMaterialPropertyHandler, | |
pMaterialToggleUIDrawer | |
}, | |
Expression.Assign( | |
pMaterialPropertyHandler, | |
Expression.Call( | |
typeMph.GetMethod( | |
"GetHandler", | |
BindingFlags.NonPublic | |
| BindingFlags.Static) | |
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.MaterialPropertyHandler.GetHandler"), | |
pShader, | |
Expression.Property( | |
pMaterialProperty, | |
typeof(MaterialProperty).GetProperty( | |
"name", | |
BindingFlags.GetProperty | |
| BindingFlags.Public | |
| BindingFlags.Instance)))), | |
Expression.IfThen( | |
Expression.Equal( | |
pMaterialPropertyHandler, | |
cNull), | |
Expression.Throw( | |
Expression.New( | |
ciArgumentException, | |
Expression.Constant("Specified MaterialProperty does not have UnityEditor.MaterialPropertyHandler")))), | |
Expression.Assign( | |
pMaterialToggleUIDrawer, | |
Expression.TypeAs( | |
Expression.Property( | |
pMaterialPropertyHandler, | |
typeMph.GetProperty( | |
"propertyDrawer", | |
BindingFlags.GetProperty | |
| BindingFlags.Public | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("PropertyInfo not found: UnityEditor.MaterialPropertyHandler.propertyDrawer")), | |
typeMtud)), | |
Expression.IfThen( | |
Expression.Equal( | |
pMaterialToggleUIDrawer, | |
cNull), | |
Expression.Throw( | |
Expression.New( | |
ciArgumentException, | |
Expression.Constant("Specified MaterialProperty does not have UnityEditor.MaterialToggleUIDrawer")))), | |
Expression.Call( | |
pMaterialToggleUIDrawer, | |
typeMtud.GetMethod( | |
"SetKeyword", | |
BindingFlags.NonPublic | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.MaterialToggleUIDrawer.SetKeyword"), | |
pMaterialProperty, | |
pIsOn)), | |
"ToggleKeyword", | |
new [] | |
{ | |
pShader, | |
pMaterialProperty, | |
pIsOn | |
}).Compile(); | |
} | |
/// <summary> | |
/// <para>Create delegate of reflection results about UnityEditor.MaterialToggleDrawer.</para> | |
/// <code> | |
/// (Material material, string propName, bool isOn) => | |
/// { | |
/// MaterialPropertyHandler mph = UnityEditor.MaterialPropertyHandler.GetHandler(material.shader, propName); | |
/// if (mph is null) | |
/// { | |
/// throw new ArgumentException("Specified MaterialProperty does not have UnityEditor.MaterialPropertyHandler"); | |
/// } | |
/// | |
/// MaterialToggleDrawer mpd = mph.propertyDrawer as MaterialToggleDrawer; | |
/// if (mpd is null) | |
/// { | |
/// throw new ArgumentException("Specified MaterialProperty does not have UnityEditor.MaterialToggleDrawer"); | |
/// } | |
/// | |
/// var keyword = mpud.keyword; | |
/// if (string.IsNullOrEmpty(keyword)) | |
/// { | |
/// keyword = propName.ToUpperInvariant() + (mpd is MaterialToggleOffDrawer ? "_OFF" : "_ON") | |
/// } | |
/// | |
/// if (mpd is MaterialToggleOffDrawer) | |
/// { | |
/// isOn = !isOn; | |
/// } | |
/// | |
/// if (isOn) | |
/// { | |
/// material.EnableKeyword(keyword); | |
/// } | |
/// else | |
/// { | |
/// material.DisableKeyword(keyword); | |
/// } | |
/// }; | |
/// </code> | |
/// </summary> | |
private static Action<Material, string, bool> CreateToggleKeywordExDelegate() | |
{ | |
// Get assembly from public class. | |
var asm = Assembly.GetAssembly(typeof(UnityEditor.MaterialPropertyDrawer)); | |
// Get type of UnityEditor.MaterialPropertyHandler which is the internal class. | |
var typeMph = asm.GetType("UnityEditor.MaterialPropertyHandler") | |
?? throw new InvalidOperationException("Type not found: UnityEditor.MaterialPropertyHandler"); | |
var typeMtd = asm.GetType("UnityEditor.MaterialToggleDrawer") | |
?? throw new InvalidOperationException("Type not found: UnityEditor.MaterialToggleDrawer"); | |
var typeMtod = asm.GetType("UnityEditor.MaterialToggleOffDrawer") | |
?? throw new InvalidOperationException("Type not found: UnityEditor.MaterialToggleOffDrawer"); | |
var ciArgumentException = typeof(ArgumentException).GetConstructor(new[] {typeof(string)}); | |
// Arguments. | |
var pMaterial = Expression.Parameter(typeof(Material), "material"); | |
var pPropName = Expression.Parameter(typeof(string), "propName"); | |
var pIsOn = Expression.Parameter(typeof(bool), "isOn"); | |
// Local variables. | |
var pShader = Expression.Parameter(typeof(Shader), "shader"); | |
var pMaterialPropertyHandler = Expression.Parameter(typeMph, "mph"); | |
var pMaterialToggleDrawer = Expression.Parameter(typeMtd, "mtd"); | |
var pKeyword = Expression.Parameter(typeof(string), "keyword"); | |
// Constants. | |
var cNull = Expression.Constant(null); | |
return Expression.Lambda<Action<Material, string, bool>>( | |
Expression.Block( | |
new[] | |
{ | |
pMaterialPropertyHandler, | |
pMaterialToggleDrawer, | |
pKeyword | |
}, | |
Expression.Assign( | |
pMaterialPropertyHandler, | |
Expression.Call( | |
typeMph.GetMethod( | |
"GetHandler", | |
BindingFlags.NonPublic | |
| BindingFlags.Static) | |
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.MaterialPropertyHandler.GetHandler"), | |
Expression.Property( | |
pMaterial, | |
typeof(Material).GetProperty( | |
"shader", | |
BindingFlags.GetProperty | |
| BindingFlags.Public | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("PropertyInfo not found: UnityEngine.Material.shader")), | |
pPropName)), | |
Expression.IfThen( | |
Expression.Equal( | |
pMaterialPropertyHandler, | |
cNull), | |
Expression.Throw( | |
Expression.New( | |
ciArgumentException, | |
Expression.Constant("Specified MaterialProperty does not have UnityEditor.MaterialPropertyHandler")))), | |
Expression.Assign( | |
pMaterialToggleDrawer, | |
Expression.TypeAs( | |
Expression.Property( | |
pMaterialPropertyHandler, | |
typeMph.GetProperty( | |
"propertyDrawer", | |
BindingFlags.GetProperty | |
| BindingFlags.Public | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("PropertyInfo not found: UnityEditor.MaterialPropertyHandler.propertyDrawer")), | |
typeMtd)), | |
Expression.IfThen( | |
Expression.Equal( | |
pMaterialToggleDrawer, | |
cNull), | |
Expression.Throw( | |
Expression.New( | |
ciArgumentException, | |
Expression.Constant("Specified MaterialProperty does not have UnityEditor.MaterialToggleDrawer")))), | |
Expression.Assign( | |
pKeyword, | |
Expression.Field( | |
pMaterialToggleDrawer, | |
typeMtd.GetField( | |
"keyword", | |
BindingFlags.GetField | |
| BindingFlags.NonPublic | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("FieldInfo not found: UnityEditor.MaterialToggleDrawer.keyword"))), | |
Expression.IfThen( | |
Expression.Call( | |
typeof(string).GetMethod( | |
"IsNullOrEmpty", | |
BindingFlags.Public | |
| BindingFlags.Static) | |
?? throw new InvalidOperationException("MethodInfo not found: String.IsNullOrEmpty"), | |
pKeyword), | |
Expression.Assign( | |
pKeyword, | |
Expression.Call( | |
typeof(string).GetMethod( | |
"Concat", | |
new Type[] | |
{ | |
typeof(string), | |
typeof(string) | |
}) | |
?? throw new InvalidOperationException("MethodInfo not found: String.IsNullOrEmpty"), | |
Expression.Call( | |
pPropName, | |
typeof(string).GetMethod( | |
"ToUpperInvariant", | |
BindingFlags.Public | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("MethodInfo not found: String.ToUpperInvariant")), | |
Expression.Condition( | |
Expression.TypeIs( | |
pMaterialToggleDrawer, | |
typeMtod), | |
Expression.Constant("_OFF"), | |
Expression.Constant("_ON"))))), | |
Expression.IfThen( | |
Expression.TypeIs( | |
pMaterialToggleDrawer, | |
typeMtod), | |
Expression.Assign( | |
pIsOn, | |
Expression.Not(pIsOn))), | |
Expression.IfThenElse( | |
pIsOn, | |
Expression.Call( | |
pMaterial, | |
typeof(Material).GetMethod( | |
"EnableKeyword", | |
BindingFlags.Public | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("MethodInfo not found: UnityEngine.Material.EnableKeyword"), | |
pKeyword), | |
Expression.Call( | |
pMaterial, | |
typeof(Material).GetMethod( | |
"DisableKeyword", | |
BindingFlags.Public | |
| BindingFlags.Instance) | |
?? throw new InvalidOperationException("MethodInfo not found: UnityEngine.Material.DisableKeyword"), | |
pKeyword))), | |
"ToggleKeyword", | |
new [] | |
{ | |
pMaterial, | |
pPropName, | |
pIsOn | |
}).Compile(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment