using System.Collections.Generic; using UnityEngine; /* * The MIT License * * Copyright 2018-2021 whiteflare. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if UNITY_EDITOR using System.Linq; using UnityEditor; namespace UnlitWF { public class WF_DebugViewEditor : ShaderGUI { public const string SHADER_NAME_DEBUGVIEW = "UnlitWF/Debug/WF_DebugView"; public const string TAG_PREV_SHADER = "PrevShader"; public const string TAG_PREV_QUEUE = "PrevQueue"; [MenuItem(MenuPathString.MATERIAL_DEBUGVIEW)] public static void ChangeFromMenu(MenuCommand cmd) { WFCommonUtility.ChangeShader(SHADER_NAME_DEBUGVIEW, cmd.context as Material); } [MenuItem(MenuPathString.TOOLS_DEBUGVIEW)] [MenuItem(MenuPathString.ASSETS_DEBUGVIEW)] private static void ChangeFromMenu() { foreach(var mat in Selection.GetFiltered(SelectionMode.Assets)) { WFCommonUtility.ChangeShader(SHADER_NAME_DEBUGVIEW, mat); } } public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) { PreChangeShader(material, oldShader, newShader); // newShaderの割り当て base.AssignNewShaderToMaterial(material, oldShader, newShader); PostChangeShader(material, oldShader, newShader); } public static void PreChangeShader(Material material, Shader oldShader, Shader newShader) { // 古いシェーダ名の保存に OverrideTag を利用する if (material != null && oldShader != null && !IsSupportedShader(oldShader)) { material.SetOverrideTag(TAG_PREV_SHADER, oldShader.name); material.SetOverrideTag(TAG_PREV_QUEUE, material.renderQueue.ToString()); } } public static void PostChangeShader(Material material, Shader oldShader, Shader newShader) { // nop } public static bool IsSupportedShader(Shader shader) { return WFCommonUtility.IsSupportedShader(shader) && shader.name.Contains("WF_DebugView"); } public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { materialEditor.SetDefaultGUIWidths(); // 元シェーダに戻すボタン OnGuiSub_SwitchPrevShaderButton(materialEditor); var mat = materialEditor.target as Material; var mats = WFCommonUtility.AsMaterials(materialEditor.targets); // モード変更メニュー表示 foreach (var section in sections) { GUI.Label(EditorGUI.IndentedRect(EditorGUILayout.GetControlRect()), section.name, EditorStyles.boldLabel); foreach (var mode in section.modes) { bool active = mode.IsActive(mat); EditorGUI.showMixedValue = mode.IsMixedValue(mats); EditorGUI.BeginChangeCheck(); active = EditorGUILayout.Toggle(mode.displayName, active); if (EditorGUI.EndChangeCheck()) { mode.SetActive(mats); } EditorGUI.showMixedValue = false; } EditorGUILayout.Space(); } // モード変更以外のプロパティを表示 foreach (var p in properties) { if (!p.name.StartsWith("_Mode")) { materialEditor.ShaderProperty(p, p.displayName); } } EditorGUILayout.Space(); GUI.Label(EditorGUI.IndentedRect(EditorGUILayout.GetControlRect()), "Advanced Options", EditorStyles.boldLabel); materialEditor.RenderQueueField(); materialEditor.EnableInstancingField(); EditorGUILayout.Space(); // 一番下にも、元シェーダに戻すボタンを置く OnGuiSub_SwitchPrevShaderButton(materialEditor); } private static void OnGuiSub_SwitchPrevShaderButton(MaterialEditor materialEditor) { // 編集中のマテリアルの配列 var mats = WFCommonUtility.AsMaterials(materialEditor.targets); // PrevShader タグを持っているものがひとつでもあればボタン表示 if (mats.Select(m => m.GetTag(TAG_PREV_SHADER, false)).Any(tag => !string.IsNullOrWhiteSpace(tag))) { if (GUI.Button(EditorGUI.IndentedRect(EditorGUILayout.GetControlRect()), "Switch Prev Shader")) { // 元のシェーダに戻す Undo.RecordObjects(mats, "change shader"); // それぞれのマテリアルに設定された PrevShader へと切り替え foreach (var mat in mats) { var name = mat.GetTag(TAG_PREV_SHADER, false); var queue = mat.GetTag(TAG_PREV_QUEUE, false); // DebugViewの保存に使っているタグはクリア ClearDebugOverrideTag(mat); // シェーダ切り替え WFCommonUtility.ChangeShader(name, mat); // queue戻し if (queue != null && int.TryParse(queue, out int numQueue)) { mat.renderQueue = numQueue; } } } EditorGUILayout.Space(); } } public static void ClearDebugOverrideTag(Material mat) { if (mat != null) { mat.SetOverrideTag(TAG_PREV_SHADER, ""); mat.SetOverrideTag(TAG_PREV_QUEUE, ""); } } private readonly List sections = new List() { new DebugModeSection("Fill Color", new List(){ new DebugMode("White", "_ModeColor", 1), new DebugMode("Black", "_ModeColor", 2), new DebugMode("Magenta", "_ModeColor", 3), new DebugMode("Discard", "_ModeColor", 4), new DebugMode("Vertex Color", "_ModeColor", 5), }), new DebugModeSection("Show Positions", new List(){ new DebugMode("Local space", "_ModePos", 1), new DebugMode("World space", "_ModePos", 2), }), new DebugModeSection("Show UVs", new List(){ new DebugMode("UV1", "_ModeUV", 1), new DebugMode("UV2", "_ModeUV", 2), new DebugMode("UV3", "_ModeUV", 3), new DebugMode("UV4", "_ModeUV", 4), new DebugMode("Lightmap UV", "_ModeUV", 5), new DebugMode("Dynamic Lightmap UV", "_ModeUV", 6), }), new DebugModeSection("Show Normals", new List(){ new DebugMode("Normal (local space)", "_ModeNormal", 1), new DebugMode("Tangent (local space)", "_ModeNormal", 2), new DebugMode("BiTangent (local space)", "_ModeNormal", 3), new DebugMode("Normal (world space)", "_ModeNormal", 4), new DebugMode("Tangent (world space)", "_ModeNormal", 5), new DebugMode("BiTangent (world space)", "_ModeNormal", 6), }), new DebugModeSection("Show Parallel Normals", new List(){ new DebugMode("view Parallel Normal and Tangent", "_ModeParaNormal", 1), }), new DebugModeSection("Show Lightings", new List(){ new DebugMode("Light 0", "_ModeLight", 1), new DebugMode("Light 1-4", "_ModeLight", 2), new DebugMode("ShadeSH9", "_ModeLight", 3), }), new DebugModeSection("Show LightMaps", new List(){ new DebugMode("Lightmap", "_ModeLightMap", 1), new DebugMode("Dynamic Lightmap", "_ModeLightMap", 2), }), new DebugModeSection("Show SpecCubes", new List(){ new DebugMode("SpecCube0", "_ModeSpecCube", 1), new DebugMode("SpecCube1", "_ModeSpecCube", 2), }), }; class DebugModeSection { public readonly string name; public readonly List modes; public DebugModeSection(string name, List listMode) { this.name = name; this.modes = listMode; } } class DebugMode { public readonly string displayName; public readonly string propertyName; public readonly int value; public DebugMode(string displayName, string propertyName, int value) { this.displayName = displayName; this.propertyName = propertyName; this.value = value; } public bool IsActive(Material mat) { return mat != null && mat.HasProperty(propertyName) && mat.GetInt(propertyName) == value; } public bool IsMixedValue(Material[] targets) { return WFCommonUtility.AsMaterials(targets).Select(mat => IsActive(mat)).Distinct().Count() == 2; } public void SetActive(Material[] targets) { foreach(var mat in WFCommonUtility.AsMaterials(targets)) { // リセット foreach (var p in ShaderMaterialProperty.AsList(mat).Where(p => p.Name.StartsWith("_Mode"))) { mat.SetInt(p.Name, 0); } // セット mat.SetInt(propertyName, value); } } } } } #endif