523 lines
26 KiB
C#

using UnityEditor;
using UnityEngine;
using System;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
using static SilentCelShading.Unity.InspectorCommon;
// Parts of this file are based on https://github.com/Microsoft/MixedRealityToolkit-Unity/
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace SilentCelShading.Unity
{
public class SCSSShaderGUI : ShaderGUI
{
public enum DepthWrite
{
Off,
On
}
public enum RenderingMode
{
Opaque = 0,
Cutout = 1,
Fade = 2,
Transparent = 3,
Additive = 4,
Custom = 5
}
public enum CustomRenderingMode
{
Opaque = 0,
Cutout = 1,
Fade = 2,
Premultiplied = 3
}
protected static class BaseStyles
{
public static string renderTypeName = "RenderType";
public static string renderingModeName = "_Mode";
public static string customRenderingModeName = "_CustomMode";
public static string sourceBlendName = "_SrcBlend";
public static string destinationBlendName = "_DstBlend";
public static string blendOperationName = "_BlendOp";
public static string depthTestName = "_ZTest";
public static string depthWriteName = "_ZWrite";
public static string colorWriteMaskName = "_ColorWriteMask";
public static string cullModeName = "_CullMode";
public static string renderQueueOverrideName = "_RenderQueueOverride";
public static string alphaToMaskName = "_AtoCMode";
public static string alphaTestOnName = "_ALPHATEST_ON";
public static string alphaBlendOnName = "_ALPHABLEND_ON";
public static string alphaPremultiplyOnName = "_ALPHAPREMULTIPLY_ON";
public static readonly string[] renderingModeNames = Enum.GetNames(typeof(RenderingMode));
public static readonly string[] customRenderingModeNames = Enum.GetNames(typeof(CustomRenderingMode));
public static readonly string[] depthWriteNames = Enum.GetNames(typeof(DepthWrite));
public static GUIContent sourceBlend = new GUIContent("Source Blend", "Blend Mode of Newly Calculated Color");
public static GUIContent destinationBlend = new GUIContent("Destination Blend", "Blend Mode of Exisiting Color");
public static GUIContent blendOperation = new GUIContent("Blend Operation", "Operation for Blending New Color With Exisiting Color");
public static GUIContent depthTest = new GUIContent("Depth Test", "How Should Depth Testing Be Performed.");
public static GUIContent depthWrite = new GUIContent("Depth Write", "Controls Whether Pixels From This Object Are Written to the Depth Buffer");
public static GUIContent colorWriteMask = new GUIContent("Color Write Mask", "Color Channel Writing Mask");
public static GUIContent cullMode = new GUIContent("Cull Mode", "Triangle culling mode. Note: Outlines require this to be set to front to work properly.");
public static GUIContent renderQueueOverride = new GUIContent("Render Queue Override", "Manually set the Render Queue.");
public static string stencilComparisonName = "_StencilComp";
public static string stencilOperationName = "_StencilOp";
public static string stencilFailName = "_StencilFail";
public static string stencilZFailName = "_StencilZFail";
public static GUIContent stencilReference = new GUIContent("Stencil Test", "Raising this enables reading or writing a stencil. When set, contains calue to compare against (if Comparison is anything but Always) and/or the value to be written to the buffer (if wither Pass, Fail or ZFail is set to Replace)");
public static GUIContent stencilComparison = new GUIContent("Stencil Comparison", "The function to be used when reading the stencil value.");
public static GUIContent stencilOperation = new GUIContent("Stencil Operation", "The operation to be performed when the stencil test passes.");
public static GUIContent stencilFail = new GUIContent("Stencil Fail", "The operation to be performed when the stencil test fails.");
public static GUIContent stencilZFail = new GUIContent("Stencil ZFail", "The operation to be performed when the stencil test passes, but the geometry is occluded.");
}
protected bool initialised;
protected MaterialProperty renderingMode;
protected MaterialProperty customRenderingMode;
protected MaterialProperty sourceBlend;
protected MaterialProperty destinationBlend;
protected MaterialProperty blendOperation;
protected MaterialProperty depthTest;
protected MaterialProperty depthWrite;
protected MaterialProperty colorWriteMask;
protected MaterialProperty cullMode;
protected MaterialProperty renderQueueOverride;
protected MaterialProperty stencilReference;
protected MaterialProperty stencilComparison;
protected MaterialProperty stencilOperation;
protected MaterialProperty stencilFail;
protected MaterialProperty stencilZFail;
protected const string LegacyShadersPath = "Legacy Shaders/";
protected const string TransparentShadersPath = "/Transparent/";
protected const string TransparentCutoutShadersPath = "/Transparent/Cutout/";
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
{
Material material = (Material)materialEditor.target;
FindProperties(props);
Initialise(material);
RenderingModeOptions(materialEditor);
}
protected virtual void FindProperties(MaterialProperty[] props)
{
renderingMode = FindProperty(BaseStyles.renderingModeName, props);
customRenderingMode = FindProperty(BaseStyles.customRenderingModeName, props);
sourceBlend = FindProperty(BaseStyles.sourceBlendName, props);
destinationBlend = FindProperty(BaseStyles.destinationBlendName, props);
blendOperation = FindProperty(BaseStyles.blendOperationName, props);
depthTest = FindProperty(BaseStyles.depthTestName, props);
depthWrite = FindProperty(BaseStyles.depthWriteName, props);
//depthOffsetFactor = FindProperty(BaseStyles.depthOffsetFactorName, props);
//depthOffsetUnits = FindProperty(BaseStyles.depthOffsetUnitsName, props);
colorWriteMask = FindProperty(BaseStyles.colorWriteMaskName, props);
stencilReference = FindProperty("_Stencil", props);
stencilComparison = FindProperty(BaseStyles.stencilComparisonName, props);
stencilOperation = FindProperty(BaseStyles.stencilOperationName, props);
stencilFail = FindProperty(BaseStyles.stencilFailName, props);
stencilZFail = FindProperty(BaseStyles.stencilZFailName, props);
cullMode = FindProperty(BaseStyles.cullModeName, props);
renderQueueOverride = FindProperty(BaseStyles.renderQueueOverrideName, props);
}
protected void Initialise(Material material)
{
if (!initialised)
{
MaterialChanged(material);
initialised = true;
}
}
protected virtual void MaterialChanged(Material material)
{
SetupMaterialWithRenderingMode(material,
(RenderingMode)renderingMode.floatValue,
(CustomRenderingMode)customRenderingMode.floatValue,
(int)renderQueueOverride.floatValue);
}
protected void RenderingModeOptions(MaterialEditor materialEditor)
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = renderingMode.hasMixedValue;
RenderingMode mode = (RenderingMode)renderingMode.floatValue;
EditorGUI.BeginChangeCheck();
mode = (RenderingMode)EditorGUILayout.Popup(renderingMode.displayName, (int)mode, BaseStyles.renderingModeNames);
if (EditorGUI.EndChangeCheck())
{
materialEditor.RegisterPropertyChangeUndo(renderingMode.displayName);
renderingMode.floatValue = (float)mode;
}
EditorGUI.showMixedValue = false;
if ((RenderingMode)renderingMode.floatValue == RenderingMode.Custom)
{
EditorGUI.indentLevel += 2;
customRenderingMode.floatValue = EditorGUILayout.Popup(customRenderingMode.displayName, (int)customRenderingMode.floatValue, BaseStyles.customRenderingModeNames);
materialEditor.ShaderProperty(sourceBlend, BaseStyles.sourceBlend);
materialEditor.ShaderProperty(destinationBlend, BaseStyles.destinationBlend);
materialEditor.ShaderProperty(blendOperation, BaseStyles.blendOperation);
materialEditor.ShaderProperty(depthTest, BaseStyles.depthTest);
depthWrite.floatValue = EditorGUILayout.Popup(depthWrite.displayName, (int)depthWrite.floatValue, BaseStyles.depthWriteNames);
materialEditor.ShaderProperty(colorWriteMask, BaseStyles.colorWriteMask);
EditorGUI.indentLevel -= 2;
}
materialEditor.ShaderProperty(cullMode, BaseStyles.cullMode);
if (EditorGUI.EndChangeCheck())
{
Object[] targets = renderingMode.targets;
foreach (Object target in targets)
{
MaterialChanged((Material)target);
}
}
}
protected void StencilOptions(MaterialEditor materialEditor, Material material)
{
materialEditor.ShaderProperty(stencilReference, BaseStyles.stencilReference);
if (stencilReference.floatValue > 0)
{
materialEditor.ShaderProperty(stencilComparison, BaseStyles.stencilComparison, 2);
materialEditor.ShaderProperty(stencilOperation, BaseStyles.stencilOperation, 2);
materialEditor.ShaderProperty(stencilFail, BaseStyles.stencilFail, 2);
materialEditor.ShaderProperty(stencilZFail, BaseStyles.stencilZFail, 2);
}
else
{
// When stencil is disable, revert to the default stencil operations. Note, when tested on D3D11 hardware the stencil state
// is still set even when the CompareFunction.Disabled is selected, but this does not seem to affect performance.
material.SetInt(BaseStyles.stencilComparisonName, (int)CompareFunction.Disabled);
material.SetInt(BaseStyles.stencilOperationName, (int)StencilOp.Keep);
material.SetInt(BaseStyles.stencilFailName, (int)StencilOp.Keep);
material.SetInt(BaseStyles.stencilZFailName, (int)StencilOp.Keep);
}
EditorGUILayout.Space();
}
protected static void SetupMaterialWithRenderingMode(Material material, RenderingMode mode, CustomRenderingMode customMode, int renderQueueOverride)
{
// If we aren't switching to Custom, then set default values for all RenderingMode types. Otherwise keep whatever user had before
if (mode != RenderingMode.Custom)
{
material.SetInt(BaseStyles.blendOperationName, (int)BlendOp.Add);
material.SetInt(BaseStyles.depthTestName, (int)CompareFunction.LessEqual);
//material.SetFloat(BaseStyles.depthOffsetFactorName, 0.0f);
//material.SetFloat(BaseStyles.depthOffsetUnitsName, 0.0f);
material.SetInt(BaseStyles.colorWriteMaskName, (int)ColorWriteMask.All);
}
switch (mode)
{
case RenderingMode.Opaque:
{
material.SetOverrideTag(BaseStyles.renderTypeName, BaseStyles.renderingModeNames[(int)RenderingMode.Opaque]);
material.SetInt(BaseStyles.customRenderingModeName, (int)CustomRenderingMode.Opaque);
material.SetInt(BaseStyles.sourceBlendName, (int)BlendMode.One);
material.SetInt(BaseStyles.destinationBlendName, (int)BlendMode.Zero);
material.SetInt(BaseStyles.blendOperationName, (int)BlendOp.Add);
material.SetInt(BaseStyles.depthTestName, (int)CompareFunction.LessEqual);
material.SetInt(BaseStyles.depthWriteName, (int)DepthWrite.On);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.colorWriteMaskName, (int)ColorWriteMask.All);
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.DisableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.renderQueue = (renderQueueOverride >= 0) ? renderQueueOverride : (int)RenderQueue.Geometry;
}
break;
case RenderingMode.Cutout:
{
material.SetOverrideTag(BaseStyles.renderTypeName, BaseStyles.renderingModeNames[(int)RenderingMode.Cutout]);
material.SetInt(BaseStyles.customRenderingModeName, (int)CustomRenderingMode.Cutout);
material.SetInt(BaseStyles.sourceBlendName, (int)BlendMode.One);
material.SetInt(BaseStyles.destinationBlendName, (int)BlendMode.Zero);
material.SetInt(BaseStyles.blendOperationName, (int)BlendOp.Add);
material.SetInt(BaseStyles.depthTestName, (int)CompareFunction.LessEqual);
material.SetInt(BaseStyles.depthWriteName, (int)DepthWrite.On);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.On);
material.SetInt(BaseStyles.colorWriteMaskName, (int)ColorWriteMask.All);
material.EnableKeyword(BaseStyles.alphaTestOnName);
material.DisableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.renderQueue = (renderQueueOverride >= 0) ? renderQueueOverride : (int)RenderQueue.AlphaTest;
}
break;
case RenderingMode.Fade:
{
material.SetOverrideTag(BaseStyles.renderTypeName, BaseStyles.renderingModeNames[(int)RenderingMode.Fade]);
material.SetInt(BaseStyles.customRenderingModeName, (int)CustomRenderingMode.Fade);
material.SetInt(BaseStyles.sourceBlendName, (int)BlendMode.SrcAlpha);
material.SetInt(BaseStyles.destinationBlendName, (int)BlendMode.OneMinusSrcAlpha);
material.SetInt(BaseStyles.blendOperationName, (int)BlendOp.Add);
material.SetInt(BaseStyles.depthTestName, (int)CompareFunction.LessEqual);
material.SetInt(BaseStyles.depthWriteName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.colorWriteMaskName, (int)ColorWriteMask.All);
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.EnableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.renderQueue = (renderQueueOverride >= 0) ? renderQueueOverride : (int)RenderQueue.Transparent;
}
break;
case RenderingMode.Transparent:
{
material.SetOverrideTag(BaseStyles.renderTypeName, BaseStyles.renderingModeNames[(int)RenderingMode.Fade]);
material.SetInt(BaseStyles.customRenderingModeName, (int)CustomRenderingMode.Fade);
material.SetInt(BaseStyles.sourceBlendName, (int)BlendMode.One);
material.SetInt(BaseStyles.destinationBlendName, (int)BlendMode.OneMinusSrcAlpha);
material.SetInt(BaseStyles.blendOperationName, (int)BlendOp.Add);
material.SetInt(BaseStyles.depthTestName, (int)CompareFunction.LessEqual);
material.SetInt(BaseStyles.depthWriteName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.colorWriteMaskName, (int)ColorWriteMask.All);
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.DisableKeyword(BaseStyles.alphaBlendOnName);
material.EnableKeyword(BaseStyles.alphaPremultiplyOnName);
material.renderQueue = (renderQueueOverride >= 0) ? renderQueueOverride : (int)RenderQueue.Transparent;
}
break;
case RenderingMode.Additive:
{
material.SetOverrideTag(BaseStyles.renderTypeName, BaseStyles.renderingModeNames[(int)RenderingMode.Fade]);
material.SetInt(BaseStyles.customRenderingModeName, (int)CustomRenderingMode.Fade);
material.SetInt(BaseStyles.sourceBlendName, (int)BlendMode.One);
material.SetInt(BaseStyles.destinationBlendName, (int)BlendMode.One);
material.SetInt(BaseStyles.blendOperationName, (int)BlendOp.Add);
material.SetInt(BaseStyles.depthTestName, (int)CompareFunction.LessEqual);
material.SetInt(BaseStyles.depthWriteName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
material.SetInt(BaseStyles.colorWriteMaskName, (int)ColorWriteMask.All);
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.EnableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.renderQueue = (renderQueueOverride >= 0) ? renderQueueOverride : (int)RenderQueue.Transparent;
}
break;
case RenderingMode.Custom:
{
material.SetOverrideTag(BaseStyles.renderTypeName, BaseStyles.customRenderingModeNames[(int)customMode]);
// _SrcBlend, _DstBlend, _BlendOp, _ZTest, _ZWrite, _ColorWriteMask are controlled by UI.
switch (customMode)
{
case CustomRenderingMode.Opaque:
{
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.DisableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
}
break;
case CustomRenderingMode.Cutout:
{
material.EnableKeyword(BaseStyles.alphaTestOnName);
material.DisableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.On);
}
break;
case CustomRenderingMode.Fade:
{
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.EnableKeyword(BaseStyles.alphaBlendOnName);
material.DisableKeyword(BaseStyles.alphaPremultiplyOnName);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
}
break;
case CustomRenderingMode.Premultiplied:
{
material.DisableKeyword(BaseStyles.alphaTestOnName);
material.DisableKeyword(BaseStyles.alphaBlendOnName);
material.EnableKeyword(BaseStyles.alphaPremultiplyOnName);
material.SetInt(BaseStyles.alphaToMaskName, (int)DepthWrite.Off);
}
break;
}
material.renderQueue = (renderQueueOverride >= 0) ? renderQueueOverride : material.renderQueue;
}
break;
}
// If Stencil is set to NotEqual, raise the queue by 1.
if (material.GetInt("_StencilComp") == (int)CompareFunction.NotEqual)
{
material.renderQueue += 1;
}
}
protected static bool PropertyEnabled(MaterialProperty property)
{
return (property.floatValue != 0.0f);
}
protected static Texture GetTextureProperty(Material material, string propertyName)
{
if (material.HasProperty(propertyName))
{
return material.GetTexture(propertyName);
}
return null;
}
protected static float? GetFloatProperty(Material material, string propertyName)
{
if (material.HasProperty(propertyName))
{
return material.GetFloat(propertyName);
}
return null;
}
protected static int? GetIntProperty(Material material, string propertyName)
{
if (material.HasProperty(propertyName))
{
return material.GetInt(propertyName);
}
return null;
}
protected static Vector4? GetVectorProperty(Material material, string propertyName)
{
if (material.HasProperty(propertyName))
{
return material.GetVector(propertyName);
}
return null;
}
protected static Color? GetColorProperty(Material material, string propertyName)
{
if (material.HasProperty(propertyName))
{
return material.GetColor(propertyName);
}
return null;
}
protected static void SetShaderFeatureActive(Material material, string keywordName, string propertyName, float? propertyValue)
{
if (propertyValue.HasValue)
{
if (keywordName != null)
{
if (!propertyValue.Value.Equals(0.0f))
{
material.EnableKeyword(keywordName);
}
else
{
material.DisableKeyword(keywordName);
}
}
material.SetFloat(propertyName, propertyValue.Value);
}
}
protected static void SetFloatProperty(Material material, string keywordName, string propertyName, float? propertyValue)
{
if (propertyValue.HasValue)
{
if (keywordName != null)
{
if (propertyValue.Value != 0.0f)
{
material.EnableKeyword(keywordName);
}
else
{
material.DisableKeyword(keywordName);
}
}
material.SetFloat(propertyName, propertyValue.Value);
}
}
protected static void SetFloatProperty(Material material, string propertyName, float? propertyValue)
{
if (propertyValue.HasValue)
{
material.SetFloat(propertyName, propertyValue.Value);
}
}
protected static void SetVectorProperty(Material material, string propertyName, Vector4? propertyValue)
{
if (propertyValue.HasValue)
{
material.SetVector(propertyName, propertyValue.Value);
}
}
protected static void SetColorProperty(Material material, string propertyName, Color? propertyValue)
{
if (propertyValue.HasValue)
{
material.SetColor(propertyName, propertyValue.Value);
}
}
protected static void SetIntProperty(Material material, string propertyName, int? propertyValue)
{
if (propertyValue.HasValue)
{
material.SetInt(propertyName, propertyValue.Value);
}
}
protected static void SetKeyword(Material m, string keyword, bool state)
{
if (state)
m.EnableKeyword(keyword);
else
m.DisableKeyword(keyword);
}
}
}