using System; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; namespace MToon { public class MToonInspector : ShaderGUI { private const float RoundsToDegree = 360f; private const float RoundsToRadian = (float) Math.PI * 2f; private static bool isAdvancedLightingPanelFoldout = false; private static EditorRotationUnit editorRotationUnit = EditorRotationUnit.Rounds; private MaterialProperty _version; private MaterialProperty _blendMode; private MaterialProperty _bumpMap; private MaterialProperty _bumpScale; private MaterialProperty _color; private MaterialProperty _cullMode; // private MaterialProperty _outlineCullMode; private MaterialProperty _cutoff; private MaterialProperty _debugMode; private MaterialProperty _emissionColor; private MaterialProperty _emissionMap; private MaterialProperty _lightColorAttenuation; private MaterialProperty _indirectLightIntensity; private MaterialProperty _mainTex; private MaterialProperty _outlineColor; private MaterialProperty _outlineColorMode; private MaterialProperty _outlineLightingMix; private MaterialProperty _outlineWidth; private MaterialProperty _outlineScaledMaxDistance; private MaterialProperty _outlineWidthMode; private MaterialProperty _outlineWidthTexture; private MaterialProperty _receiveShadowRate; private MaterialProperty _receiveShadowTexture; private MaterialProperty _shadingGradeRate; private MaterialProperty _shadingGradeTexture; private MaterialProperty _shadeColor; private MaterialProperty _shadeShift; private MaterialProperty _shadeTexture; private MaterialProperty _shadeToony; private MaterialProperty _sphereAdd; private MaterialProperty _rimColor; private MaterialProperty _rimTexture; private MaterialProperty _rimLightingMix; private MaterialProperty _rimFresnelPower; private MaterialProperty _rimLift; private MaterialProperty _uvAnimMaskTexture; private MaterialProperty _uvAnimScrollX; private MaterialProperty _uvAnimScrollY; private MaterialProperty _uvAnimRotation; public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { _version = FindProperty(Utils.PropVersion, properties); _debugMode = FindProperty(Utils.PropDebugMode, properties); _outlineWidthMode = FindProperty(Utils.PropOutlineWidthMode, properties); _outlineColorMode = FindProperty(Utils.PropOutlineColorMode, properties); _blendMode = FindProperty(Utils.PropBlendMode, properties); _cullMode = FindProperty(Utils.PropCullMode, properties); // _outlineCullMode = FindProperty(Utils.PropOutlineCullMode, properties); _cutoff = FindProperty(Utils.PropCutoff, properties); _color = FindProperty(Utils.PropColor, properties); _shadeColor = FindProperty(Utils.PropShadeColor, properties); _mainTex = FindProperty(Utils.PropMainTex, properties); _shadeTexture = FindProperty(Utils.PropShadeTexture, properties); _bumpScale = FindProperty(Utils.PropBumpScale, properties); _bumpMap = FindProperty(Utils.PropBumpMap, properties); _receiveShadowRate = FindProperty(Utils.PropReceiveShadowRate, properties); _receiveShadowTexture = FindProperty(Utils.PropReceiveShadowTexture, properties); _shadingGradeRate = FindProperty(Utils.PropShadingGradeRate, properties); _shadingGradeTexture = FindProperty(Utils.PropShadingGradeTexture, properties); _shadeShift = FindProperty(Utils.PropShadeShift, properties); _shadeToony = FindProperty(Utils.PropShadeToony, properties); _lightColorAttenuation = FindProperty(Utils.PropLightColorAttenuation, properties); _indirectLightIntensity = FindProperty(Utils.PropIndirectLightIntensity, properties); _rimColor = FindProperty(Utils.PropRimColor, properties); _rimTexture = FindProperty(Utils.PropRimTexture, properties); _rimLightingMix = FindProperty(Utils.PropRimLightingMix, properties); _rimFresnelPower = FindProperty(Utils.PropRimFresnelPower, properties); _rimLift = FindProperty(Utils.PropRimLift, properties); _sphereAdd = FindProperty(Utils.PropSphereAdd, properties); _emissionColor = FindProperty(Utils.PropEmissionColor, properties); _emissionMap = FindProperty(Utils.PropEmissionMap, properties); _outlineWidthTexture = FindProperty(Utils.PropOutlineWidthTexture, properties); _outlineWidth = FindProperty(Utils.PropOutlineWidth, properties); _outlineScaledMaxDistance = FindProperty(Utils.PropOutlineScaledMaxDistance, properties); _outlineColor = FindProperty(Utils.PropOutlineColor, properties); _outlineLightingMix = FindProperty(Utils.PropOutlineLightingMix, properties); _uvAnimMaskTexture = FindProperty(Utils.PropUvAnimMaskTexture, properties); _uvAnimScrollX = FindProperty(Utils.PropUvAnimScrollX, properties); _uvAnimScrollY = FindProperty(Utils.PropUvAnimScrollY, properties); _uvAnimRotation = FindProperty(Utils.PropUvAnimRotation, properties); var materials = materialEditor.targets.Select(x => x as Material).ToArray(); Draw(materialEditor, materials); } private void Draw(MaterialEditor materialEditor, Material[] materials) { EditorGUI.BeginChangeCheck(); { _version.floatValue = Utils.VersionNumber; EditorGUILayout.LabelField("Rendering", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { EditorGUILayout.LabelField("Mode", EditorStyles.boldLabel); if (PopupEnum("Rendering Type", _blendMode, materialEditor)) { ModeChanged(materials, isBlendModeChangedByUser: true); } if ((RenderMode) _blendMode.floatValue == RenderMode.TransparentWithZWrite) { EditorGUILayout.HelpBox("TransparentWithZWrite mode can cause problems with rendering.", MessageType.Warning); } if (PopupEnum("Cull Mode", _cullMode, materialEditor)) { ModeChanged(materials); } } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Color", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { EditorGUILayout.LabelField("Texture", EditorStyles.boldLabel); { materialEditor.TexturePropertySingleLine(new GUIContent("Lit Color, Alpha", "Lit (RGB), Alpha (A)"), _mainTex, _color); materialEditor.TexturePropertySingleLine(new GUIContent("Shade Color", "Shade (RGB)"), _shadeTexture, _shadeColor); } var bm = (RenderMode) _blendMode.floatValue; if (bm == RenderMode.Cutout) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Alpha", EditorStyles.boldLabel); { materialEditor.ShaderProperty(_cutoff, "Cutoff"); } } } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Lighting", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { { materialEditor.ShaderProperty(_shadeToony, new GUIContent("Shading Toony", "0.0 is Lambert. Higher value get toony shading.")); // Normal EditorGUI.BeginChangeCheck(); materialEditor.TexturePropertySingleLine(new GUIContent("Normal Map [Normal]", "Normal Map (RGB)"), _bumpMap, _bumpScale); if (EditorGUI.EndChangeCheck()) { materialEditor.RegisterPropertyChangeUndo("BumpEnabledDisabled"); ModeChanged(materials); } } EditorGUILayout.Space(); EditorGUI.indentLevel++; { isAdvancedLightingPanelFoldout = EditorGUILayout.Foldout(isAdvancedLightingPanelFoldout, "Advanced Settings", EditorStyles.boldFont); if (isAdvancedLightingPanelFoldout) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.HelpBox( "The default settings are suitable for Advanced Settings if you want to toony result.", MessageType.Info); if (GUILayout.Button("Use Default")) { _shadeShift.floatValue = 0; _receiveShadowTexture.textureValue = null; _receiveShadowRate.floatValue = 1; _shadingGradeTexture.textureValue = null; _shadingGradeRate.floatValue = 1; _lightColorAttenuation.floatValue = 0; _indirectLightIntensity.floatValue = 0.1f; } EditorGUILayout.EndHorizontal(); materialEditor.ShaderProperty(_shadeShift, new GUIContent("Shading Shift", "Zero is Default. Negative value increase lit area. Positive value increase shade area.")); materialEditor.TexturePropertySingleLine( new GUIContent("Shadow Receive Multiplier", "Texture (R) * Rate. White is Default. Black attenuates shadows."), _receiveShadowTexture, _receiveShadowRate); materialEditor.TexturePropertySingleLine( new GUIContent("Lit & Shade Mixing Multiplier", "Texture (R) * Rate. Compatible with UTS2 ShadingGradeMap. White is Default. Black amplifies shade."), _shadingGradeTexture, _shadingGradeRate); materialEditor.ShaderProperty(_lightColorAttenuation, "LightColor Attenuation"); materialEditor.ShaderProperty(_indirectLightIntensity, "GI Intensity"); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Emission", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { TextureWithHdrColor(materialEditor, "Emission", "Emission (RGB)", _emissionMap, _emissionColor); materialEditor.TexturePropertySingleLine(new GUIContent("MatCap", "MatCap Texture (RGB)"), _sphereAdd); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Rim", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { TextureWithHdrColor(materialEditor, "Color", "Rim Color (RGB)", _rimTexture, _rimColor); materialEditor.DefaultShaderProperty(_rimLightingMix, "Lighting Mix"); materialEditor.ShaderProperty(_rimFresnelPower, new GUIContent("Fresnel Power", "If you increase this value, you get sharpness rim light.")); materialEditor.ShaderProperty(_rimLift, new GUIContent("Lift", "If you increase this value, you can lift rim light.")); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Outline", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { // Outline EditorGUILayout.LabelField("Width", EditorStyles.boldLabel); { if (PopupEnum("Mode", _outlineWidthMode, materialEditor)) { ModeChanged(materials); } if ((RenderMode) _blendMode.floatValue == RenderMode.Transparent && (OutlineWidthMode) _outlineWidthMode.floatValue != OutlineWidthMode.None) { EditorGUILayout.HelpBox("Outline with Transparent material cause problem with rendering.", MessageType.Warning); } var widthMode = (OutlineWidthMode) _outlineWidthMode.floatValue; if (widthMode != OutlineWidthMode.None) { materialEditor.TexturePropertySingleLine( new GUIContent("Width", "Outline Width Texture (RGB)"), _outlineWidthTexture, _outlineWidth); } if (widthMode == OutlineWidthMode.ScreenCoordinates) { materialEditor.ShaderProperty(_outlineScaledMaxDistance, "Width Scaled Max Distance"); } } EditorGUILayout.Space(); EditorGUILayout.LabelField("Color", EditorStyles.boldLabel); { var widthMode = (OutlineWidthMode) _outlineWidthMode.floatValue; if (widthMode != OutlineWidthMode.None) { EditorGUI.BeginChangeCheck(); if (PopupEnum("Mode", _outlineColorMode, materialEditor)) { ModeChanged(materials); } var colorMode = (OutlineColorMode) _outlineColorMode.floatValue; materialEditor.ShaderProperty(_outlineColor, "Color"); if (colorMode == OutlineColorMode.MixedLighting) materialEditor.DefaultShaderProperty(_outlineLightingMix, "Lighting Mix"); } } } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("UV Coordinates", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { // UV EditorGUILayout.LabelField("Scale & Offset", EditorStyles.boldLabel); { materialEditor.TextureScaleOffsetProperty(_mainTex); } EditorGUILayout.Space(); EditorGUILayout.LabelField("Auto Animation", EditorStyles.boldLabel); { materialEditor.TexturePropertySingleLine(new GUIContent("Mask", "Auto Animation Mask Texture (R)"), _uvAnimMaskTexture); materialEditor.ShaderProperty(_uvAnimScrollX, "Scroll X (per second)"); materialEditor.ShaderProperty(_uvAnimScrollY, "Scroll Y (per second)"); { var control = EditorGUILayout.GetControlRect(hasLabel: true); const int popupMargin = 5; const int popupWidth = 80; var floatControl = new Rect(control); floatControl.width -= popupMargin + popupWidth; var popupControl = new Rect(control); popupControl.x = floatControl.x + floatControl.width + popupMargin; popupControl.width = popupWidth; EditorGUI.BeginChangeCheck(); var inspectorRotationValue = GetInspectorRotationValue(editorRotationUnit, _uvAnimRotation.floatValue); inspectorRotationValue = EditorGUI.FloatField(floatControl, "Rotation value (per second)", inspectorRotationValue); if (EditorGUI.EndChangeCheck()) { materialEditor.RegisterPropertyChangeUndo("UvAnimRotationValueChanged"); _uvAnimRotation.floatValue = GetRawRotationValue(editorRotationUnit, inspectorRotationValue); } editorRotationUnit = (EditorRotationUnit) EditorGUI.EnumPopup(popupControl, editorRotationUnit); } } } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Options", EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); { EditorGUILayout.LabelField("Debugging Options", EditorStyles.boldLabel); { if (PopupEnum("Visualize", _debugMode, materialEditor)) { ModeChanged(materials); } } EditorGUILayout.Space(); EditorGUILayout.LabelField("Advanced Options", EditorStyles.boldLabel); { #if UNITY_5_6_OR_NEWER // materialEditor.EnableInstancingField(); materialEditor.DoubleSidedGIField(); #endif EditorGUI.BeginChangeCheck(); materialEditor.RenderQueueField(); if (EditorGUI.EndChangeCheck()) { ModeChanged(materials); } } } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); } EditorGUI.EndChangeCheck(); } public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) { base.AssignNewShaderToMaterial(material, oldShader, newShader); Utils.ValidateProperties(material, isBlendModeChangedByUser: true); } private static void ModeChanged(Material[] materials, bool isBlendModeChangedByUser = false) { foreach (var material in materials) { Utils.ValidateProperties(material, isBlendModeChangedByUser); } } private static bool PopupEnum(string name, MaterialProperty property, MaterialEditor editor) where T : struct { EditorGUI.showMixedValue = property.hasMixedValue; EditorGUI.BeginChangeCheck(); var ret = EditorGUILayout.Popup(name, (int) property.floatValue, Enum.GetNames(typeof(T))); var changed = EditorGUI.EndChangeCheck(); if (changed) { editor.RegisterPropertyChangeUndo("EnumPopUp"); property.floatValue = ret; } EditorGUI.showMixedValue = false; return changed; } private static void TextureWithHdrColor(MaterialEditor materialEditor, string label, string description, MaterialProperty texProp, MaterialProperty colorProp) { materialEditor.TexturePropertyWithHDRColor(new GUIContent(label, description), texProp, colorProp, #if UNITY_2018_1_OR_NEWER #else new ColorPickerHDRConfig(minBrightness: 0, maxBrightness: 10, minExposureValue: -10, maxExposureValue: 10), #endif showAlpha: false); } private static float GetRawRotationValue(EditorRotationUnit unit, float inspectorValue) { switch (unit) { case EditorRotationUnit.Rounds: return inspectorValue; case EditorRotationUnit.Degrees: return inspectorValue / RoundsToDegree; case EditorRotationUnit.Radians: return inspectorValue / RoundsToRadian; default: throw new ArgumentOutOfRangeException(); } } private static float GetInspectorRotationValue(EditorRotationUnit unit, float rawValue) { switch (unit) { case EditorRotationUnit.Rounds: return rawValue; case EditorRotationUnit.Degrees: return rawValue * RoundsToDegree; case EditorRotationUnit.Radians: return rawValue * RoundsToRadian; default: throw new ArgumentOutOfRangeException(); } } } }