Beispiel #1
0
    private static Texture2D GetCustomTexture(string name)
    {
        var uiName = name + (EditorGUIUtility.isProSkin ? "pro" : "");

        if (CustomEditorTextures.ContainsKey(uiName))
        {
            return(CustomEditorTextures[uiName]);
        }

        var rootPath = TCP2_Utils.FindReadmePath(true);

        Texture2D texture = null;

        //load pro version
        if (EditorGUIUtility.isProSkin)
        {
            texture = AssetDatabase.LoadAssetAtPath("Assets" + rootPath + "/Editor/Icons/" + name + "_Pro.png", typeof(Texture2D)) as Texture2D;
        }

        //load default version
        if (texture == null)
        {
            texture = AssetDatabase.LoadAssetAtPath("Assets" + rootPath + "/Editor/Icons/" + name + ".png", typeof(Texture2D)) as Texture2D;
        }

        if (texture != null)
        {
            CustomEditorTextures.Add(uiName, texture);
            return(texture);
        }

        return(null);
    }
Beispiel #2
0
    //Get a PackedFile from a system file path
    static public PackedFile PackFile(string windowsPath)
    {
        if (!File.Exists(windowsPath))
        {
            EditorApplication.Beep();
            Debug.LogError("[TCP2 PackFile] File doesn't exist:" + windowsPath);
            return(null);
        }

        //Get properties
        // Content
        string content = File.ReadAllText(windowsPath, System.Text.Encoding.UTF8);
        // File relative path
        string tcpRoot = TCP2_Utils.FindReadmePath();

        if (tcpRoot == null)
        {
            EditorApplication.Beep();
            Debug.LogError("[TCP2 PackFile] Can't find TCP2 Readme file!\nCan't determine root folder to pack/unpack files.");
            return(null);
        }
        tcpRoot = UnityToSystemPath(tcpRoot);
        string relativePath = windowsPath.Replace(tcpRoot, "");

        PackedFile pf = new PackedFile(relativePath, content);

        return(pf);
    }
Beispiel #3
0
    static public string OpenFolderPanel_ProjectPath(string label)
    {
        string output = null;
        string path   = EditorUtility.OpenFolderPanel(label, Application.dataPath, "");

        if (!string.IsNullOrEmpty(path))
        {
            bool validPath = TCP2_Utils.SystemToUnityPath(ref path);
            if (validPath)
            {
                if (path == "Assets")
                {
                    output = "/";
                }
                else
                {
                    output = path.Substring("Assets/".Length);
                }
            }
            else
            {
                EditorApplication.Beep();
                EditorUtility.DisplayDialog("Invalid Path", "The selected path is invalid.\n\nPlease select a folder inside the \"Assets\" folder of your project!", "Ok");
            }
        }

        return(output);
    }
Beispiel #4
0
    private TextAsset[] LoadAllTemplates()
    {
        var list = new List <TextAsset>();

        var systemPath = Application.dataPath + @"/JMO Assets/Toony Colors Pro/Editor/Shader Templates/";

        if (!Directory.Exists(systemPath))
        {
            var rootDir = TCP2_Utils.FindReadmePath();
            systemPath = rootDir.Replace(@"\", "/") + "/Editor/Shader Templates/";
        }

        if (Directory.Exists(systemPath))
        {
            var txtFiles = Directory.GetFiles(systemPath, "*.txt", SearchOption.AllDirectories);

            foreach (var sysPath in txtFiles)
            {
                var unityPath = sysPath;
                if (TCP2_Utils.SystemToUnityPath(ref unityPath))
                {
                    var textAsset = AssetDatabase.LoadAssetAtPath <TextAsset>(unityPath);
                    if (textAsset != null && !list.Contains(textAsset))
                    {
                        list.Add(textAsset);
                    }
                }
            }

            list.Sort((x, y) => x.name.CompareTo(y.name));
            return(list.ToArray());
        }

        return(null);
    }
    //Process #KEYWORDS section from Template
    private static string ProcessKeywords(string line, TCP2_Config config, ref List <string> features, ref List <string> flags, ref Dictionary <string, string> keywords, ref int i, ref int depth, ref List <bool> stack, ref List <bool> done)
    {
        if (line.Contains("///"))
        {
            ProcessCondition(line, ref features, ref i, ref depth, ref stack, ref done);
        }
        //Regular line
        else
        {
            if (string.IsNullOrEmpty(line))
            {
                return(null);
            }

            //Inside valid block
            if ((depth >= 0 && stack[depth]) || depth < 0)
            {
                var parts = line.Split(new[] { "\t" }, StringSplitOptions.RemoveEmptyEntries);
                switch (parts[0])
                {
                case "set":
                    if (keywords.ContainsKey(parts[1]))
                    {
                        keywords[parts[1]] = parts[2];
                    }
                    else
                    {
                        keywords.Add(parts[1], parts[2]);
                    }
                    break;

                case "enable_kw":               TCP2_Utils.AddIfMissing(features, parts[1]); break;

                case "disable_kw":              TCP2_Utils.RemoveIfExists(features, parts[1]); break;

                case "enable_flag":             TCP2_Utils.AddIfMissing(flags, parts[1]); break;

                case "disable_flag":    TCP2_Utils.RemoveIfExists(flags, parts[1]); break;

                //Keywords that will also be modified in the Config
                case "enable_kw_config":
                    TCP2_Utils.AddIfMissing(features, parts[1]);
                    TCP2_Utils.AddIfMissing(config.Features, parts[1]);
                    break;

                case "disable_kw_config":
                    TCP2_Utils.RemoveIfExists(features, parts[1]);
                    TCP2_Utils.RemoveIfExists(config.Features, parts[1]);
                    break;
                }
            }
        }

        return(null);
    }
Beispiel #6
0
    private static void UnpackShaders(string filter)
    {
        const string packedShadersGuid = "552f9a41dd13c0c44a9bb1aad0ec3598";
        var          path = AssetDatabase.GUIDToAssetPath(packedShadersGuid);

        if (string.IsNullOrEmpty(path))
        {
            EditorApplication.Beep();
            Debug.LogError("[TCP2 Unpack Shaders] Couldn't find file: \"TCP2 Packed Shaders.tcp2data\"\nPlease reimport Toony Colors Pro 2.");
            return;
        }

        var fullPath = Application.dataPath + path.Substring("Assets".Length);

        if (File.Exists(fullPath))
        {
            var files = TCP2_Utils.ExtractArchive(fullPath, filter);

            var @continue = 0;
            if (files.Length > 8)
            {
                do
                {
                    @continue = EditorUtility.DisplayDialogComplex("TCP2 : Unpack Shaders", "You are about to import " + files.Length + " shaders in Unity.\nIt could take a few minutes!\nContinue?", "Yes", "No", "Help");
                    if (@continue == 2)
                    {
                        TCP2_GUI.OpenHelpFor("Unpack Shaders");
                    }
                }while(@continue == 2);
            }

            if (@continue == 0 && files.Length > 0)
            {
                var tcpRoot = TCP2_Utils.FindReadmePath();
                foreach (var f in files)
                {
                    var filePath = tcpRoot + f.path;
                    var fileDir  = Path.GetDirectoryName(filePath);
                    if (!Directory.Exists(fileDir))
                    {
                        Directory.CreateDirectory(fileDir);
                    }
                    File.WriteAllText(filePath, f.content);
                }

                Debug.Log("Toony Colors Pro 2 - Unpack Shaders:\n" + files.Length + (files.Length > 1 ? " shaders extracted." : " shader extracted."));
                AssetDatabase.Refresh();
            }

            if (files.Length == 0)
            {
                Debug.Log("Toony Colors Pro 2 - Unpack Shaders:\nNothing to unpack. Shaders are probably already unpacked!");
            }
        }
    }
Beispiel #7
0
    private static void UnpackShaders(string filter)
    {
        var archFiles = Directory.GetFiles(TCP2_Utils.UnityToSystemPath(Application.dataPath), "TCP2 Packed Shaders.tcp2data", SearchOption.AllDirectories);

        if (archFiles == null || archFiles.Length == 0)
        {
            EditorApplication.Beep();
            Debug.LogError("[TCP2 Unpack Shaders] Couldn't find file: \"TCP2 Packed Shaders.tcp2data\"\nPlease reimport Toony Colors Pro 2.");
            return;
        }
        var archivePath = archFiles[0];

        if (archivePath.EndsWith(".tcp2data"))
        {
            var files = TCP2_Utils.ExtractArchive(archivePath, filter);

            var @continue = 0;
            if (files.Length > 8)
            {
                do
                {
                    @continue = EditorUtility.DisplayDialogComplex("TCP2 : Unpack Shaders", "You are about to import " + files.Length + " shaders in Unity.\nIt could take a few minutes!\nContinue?", "Yes", "No", "Help");
                    if (@continue == 2)
                    {
                        TCP2_GUI.OpenHelpFor("Unpack Shaders");
                    }
                }while(@continue == 2);
            }

            if (@continue == 0 && files.Length > 0)
            {
                var tcpRoot = TCP2_Utils.FindReadmePath();
                foreach (var f in files)
                {
                    var filePath = tcpRoot + f.path;
                    var fileDir  = Path.GetDirectoryName(filePath);
                    if (!Directory.Exists(fileDir))
                    {
                        Directory.CreateDirectory(fileDir);
                    }
                    File.WriteAllText(filePath, f.content);
                }

                Debug.Log("Toony Colors Pro 2 - Unpack Shaders:\n" + files.Length + (files.Length > 1 ? " shaders extracted." : " shader extracted."));
                AssetDatabase.Refresh();
            }

            if (files.Length == 0)
            {
                Debug.Log("Toony Colors Pro 2 - Unpack Shaders:\nNothing to unpack. Shaders are probably already unpacked!");
            }
        }
    }
Beispiel #8
0
    Texture2D TryFindDefaultRampTexture()
    {
        string rootPath = TCP2_Utils.FindReadmePath(true);

        if (!string.IsNullOrEmpty(rootPath))
        {
            string defaultTexPath = "Assets" + rootPath + "/Textures/TCP2_Ramp_3Levels.png";
            return(AssetDatabase.LoadAssetAtPath <Texture2D>(defaultTexPath));
        }

        return(null);
    }
Beispiel #9
0
    static public Texture2D GetHelpBoxIcon(MessageType msgType)
    {
        string iconName = null;

        switch (msgType)
        {
        case MessageType.Error:
            if (errorIconCached != null)
            {
                return(errorIconCached);
            }
            iconName = "TCP2_ErrorIcon";
            break;

        case MessageType.Warning:
            if (warningIconCached != null)
            {
                return(warningIconCached);
            }
            iconName = "TCP2_WarningIcon";
            break;

        case MessageType.Info:
            if (infoIconCached != null)
            {
                return(infoIconCached);
            }
            iconName = "TCP2_InfoIcon";
            break;
        }

        if (string.IsNullOrEmpty(iconName))
        {
            return(null);
        }

        string    rootPath = TCP2_Utils.FindReadmePath(true);
        Texture2D icon     = AssetDatabase.LoadAssetAtPath("Assets" + rootPath + "/Editor/Icons/" + iconName + ".png", typeof(Texture2D)) as Texture2D;

        switch (msgType)
        {
        case MessageType.Error: errorIconCached = icon; break;

        case MessageType.Warning: warningIconCached = icon; break;

        case MessageType.Info: infoIconCached = icon; break;
        }

        return(icon);
    }
Beispiel #10
0
    static public void OpenHelp()
    {
        string rootDir = TCP2_Utils.FindReadmePath();

        if (rootDir == null)
        {
            EditorUtility.DisplayDialog("TCP2 Documentation", "Couldn't find TCP2 root folder! (the readme file is missing)\nYou can still access the documentation manually in the Documentation folder.", "Ok");
        }
        else
        {
            string helpLink = "file:///" + rootDir.Replace(@"\", "/") + "/Documentation/TCP2 Documentation.html";
            Application.OpenURL(helpLink);
        }
    }
Beispiel #11
0
    static public void OpenHelpFor(string helpTopic)
    {
        string rootDir = TCP2_Utils.FindReadmePath();

        if (rootDir == null)
        {
            EditorUtility.DisplayDialog("TCP2 Documentation", "Couldn't find TCP2 root folder! (the readme file is missing)\nYou can still access the documentation manually in the Documentation folder.", "Ok");
        }
        else
        {
            string helpAnchor = helpTopic.Replace("/", "_").Replace(@"\", "_").Replace(" ", "_").ToLowerInvariant() + ".htm";
            string topicLink  = "file:///" + rootDir.Replace(@"\", "/") + "/Documentation/Documentation Data/Anchors/" + helpAnchor;
            Application.OpenURL(topicLink);
        }
    }
    //Process #KEYWORDS section from Template
    static private string ProcessKeywords(string line, ref List <string> features, ref List <string> flags, ref Dictionary <string, string> keywords, ref int i, ref int depth, ref List <bool> stack, ref List <bool> done, bool useNewEvaluationSystem)
    {
        if (line.Contains("///"))
        {
            ProcessCondition(line, ref features, ref i, ref depth, ref stack, ref done, useNewEvaluationSystem);
        }
        //Regular line
        else
        {
            if (string.IsNullOrEmpty(line))
            {
                return(null);
            }

            //Inside valid block
            if ((depth >= 0 && stack[depth]) || depth < 0)
            {
                string[] parts = line.Split(new string[] { "\t" }, System.StringSplitOptions.RemoveEmptyEntries);
                switch (parts[0])
                {
                case "set":
                    if (keywords.ContainsKey(parts[1]))
                    {
                        keywords[parts[1]] = parts[2];
                    }
                    else
                    {
                        keywords.Add(parts[1], parts[2]);
                    }
                    break;

                case "enable_kw":               TCP2_Utils.AddIfMissing(features, parts[1]); break;

                case "disable_kw":              TCP2_Utils.RemoveIfExists(features, parts[1]); break;

                case "enable_flag":             TCP2_Utils.AddIfMissing(flags, parts[1]); break;

                case "disable_flag":    TCP2_Utils.RemoveIfExists(flags, parts[1]); break;
                }
            }
        }

        return(null);
    }
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        mMaterialEditor = materialEditor;

#if SHOW_DEFAULT_INSPECTOR
        base.OnGUI();
        return;
#else
        if (mJustChangedShader)
        {
            mJustChangedShader = false;
            mVariantError      = null;
            Event.current.Use();                        //Avoid layout mismatch error
            SceneView.RepaintAll();
        }

        UpdateFeaturesFromShader();

        //Get material keywords
        List <string> keywordsList   = new List <string>(targetMaterial.shaderKeywords);
        bool          updateKeywords = false;
        bool          updateVariant  = false;

        //Header
        EditorGUILayout.BeginHorizontal();
        TCP2_GUI.HeaderBig("TOONY COLORS PRO 2 - INSPECTOR");
        if (isGeneratedShader && TCP2_GUI.Button(TCP2_GUI.CogIcon, "O", "Open in Shader Generator"))
        {
            if (targetMaterial.shader != null)
            {
                TCP2_ShaderGenerator.OpenWithShader(targetMaterial.shader);
            }
        }
        TCP2_GUI.HelpButton("Unified Shader");
        EditorGUILayout.EndHorizontal();
        TCP2_GUI.Separator();

        if (!string.IsNullOrEmpty(mVariantError))
        {
            EditorGUILayout.HelpBox(mVariantError, MessageType.Error);

            EditorGUILayout.HelpBox("Some of the shaders are packed to avoid super long loading times when you import Toony Colors Pro 2 into Unity.\n\n" +
                                    "You can unpack them by category in the menu:\n\"Tools > Toony Colors Pro 2 > Unpack Shaders > ...\"",
                                    MessageType.Info);
        }

        //Iterate Shader properties
        materialEditor.serializedObject.Update();
        SerializedProperty mShader = materialEditor.serializedObject.FindProperty("m_Shader");
        if (materialEditor.isVisible && !mShader.hasMultipleDifferentValues && mShader.objectReferenceValue != null)
        {
            //Retina display fix
            EditorGUIUtility.labelWidth = TCP2_Utils.ScreenWidthRetina - 120f;
            EditorGUIUtility.fieldWidth = 64f;

            EditorGUI.BeginChangeCheck();

            MaterialProperty[] props = properties;

            //UNFILTERED PARAMETERS ==============================================================

            TCP2_GUI.HeaderAndHelp("BASE", "Base Properties");
            if (ShowFilteredProperties(null, props))
            {
                if (!isGeneratedShader)
                {
                    TCP2_Utils.ShaderKeywordToggle("TCP2_DISABLE_WRAPPED_LIGHT", "Disable Wrapped Lighting", "Disable wrapped lighting, reducing intensity received from lights", keywordsList, ref updateKeywords, "Disable Wrapped Lighting");
                }

                TCP2_GUI.Separator();
            }

            //FILTERED PARAMETERS ================================================================

            //RAMP TYPE --------------------------------------------------------------------------

            if (CategoryFilter("TEXTURE_RAMP"))
            {
                if (isGeneratedShader)
                {
                    ShowFilteredProperties("#RAMPT#", props);
                }
                else
                {
                    if (TCP2_Utils.ShaderKeywordToggle("TCP2_RAMPTEXT", "Texture Toon Ramp", "Make the toon ramp based on a texture", keywordsList, ref updateKeywords, "Ramp Style"))
                    {
                        ShowFilteredProperties("#RAMPT#", props);
                    }
                    else
                    {
                        ShowFilteredProperties("#RAMPF#", props);
                    }
                }
            }
            else
            {
                ShowFilteredProperties("#RAMPF#", props);
            }

            TCP2_GUI.Separator();

            //BUMP/NORMAL MAPPING ----------------------------------------------------------------

            if (CategoryFilter("BUMP"))
            {
                if (isGeneratedShader)
                {
                    TCP2_GUI.HeaderAndHelp("BUMP/NORMAL MAPPING", "Normal/Bump map");

                    ShowFilteredProperties("#NORM#", props);
                    ShowFilteredProperties("#PLLX#", props);
                }
                else
                {
                    if (TCP2_Utils.ShaderKeywordToggle("TCP2_BUMP", "BUMP/NORMAL MAPPING", "Enable bump mapping using normal maps", keywordsList, ref updateKeywords, "Normal/Bump map"))
                    {
                        ShowFilteredProperties("#NORM#", props);
                    }
                }

                TCP2_GUI.Separator();
            }

            //SPECULAR ---------------------------------------------------------------------------

            if (CategoryFilter("SPECULAR", "SPECULAR_ANISOTROPIC"))
            {
                if (isGeneratedShader)
                {
                    TCP2_GUI.HeaderAndHelp("SPECULAR", "Specular");
                    ShowFilteredProperties("#SPEC#", props);
                    if (HasFlags("SPECULAR_ANISOTROPIC"))
                    {
                        ShowFilteredProperties("#SPECA#", props);
                    }
                    if (HasFlags("SPECULAR_TOON"))
                    {
                        ShowFilteredProperties("#SPECT#", props);
                    }
                }
                else
                {
                    bool specular = TCP2_Utils.HasKeywords(keywordsList, "TCP2_SPEC", "TCP2_SPEC_TOON");
                    TCP2_Utils.ShaderVariantUpdate("Specular", ShaderVariants, ShaderVariantsEnabled, specular, ref updateVariant);

                    specular |= TCP2_Utils.ShaderKeywordRadio("SPECULAR", new string[] { "TCP2_SPEC_OFF", "TCP2_SPEC", "TCP2_SPEC_TOON" }, new GUIContent[]
                    {
                        new GUIContent("Off", "No Specular"),
                        new GUIContent("Regular", "Default Blinn-Phong Specular"),
                        new GUIContent("Cartoon", "Specular with smoothness control")
                    },
                                                              keywordsList, ref updateKeywords);

                    if (specular)
                    {
                        ShowFilteredProperties("#SPEC#", props);

                        bool specr = TCP2_Utils.HasKeywords(keywordsList, "TCP2_SPEC_TOON");
                        if (specr)
                        {
                            ShowFilteredProperties("#SPECT#", props);
                        }
                    }
                }

                TCP2_GUI.Separator();
            }

            //REFLECTION -------------------------------------------------------------------------

            if (CategoryFilter("REFLECTION") && !isMobileShader)
            {
                if (isGeneratedShader)
                {
                    TCP2_GUI.HeaderAndHelp("REFLECTION", "Reflection");

                    ShowFilteredProperties("#REFL#", props);
#if UNITY_5
                    if (HasFlags("U5_REFLPROBE"))
                    {
                        ShowFilteredProperties("#REFL_U5#", props);
                    }
#endif
                    if (HasFlags("REFL_COLOR"))
                    {
                        ShowFilteredProperties("#REFLC#", props);
                    }
                    if (HasFlags("REFL_ROUGH"))
                    {
                        ShowFilteredProperties("#REFLR#", props);
                        EditorGUILayout.HelpBox("Cubemap Texture needs to have MipMaps enabled for Roughness to work!", MessageType.Info);
                    }
                }
                else
                {
                    bool reflection = TCP2_Utils.HasKeywords(keywordsList, "TCP2_REFLECTION", "TCP2_REFLECTION_MASKED");
                    TCP2_Utils.ShaderVariantUpdate("Reflection", ShaderVariants, ShaderVariantsEnabled, reflection, ref updateVariant);

                    reflection |= TCP2_Utils.ShaderKeywordRadio("REFLECTION", new string[] { "TCP2_REFLECTION_OFF", "TCP2_REFLECTION", "TCP2_REFLECTION_MASKED" }, new GUIContent[]
                    {
                        new GUIContent("Off", "No Cubemap Reflection"),
                        new GUIContent("Global", "Global Cubemap Reflection"),
                        new GUIContent("Masked", "Masked Cubemap Reflection (using the main texture's alpha channel)")
                    },
                                                                keywordsList, ref updateKeywords);

                    if (reflection)
                    {
#if UNITY_5
                        //Reflection Probes toggle
                        if (TCP2_Utils.ShaderKeywordToggle("TCP2_U5_REFLPROBE", "Use Reflection Probes", "Use Unity 5's Reflection Probes", keywordsList, ref updateKeywords))
                        {
                            ShowFilteredProperties("#REFL_U5#", props);
                        }
#endif
                        ShowFilteredProperties("#REFL#", props);
                    }
                }

                TCP2_GUI.Separator();
            }

            //MATCAP -----------------------------------------------------------------------------

            if (CategoryFilter("MATCAP"))
            {
                if (isGeneratedShader)
                {
                    TCP2_GUI.Header("MATCAP");
                    ShowFilteredProperties("#MC#", props);

                    TCP2_GUI.Separator();
                }
                else if (isMobileShader)
                {
                    bool matcap = TCP2_Utils.HasKeywords(keywordsList, "TCP2_MC", "TCP2_MCMASK");
                    TCP2_Utils.ShaderVariantUpdate("Matcap", ShaderVariants, ShaderVariantsEnabled, matcap, ref updateVariant);

                    matcap |= TCP2_Utils.ShaderKeywordRadio("MATCAP", new string[] { "TCP2_MC_OFF", "TCP2_MC", "TCP2_MCMASK" }, new GUIContent[]
                    {
                        new GUIContent("Off", "No MatCap reflection"),
                        new GUIContent("Global", "Global additive MatCap"),
                        new GUIContent("Masked", "Masked additive MatCap (using the main texture's alpha channel)")
                    },
                                                            keywordsList, ref updateKeywords);

                    if (matcap)
                    {
                        ShowFilteredProperties("#MC#", props);
                    }

                    TCP2_GUI.Separator();
                }
            }

            //SUBSURFACE SCATTERING --------------------------------------------------------------------------------

            if (CategoryFilter("SUBSURFACE_SCATTERING") && isGeneratedShader)
            {
                TCP2_GUI.HeaderAndHelp("SUBSURFACE SCATTERING", "Subsurface Scattering");
                ShowFilteredProperties("#SUBS#", props);
                TCP2_GUI.Separator();
            }

            //RIM --------------------------------------------------------------------------------

            if (CategoryFilter("RIM", "RIM_OUTLINE"))
            {
                if (isGeneratedShader)
                {
                    TCP2_GUI.HeaderAndHelp("RIM", "Rim");

                    ShowFilteredProperties("#RIM#", props);

                    if (HasFlags("RIMDIR"))
                    {
                        ShowFilteredProperties("#RIMDIR#", props);

                        if (HasFlags("PARALLAX"))
                        {
                            EditorGUILayout.HelpBox("Because it affects the view direction vector, Rim Direction may distort Parallax effect.", MessageType.Warning);
                        }
                    }
                }
                else
                {
                    bool rim        = TCP2_Utils.HasKeywords(keywordsList, "TCP2_RIM");
                    bool rimOutline = TCP2_Utils.HasKeywords(keywordsList, "TCP2_RIMO");

                    TCP2_Utils.ShaderVariantUpdate("Rim", ShaderVariants, ShaderVariantsEnabled, rim, ref updateVariant);
                    TCP2_Utils.ShaderVariantUpdate("RimOutline", ShaderVariants, ShaderVariantsEnabled, rimOutline, ref updateVariant);

                    rim |= rimOutline |= TCP2_Utils.ShaderKeywordRadio("RIM", new string[] { "TCP2_RIM_OFF", "TCP2_RIM", "TCP2_RIMO" }, new GUIContent[]
                    {
                        new GUIContent("Off", "No Rim effect"),
                        new GUIContent("Lighting", "Rim lighting (additive)"),
                        new GUIContent("Outline", "Rim outline (blended)")
                    },
                                                                       keywordsList, ref updateKeywords);

                    if (rim || rimOutline)
                    {
                        ShowFilteredProperties("#RIM#", props);

                        if (CategoryFilter("RIMDIR"))
                        {
                            if (TCP2_Utils.ShaderKeywordToggle("TCP2_RIMDIR", "Directional Rim", "Enable directional rim control (rim calculation is approximated if enabled)", keywordsList, ref updateKeywords))
                            {
                                ShowFilteredProperties("#RIMDIR#", props);
                            }
                        }
                    }
                }

                TCP2_GUI.Separator();
            }

            //CUBEMAP AMBIENT --------------------------------------------------------------------

            if (CategoryFilter("CUBE_AMBIENT") && isGeneratedShader)
            {
                TCP2_GUI.HeaderAndHelp("CUBEMAP AMBIENT", "Cubemap Ambient");
                ShowFilteredProperties("#CUBEAMB#", props);
                TCP2_GUI.Separator();
            }

            //DIRECTIONAL AMBIENT --------------------------------------------------------------------

            if (CategoryFilter("DIRAMBIENT") && isGeneratedShader)
            {
                TCP2_GUI.HeaderAndHelp("DIRECTIONAL AMBIENT", "Directional Ambient");
                DirectionalAmbientGUI("#DAMB#", props);
                TCP2_GUI.Separator();
            }

            //SKETCH --------------------------------------------------------------------------------

            if (CategoryFilter("SKETCH", "SKETCH_GRADIENT") && isGeneratedShader)
            {
                TCP2_GUI.HeaderAndHelp("SKETCH", "Sketch");

                bool sketch  = HasFlags("SKETCH");
                bool sketchG = HasFlags("SKETCH_GRADIENT");

                if (sketch || sketchG)
                {
                    ShowFilteredProperties("#SKETCH#", props);
                }

                if (sketchG)
                {
                    ShowFilteredProperties("#SKETCHG#", props);
                }

                TCP2_GUI.Separator();
            }

            //OUTLINE --------------------------------------------------------------------------------

            if (CategoryFilter("OUTLINE", "OUTLINE_BLENDING"))
            {
                bool hasOutlineOpaque   = false;
                bool hasOutlineBlending = false;
                bool hasOutline         = false;

                if (isGeneratedShader)
                {
                    TCP2_GUI.HeaderAndHelp("OUTLINE", "Outline");

                    hasOutlineOpaque   = HasFlags("OUTLINE");
                    hasOutlineBlending = HasFlags("OUTLINE_BLENDING");
                    hasOutline         = hasOutlineOpaque || hasOutlineBlending;
                }
                else
                {
                    hasOutlineOpaque   = TCP2_Utils.HasKeywords(keywordsList, "OUTLINES");
                    hasOutlineBlending = TCP2_Utils.HasKeywords(keywordsList, "OUTLINE_BLENDING");
                    hasOutline         = hasOutlineOpaque || hasOutlineBlending;

                    TCP2_Utils.ShaderVariantUpdate("Outline", ShaderVariants, ShaderVariantsEnabled, hasOutlineOpaque, ref updateVariant);
                    TCP2_Utils.ShaderVariantUpdate("OutlineBlending", ShaderVariants, ShaderVariantsEnabled, hasOutlineBlending, ref updateVariant);

                    hasOutline |= TCP2_Utils.ShaderKeywordRadio("OUTLINE", new string[] { "OUTLINE_OFF", "OUTLINES", "OUTLINE_BLENDING" }, new GUIContent[]
                    {
                        new GUIContent("Off", "No Outline"),
                        new GUIContent("Opaque", "Opaque Outline"),
                        new GUIContent("Blended", "Allows transparent Outline and other effects")
                    },
                                                                keywordsList, ref updateKeywords);
                }

                if (hasOutline)
                {
                    EditorGUI.indentLevel++;

                    //Outline Type ---------------------------------------------------------------------------
                    ShowFilteredProperties("#OUTLINE#", props, false);
                    if (!isMobileShader && !HasFlags("FORCE_SM2"))
                    {
                        bool outlineTextured = TCP2_Utils.ShaderKeywordToggle("TCP2_OUTLINE_TEXTURED", "Outline Color from Texture", "If enabled, outline will take an averaged color from the main texture multiplied by Outline Color", keywordsList, ref updateKeywords);
                        if (outlineTextured)
                        {
                            ShowFilteredProperties("#OUTLINETEX#", props);
                        }
                    }
                    TCP2_Utils.ShaderKeywordToggle("TCP2_OUTLINE_CONST_SIZE", "Constant Size Outline", "If enabled, outline will have a constant size independently from camera distance", keywordsList, ref updateKeywords);
                    if (TCP2_Utils.ShaderKeywordToggle("TCP2_ZSMOOTH_ON", "Correct Z Artefacts", "Enable the outline z-correction to try to hide artefacts from complex models", keywordsList, ref updateKeywords))
                    {
                        ShowFilteredProperties("#OUTLINEZ#", props);
                    }

                    //Smoothed Normals -----------------------------------------------------------------------
                    EditorGUI.indentLevel--;
                    TCP2_GUI.Header("OUTLINE NORMALS", "Defines where to take the vertex normals from to draw the outline.\nChange this when using a smoothed mesh to fill the gaps shown in hard-edged meshes.");
                    EditorGUI.indentLevel++;
                    TCP2_Utils.ShaderKeywordRadio(null, new string[] { "TCP2_NONE", "TCP2_COLORS_AS_NORMALS", "TCP2_TANGENT_AS_NORMALS", "TCP2_UV2_AS_NORMALS" }, new GUIContent[]
                    {
                        new GUIContent("Regular", "Use regular vertex normals"),
                        new GUIContent("Vertex Colors", "Use vertex colors as normals (with smoothed mesh)"),
                        new GUIContent("Tangents", "Use tangents as normals (with smoothed mesh)"),
                        new GUIContent("UV2", "Use second texture coordinates as normals (with smoothed mesh)"),
                    },
                                                  keywordsList, ref updateKeywords);
                    EditorGUI.indentLevel--;

                    //Outline Blending -----------------------------------------------------------------------

                    if (hasOutlineBlending)
                    {
                        MaterialProperty[] blendProps = GetFilteredProperties("#BLEND#", props);

                        if (blendProps.Length != 2)
                        {
                            EditorGUILayout.HelpBox("Couldn't find Blending properties!", MessageType.Error);
                        }
                        else
                        {
                            TCP2_GUI.Header("OUTLINE BLENDING", "BLENDING EXAMPLES:\nAlpha Transparency: SrcAlpha / OneMinusSrcAlpha\nMultiply: DstColor / Zero\nAdd: One / One\nSoft Add: OneMinusDstColor / One");

                            UnityEngine.Rendering.BlendMode blendSrc = (UnityEngine.Rendering.BlendMode)blendProps[0].floatValue;
                            UnityEngine.Rendering.BlendMode blendDst = (UnityEngine.Rendering.BlendMode)blendProps[1].floatValue;

                            EditorGUI.BeginChangeCheck();
                            float f = EditorGUIUtility.fieldWidth;
                            float l = EditorGUIUtility.labelWidth;
                            EditorGUIUtility.fieldWidth  = 110f;
                            EditorGUIUtility.labelWidth -= Mathf.Abs(f - EditorGUIUtility.fieldWidth);
                            blendSrc = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup("Source Factor", blendSrc);
                            blendDst = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup("Destination Factor", blendDst);
                            EditorGUIUtility.fieldWidth = f;
                            EditorGUIUtility.labelWidth = l;
                            if (EditorGUI.EndChangeCheck())
                            {
                                blendProps[0].floatValue = (float)blendSrc;
                                blendProps[1].floatValue = (float)blendDst;
                            }
                        }
                    }
                }

                TCP2_GUI.Separator();
            }

            //LIGHTMAP --------------------------------------------------------------------------------

#if UNITY_4_5
            if (CategoryFilter("LIGHTMAP") && !isGeneratedShader)
            {
                TCP2_Utils.ShaderKeywordRadio("LIGHTMAP", new string[] { "TCP2_LIGHTMAP_OFF", "TCP2_LIGHTMAP" }, new GUIContent[] {
                    new GUIContent("Unity", "Use Unity's built-in lightmap decoding"),
                    new GUIContent("Toony Colors Pro 2", "Use TCP2's lightmap decoding (lightmaps will be affected by ramp and color settings)")
                }, keywordsList, ref updateKeywords);
            }
#endif

            //TRANSPARENCY --------------------------------------------------------------------------------

            if (CategoryFilter("ALPHA", "CUTOUT") && isGeneratedShader)
            {
                bool alpha  = false;
                bool cutout = false;

                if (isGeneratedShader)
                {
                    TCP2_GUI.Header("TRANSPARENCY");

                    alpha  = HasFlags("ALPHA");
                    cutout = HasFlags("CUTOUT");
                }

                if (alpha)
                {
                    MaterialProperty[] blendProps = GetFilteredProperties("#ALPHA#", props);
                    if (blendProps.Length != 2)
                    {
                        EditorGUILayout.HelpBox("Couldn't find Blending properties!", MessageType.Error);
                    }
                    else
                    {
                        TCP2_GUI.Header("BLENDING", "BLENDING EXAMPLES:\nAlpha Transparency: SrcAlpha / OneMinusSrcAlpha\nMultiply: DstColor / Zero\nAdd: One / One\nSoft Add: OneMinusDstColor / One");

                        UnityEngine.Rendering.BlendMode blendSrc = (UnityEngine.Rendering.BlendMode)blendProps[0].floatValue;
                        UnityEngine.Rendering.BlendMode blendDst = (UnityEngine.Rendering.BlendMode)blendProps[1].floatValue;

                        EditorGUI.BeginChangeCheck();
                        float f = EditorGUIUtility.fieldWidth;
                        float l = EditorGUIUtility.labelWidth;
                        EditorGUIUtility.fieldWidth  = 110f;
                        EditorGUIUtility.labelWidth -= Mathf.Abs(f - EditorGUIUtility.fieldWidth);
                        blendSrc = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup("Source Factor", blendSrc);
                        blendDst = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup("Destination Factor", blendDst);
                        EditorGUIUtility.fieldWidth = f;
                        EditorGUIUtility.labelWidth = l;
                        if (EditorGUI.EndChangeCheck())
                        {
                            blendProps[0].floatValue = (float)blendSrc;
                            blendProps[1].floatValue = (float)blendDst;
                        }
                    }
                }

                if (cutout)
                {
                    ShowFilteredProperties("#CUTOUT#", props);
                }
            }

#if DEBUG_INFO
            //--------------------------------------------------------------------------------------
            //DEBUG --------------------------------------------------------------------------------

            TCP2_GUI.SeparatorBig();

            TCP2_GUI.Header("DEBUG");

            //Clear Keywords
            if (GUILayout.Button("Clear Keywords", EditorStyles.miniButton))
            {
                keywordsList.Clear();
                updateKeywords = true;
            }

            //Shader Flags
            GUILayout.Label("Features", EditorStyles.boldLabel);
            string features = "";
            if (mShaderFeatures != null)
            {
                foreach (string flag in mShaderFeatures)
                {
                    features += flag + ", ";
                }
            }
            if (features.Length > 0)
            {
                features = features.Substring(0, features.Length - 2);
            }

            GUILayout.Label(features, EditorStyles.wordWrappedMiniLabel);

            //Shader Keywords
            GUILayout.Label("Keywords", EditorStyles.boldLabel);
            string keywords = "";
            foreach (string keyword in keywordsList)
            {
                keywords += keyword + ", ";
            }
            if (keywords.Length > 0)
            {
                keywords = keywords.Substring(0, keywords.Length - 2);
            }

            GUILayout.Label(keywords, EditorStyles.wordWrappedMiniLabel);
#endif
            //--------------------------------------------------------------------------------------

            if (EditorGUI.EndChangeCheck())
            {
                materialEditor.PropertiesChanged();
            }
        }

        //Update Keywords
        if (updateKeywords)
        {
            if (materialEditor.targets != null && materialEditor.targets.Length > 0)
            {
                foreach (Object t in materialEditor.targets)
                {
                    (t as Material).shaderKeywords = keywordsList.ToArray();
                    EditorUtility.SetDirty(t);
                }
            }
            else
            {
                targetMaterial.shaderKeywords = keywordsList.ToArray();
                EditorUtility.SetDirty(targetMaterial);
            }
        }

        //Update Variant
        if (updateVariant && !isGeneratedShader)
        {
            string baseName = isMobileShader ? BASE_SHADER_NAME_MOB : BASE_SHADER_NAME;

            string newShader = baseName;
            for (int i = 0; i < ShaderVariants.Count; i++)
            {
                if (ShaderVariantsEnabled[i])
                {
                    newShader += " " + ShaderVariants[i];
                }
            }
            newShader = newShader.TrimEnd();

            //If variant shader
            string basePath = BASE_SHADER_PATH;
            if (newShader != baseName)
            {
                basePath = VARIANT_SHADER_PATH;
            }

            Shader shader = Shader.Find(basePath + newShader);
            if (shader != null)
            {
                materialEditor.SetShader(shader, false);

                mJustChangedShader = true;
            }
            else
            {
                if (Event.current.type != EventType.Layout)
                {
                    mVariantError = "Can't find shader variant:\n" + basePath + newShader;
                }
                materialEditor.Repaint();
            }
        }
        else if (!string.IsNullOrEmpty(mVariantError) && Event.current.type != EventType.Layout)
        {
            mVariantError = null;
            materialEditor.Repaint();
        }
#endif

#if UNITY_5_5_OR_NEWER
        materialEditor.RenderQueueField();
#endif
    }
    //Generate the source code for the shader as a string
    private static string GenerateShaderSource(this TCP2_Config config, TCP2_ShaderGenerator.ShaderGeneratorTemplate template, Shader existingShader = null)
    {
        if (config == null)
        {
            var error = "[TCP2 Shader Generator] Config file is null";
            Debug.LogError(error);
            return(error);
        }

        if (template == null)
        {
            var error = "[TCP2 Shader Generator] Template is null";
            Debug.LogError(error);
            return(error);
        }

        if (template.textAsset == null || string.IsNullOrEmpty(template.textAsset.text))
        {
            var error = "[TCP2 Shader Generator] Template string is null or empty";
            Debug.LogError(error);
            return(error);
        }

        //------------------------------------------------
        // SHADER PARAMETERS

        //Masks
        bool mask1 = false, mask2 = false, mask3 = false, vcolors_mask = false, mainTex_mask = false;
        var  mask1features = "";
        var  mask2features = "";
        var  mask3features = "";

        //Enable Masks according to their dependencies (new system using Template)
        foreach (var kvp in config.Keywords)
        {
            if (kvp.Value == "mask1")
            {
                var maskEnabled = template.GetMaskDependency(kvp.Key, config);
                mask1 |= maskEnabled;
                if (maskEnabled)
                {
                    mask1features += template.GetMaskDisplayName(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "mask2")
            {
                var maskEnabled = template.GetMaskDependency(kvp.Key, config);
                mask2 |= maskEnabled;
                if (maskEnabled)
                {
                    mask2features += template.GetMaskDisplayName(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "mask3")
            {
                var maskEnabled = template.GetMaskDependency(kvp.Key, config);
                mask3 |= maskEnabled;
                if (maskEnabled)
                {
                    mask3features += template.GetMaskDisplayName(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "IN.color" || kvp.Value == "vcolors")
            {
                vcolors_mask |= template.GetMaskDependency(kvp.Key, config);
            }
            else if (kvp.Value == "mainTex")
            {
                mainTex_mask |= template.GetMaskDependency(kvp.Key, config);
            }
        }

        //Only enable Independent UVs if relevant Mask is actually enabled
        foreach (var kvp in config.Keywords)
        {
            if (kvp.Key == "UV_mask1")
            {
                config.ToggleFeature("UVMASK1", (kvp.Value == "Independent UV" || kvp.Value == "Independent UV0") && mask1);
                config.ToggleFeature("UVMASK1_UV2", kvp.Value == "Independent UV1" && mask1);
            }
            else if (kvp.Key == "UV_mask2")
            {
                config.ToggleFeature("UVMASK2", (kvp.Value == "Independent UV" || kvp.Value == "Independent UV0") && mask2);
                config.ToggleFeature("UVMASK2_UV2", kvp.Value == "Independent UV1" && mask2);
            }
            else if (kvp.Key == "UV_mask3")
            {
                config.ToggleFeature("UVMASK3", (kvp.Value == "Independent UV" || kvp.Value == "Independent UV0") && mask3);
                config.ToggleFeature("UVMASK3_UV2", kvp.Value == "Independent UV1" && mask3);
            }
        }
        mask1features = mask1features.TrimEnd(',');
        mask2features = mask2features.TrimEnd(',');
        mask3features = mask3features.TrimEnd(',');

        config.ToggleFeature("MASK1", mask1);
        config.ToggleFeature("MASK2", mask2);
        config.ToggleFeature("MASK3", mask3);
        config.ToggleFeature("VCOLORS_MASK", vcolors_mask);
        config.ToggleFeature("MASK_MAINTEX", mainTex_mask);

        //---

        var keywords = new Dictionary <string, string>(config.Keywords);
        var flags    = new List <string>(config.Flags);
        var features = new List <string>(config.Features);

        //Unity version
#if UNITY_5_4_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_5_4");
#endif
#if UNITY_5_5_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_5_5");
#endif
#if UNITY_2017_1_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_2017_1");
#endif
#if UNITY_2018_1_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_2018_1");
#endif
#if UNITY_2018_2_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_2018_2");
#endif

        //Masks
        keywords.Add("MASK1", mask1features);
        keywords.Add("MASK2", mask2features);
        keywords.Add("MASK3", mask3features);

        //Shader name
        keywords.Add("SHADER_NAME", config.ShaderName);

        //Include path
        var include = GetIncludePrefix(config) + GetIncludeRelativePath(config, existingShader).TrimEnd('/');
        keywords.Add("INCLUDE_PATH", include);

        //Shader Model target (old templates)
        if (!keywords.ContainsKey("SHADER_TARGET"))
        {
            var target = GetShaderTarget(config);
            keywords.Add("SHADER_TARGET", target);
            if (config.shaderTarget == 20)
            {
                TCP2_Utils.AddIfMissing(features, "FORCE_SM2");
            }
        }

        //Generate Surface parameters
        var strFlags = ArrayToString(flags.ToArray(), " ");
        keywords.Add("SURF_PARAMS", strFlags);

        //------------------------------------------------
        // PARSING & GENERATION

        var sb            = new StringBuilder();
        var templateLines = template.textAsset.text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);

        var depth = -1;
        var stack = new List <bool>();
        var done  = new List <bool>();

        //Parse template file
        string line = null;
        for (var i = 0; i < templateLines.Length; i++)
        {
            line = templateLines[i];

            //Comment
            if (line.StartsWith("#"))
            {
                //Meta
                if (line.StartsWith("#CONFIG="))
                {
                    config.configType = line.Substring(8).TrimEnd().ToLower();
                }

                //Features UI
                if (line.StartsWith("#FEATURES"))
                {
                    while (i < templateLines.Length)
                    {
                        i++;
                        if (templateLines[i] == "#END")
                        {
                            break;
                        }
                    }
                    continue;
                }

                //Keywords
                if (line.StartsWith("#KEYWORDS"))
                {
                    while (i < templateLines.Length)
                    {
                        i++;
                        if (templateLines[i] == "#END")
                        {
                            break;
                        }

                        var error = ProcessKeywords(templateLines[i], config, ref features, ref flags, ref keywords, ref i, ref depth, ref stack, ref done);
                        if (!string.IsNullOrEmpty(error))
                        {
                            return(error);
                        }
                    }

                    //Update Surface parameters
                    strFlags = ArrayToString(flags.ToArray(), " ");
                    if (keywords.ContainsKey("SURF_PARAMS"))
                    {
                        keywords["SURF_PARAMS"] = strFlags;
                    }
                    else
                    {
                        keywords.Add("SURF_PARAMS", strFlags);
                    }
                }

                //Debugging
                if (line.StartsWith("#break"))
                {
                    Debug.Log("[TCP2] Parse Break @ " + i);
                }

                continue;
            }

            //Line break
            if (string.IsNullOrEmpty(line) && ((depth >= 0 && stack[depth]) || depth < 0))
            {
                sb.AppendLine(line);
                continue;
            }

            //Conditions
            if (line.Contains("///"))
            {
                var error = ProcessCondition(line, ref features, ref i, ref depth, ref stack, ref done);
                if (!string.IsNullOrEmpty(error))
                {
                    return(error);
                }
            }
            //Regular line
            else
            {
                //Replace keywords
                line = ReplaceKeywords(line, keywords);

                //Append line if inside valid condition block
                if ((depth >= 0 && stack[depth]) || depth < 0)
                {
                    sb.AppendLine(line);
                }
            }
        }

        if (depth >= 0)
        {
            Debug.LogWarning("[TCP2 Shader Generator] Missing " + (depth + 1) + " ending '///' tags");
        }

        var sourceCode = sb.ToString();

        //Normalize line endings
        sourceCode = sourceCode.Replace("\r\n", "\n");

        return(sourceCode);
    }
    //--------------------------------------------------------------------------------------------------

    private Mesh CreateSmoothedMeshAsset(SelectedMesh originalMesh)
    {
        //Check if we are ok to overwrite
        bool   overwrite = true;
        string rootPath  = TCP2_Utils.FindReadmePath() + OUTPUT_FOLDER;

        if (!System.IO.Directory.Exists(rootPath))
        {
            System.IO.Directory.CreateDirectory(rootPath);
        }

#if UNITY_EDITOR_WIN
        rootPath = rootPath.Replace(TCP2_Utils.UnityToSystemPath(Application.dataPath), "").Replace(@"\", "/");
#else
        rootPath = rootPath.Replace(Application.dataPath, "");
#endif

        string assetPath    = "Assets" + rootPath;
        string newAssetName = originalMesh.name + " " + MESH_SUFFIX + ".asset";
        if (originalMesh.name.Contains(MESH_SUFFIX))
        {
            newAssetName = originalMesh.name + ".asset";
        }
        assetPath += newAssetName;
        Mesh existingAsset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Mesh)) as Mesh;
        bool assetExists   = (existingAsset != null) && originalMesh.isAsset;
        if (assetExists)
        {
            if (!mAlwaysOverwrite)
            {
                overwrite = EditorUtility.DisplayDialog("TCP2 : Smoothed Mesh", "The following smoothed mesh already exists:\n\n" + newAssetName + "\n\nOverwrite?", "Yes", "No");
            }

            if (!overwrite)
            {
                return(null);
            }
            else
            {
                originalMesh.mesh = existingAsset;
                originalMesh.name = existingAsset.name;
            }
        }

        Mesh newMesh = null;
        if (originalMesh.isSkinned)
        {
            newMesh = TCP2_Utils.CreateSmoothedMesh(originalMesh.mesh, mFormat, false, true, false, !originalMesh.isAsset || (originalMesh.isAsset && assetExists));
        }
        else
        {
            newMesh = TCP2_Utils.CreateSmoothedMesh(originalMesh.mesh, mFormat, mVColors, mTangents, mUV2, !originalMesh.isAsset || (originalMesh.isAsset && assetExists));
        }

        if (newMesh == null)
        {
            ShowNotification(new GUIContent("Couldn't generate the mesh for:\n" + originalMesh.name));
        }
        else
        {
            if (originalMesh.associatedObjects != null)
            {
                Undo.RecordObjects(originalMesh.associatedObjects, "Assign TCP2 Smoothed Mesh to Selection");

                foreach (Object o in originalMesh.associatedObjects)
                {
                    if (o is SkinnedMeshRenderer)
                    {
                        (o as SkinnedMeshRenderer).sharedMesh = newMesh;
                    }
                    else if (o is MeshFilter)
                    {
                        (o as MeshFilter).sharedMesh = newMesh;
                    }
                    else
                    {
                        Debug.LogWarning("[TCP2 Smoothed Normals Utility] Unrecognized AssociatedObject: " + o + "\nType: " + o.GetType());
                    }
                    EditorUtility.SetDirty(o);
                }
            }

            if (originalMesh.isAsset)
            {
                if (overwrite && !assetExists)
                {
                    AssetDatabase.CreateAsset(newMesh, assetPath);
                }
            }
            else
            {
                return(null);
            }
        }

        return(newMesh);
    }
    public void ShaderPropertiesGUI(Material material)
    {
        // Use default labelWidth
        EditorGUIUtility.labelWidth = 0f;

        // Detect any changes to the material
        EditorGUI.BeginChangeCheck();
        {
            BlendModePopup();

            GUILayout.Space(8f);
            expandStandardProperties = GUILayout.Toggle(expandStandardProperties, "STANDARD PROPERTIES", EditorStyles.toolbarButton);
            if (expandStandardProperties)
            {
                //Background
                Rect vertRect = EditorGUILayout.BeginVertical();
                vertRect.xMax += 2;
                vertRect.xMin--;
                GUI.Box(vertRect, "", (GUIStyle)"RL Background");
                GUILayout.Space(4f);

                // Primary properties
                GUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel);
                DoAlbedoArea(material);
                DoSpecularMetallicArea();
                m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap,
                                                           bumpMap.textureValue != null ? bumpScale : null);
                m_MaterialEditor.TexturePropertySingleLine(Styles.heightMapText, heightMap,
                                                           heightMap.textureValue != null ? heigtMapScale : null);
                m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap,
                                                           occlusionMap.textureValue != null ? occlusionStrength : null);
                DoEmissionArea(material);
                m_MaterialEditor.TexturePropertySingleLine(Styles.detailMaskText, detailMask);
                EditorGUI.BeginChangeCheck();
                m_MaterialEditor.TextureScaleOffsetProperty(albedoMap);
                if (EditorGUI.EndChangeCheck())
                {
                    emissionMap.textureScaleAndOffset = albedoMap.textureScaleAndOffset;
                }
                // Apply the main texture scale and offset to the emission texture as well, for Enlighten's sake

                EditorGUILayout.Space();

                // Secondary properties
                GUILayout.Label(Styles.secondaryMapsText, EditorStyles.boldLabel);
                m_MaterialEditor.TexturePropertySingleLine(Styles.detailAlbedoText, detailAlbedoMap);
                m_MaterialEditor.TexturePropertySingleLine(Styles.detailNormalMapText, detailNormalMap, detailNormalMapScale);
                m_MaterialEditor.TextureScaleOffsetProperty(detailAlbedoMap);
                m_MaterialEditor.ShaderProperty(uvSetSecondary, Styles.uvSetLabel.text);

                // Third properties
                GUILayout.Label(Styles.forwardText, EditorStyles.boldLabel);
                if (highlights != null)
                {
                    m_MaterialEditor.ShaderProperty(highlights, Styles.highlightsText);
                }
                if (reflections != null)
                {
                    m_MaterialEditor.ShaderProperty(reflections, Styles.reflectionsText);
                }

                GUILayout.Space(8f);
                EditorGUILayout.EndVertical();
            }

            EditorGUILayout.Space();

            //----------------------------------------------------------------
            //    TOONY COLORS PRO 2

            bool useOutline        = (m_MaterialEditor.target as Material).shaderKeywords.Contains("OUTLINES");
            bool useOutlineBlended = (m_MaterialEditor.target as Material).shaderKeywords.Contains("OUTLINE_BLENDING");

            bool hasOutlineShader        = tcp2_outlineWidth != null;
            bool hasOutlineBlendedShader = tcp2_srcBlendOutline != null;

            bool useOutlineNew        = useOutline;
            bool useOutlineBlendedNew = useOutlineBlended;

            expandTCP2Properties = GUILayout.Toggle(expandTCP2Properties, "TOONY COLORS PRO 2", EditorStyles.toolbarButton);
            if (expandTCP2Properties)
            {
                //Background
                Rect vertRect = EditorGUILayout.BeginVertical();
                vertRect.xMax += 2;
                vertRect.xMin--;
                GUI.Box(vertRect, "", (GUIStyle)"RL Background");
                GUILayout.Space(4f);

                GUILayout.Label("Base Properties", EditorStyles.boldLabel);
                m_MaterialEditor.ColorProperty(tcp2_highlightColor, Styles.tcp2_highlightColorText);
                m_MaterialEditor.ColorProperty(tcp2_shadowColor, Styles.tcp2_shadowColorText);

                // Wrapped Lighting
                m_MaterialEditor.ShaderProperty(tcp2_TCP2_DISABLE_WRAPPED_LIGHT, "Disable Wrapped Lighting");

                // Ramp Texture / Threshold
                m_MaterialEditor.ShaderProperty(tcp2_TCP2_RAMPTEXT, "Use Ramp Texture");
                if (tcp2_TCP2_RAMPTEXT.floatValue > 0)
                {
                    EditorGUI.indentLevel++;
                    m_MaterialEditor.ShaderProperty(tcp2_ramp, Styles.tcp2_rampText);
                    //m_MaterialEditor.TexturePropertySingleLine(Styles.tcp2_rampText, tcp2_ramp);
                    EditorGUI.indentLevel--;
                }
                else
                {
                    m_MaterialEditor.ShaderProperty(tcp2_rampThreshold, Styles.tcp2_rampThresholdText.text, 1);
                    m_MaterialEditor.ShaderProperty(tcp2_rampSmooth, Styles.tcp2_rampSmoothText.text, 1);
                    m_MaterialEditor.ShaderProperty(tcp2_rampSmoothAdd, Styles.tcp2_rampSmoothAddText.text, 1);
                }

                EditorGUILayout.Space();
                GUILayout.Label("Stylization Options", EditorStyles.boldLabel);

                // Stylized Specular
                m_MaterialEditor.ShaderProperty(tcp2_SPEC_TOON, "Stylized Specular");
                if (tcp2_SPEC_TOON.floatValue > 0)
                {
                    m_MaterialEditor.ShaderProperty(tcp2_specSmooth, Styles.tcp2_specSmoothText.text, 1);
                    m_MaterialEditor.ShaderProperty(tcp2_SpecBlend, Styles.tcp2_SpecBlendText.text, 1);

                    EditorGUILayout.Space();
                }

                //Stylized Fresnel
                m_MaterialEditor.ShaderProperty(tcp2_STYLIZED_FRESNEL, "Stylized Fresnel");
                if (tcp2_STYLIZED_FRESNEL.floatValue > 0)
                {
                    m_MaterialEditor.ShaderProperty(tcp2_rimStrength, Styles.tcp2_rimStrengthText.text, 1);
                    m_MaterialEditor.ShaderProperty(tcp2_rimMin, Styles.tcp2_rimMinText.text, 1);
                    m_MaterialEditor.ShaderProperty(tcp2_rimMax, Styles.tcp2_rimMaxText.text, 1);

                    EditorGUILayout.Space();
                }

                //Outline
                useOutlineNew = EditorGUILayout.Toggle(new GUIContent("Outline", "Enable mesh-based outline"), useOutline);
                if (useOutline && hasOutlineShader)
                {
                    //Outline base props
                    m_MaterialEditor.ShaderProperty(tcp2_outlineColor, Styles.tcp2_outlineColorText.text, 1);
                    m_MaterialEditor.ShaderProperty(tcp2_outlineWidth, Styles.tcp2_outlineWidthText.text, 1);

                    m_MaterialEditor.ShaderProperty(tcp2_TCP2_OUTLINE_TEXTURED, "Textured Outline", 1);
                    if (tcp2_TCP2_OUTLINE_TEXTURED.floatValue > 0)
                    {
                        m_MaterialEditor.ShaderProperty(tcp2_TexLod, Styles.tcp2_TexLodText, 1);
                    }

                    m_MaterialEditor.ShaderProperty(tcp2_TCP2_OUTLINE_CONST_SIZE, "Constant Screen Size", 1);
                    m_MaterialEditor.ShaderProperty(tcp2_TCP2_ZSMOOTH_ON, "Z Smooth", 1);
                    if (tcp2_TCP2_ZSMOOTH_ON.floatValue > 0)
                    {
                        m_MaterialEditor.ShaderProperty(tcp2_ZSmooth, Styles.tcp2_ZSmoothText, 2);
                        m_MaterialEditor.ShaderProperty(tcp2_Offset1, Styles.tcp2_Offset1Text, 2);
                        m_MaterialEditor.ShaderProperty(tcp2_Offset2, Styles.tcp2_Offset2Text, 2);
                    }

                    //Blended Outline
                    EditorGUI.indentLevel++;
                    useOutlineBlendedNew = EditorGUILayout.Toggle(new GUIContent("Blended Outline", "Enable blended outline rather than opaque"), useOutlineBlended);
                    if (useOutlineBlended && hasOutlineBlendedShader)
                    {
                        EditorGUI.indentLevel++;
                        UnityEngine.Rendering.BlendMode blendSrc = (UnityEngine.Rendering.BlendMode)tcp2_srcBlendOutline.floatValue;
                        UnityEngine.Rendering.BlendMode blendDst = (UnityEngine.Rendering.BlendMode)tcp2_dstBlendOutline.floatValue;
                        EditorGUI.BeginChangeCheck();
                        blendSrc = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup(Styles.tcp2_srcBlendOutlineText, blendSrc);
                        blendDst = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup(Styles.tcp2_dstBlendOutlineText, blendDst);
                        if (EditorGUI.EndChangeCheck())
                        {
                            tcp2_srcBlendOutline.floatValue = (float)blendSrc;
                            tcp2_dstBlendOutline.floatValue = (float)blendDst;
                        }
                        EditorGUI.indentLevel--;
                    }
                    EditorGUI.indentLevel--;

                    //Outline Normals
                    int onIndex  = GetOutlineNormalsIndex();
                    int newIndex = onIndex;
                    EditorGUI.indentLevel++;
                    if (TCP2_Utils.ScreenWidthRetina < 390f)
                    {
                        newIndex = TCP2_Utils.ShaderKeywordRadioGeneric("Outline Normals", newIndex, new GUIContent[]
                        {
                            new GUIContent("R", "Use regular vertex normals"),
                            new GUIContent("VC", "Use vertex colors as normals (with smoothed mesh)"),
                            new GUIContent("T", "Use tangents as normals (with smoothed mesh)"),
                            new GUIContent("UV2", "Use second texture coordinates as normals (with smoothed mesh)"),
                        });
                    }
                    else if (TCP2_Utils.ScreenWidthRetina < 560f)
                    {
                        newIndex = TCP2_Utils.ShaderKeywordRadioGeneric("Outline Normals", newIndex, new GUIContent[]
                        {
                            new GUIContent("Regular", "Use regular vertex normals"),
                            new GUIContent("VColors", "Use vertex colors as normals (with smoothed mesh)"),
                            new GUIContent("Tangents", "Use tangents as normals (with smoothed mesh)"),
                            new GUIContent("UV2", "Use second texture coordinates as normals (with smoothed mesh)"),
                        });
                    }
                    else
                    {
                        newIndex = TCP2_Utils.ShaderKeywordRadioGeneric("Outline Normals", newIndex, new GUIContent[]
                        {
                            new GUIContent("Regular", "Use regular vertex normals"),
                            new GUIContent("Vertex Colors", "Use vertex colors as normals (with smoothed mesh)"),
                            new GUIContent("Tangents", "Use tangents as normals (with smoothed mesh)"),
                            new GUIContent("UV2", "Use second texture coordinates as normals (with smoothed mesh)"),
                        });
                    }
                    EditorGUI.indentLevel--;
                    if (newIndex != onIndex)
                    {
                        UpdateOutlineNormalsKeyword(newIndex);
                    }
                }

                GUILayout.Space(8f);
                GUILayout.EndVertical();

                // TCP2 End
                //----------------------------------------------------------------
            }

            GUILayout.Space(10f);

            //TCP2: set correct shader based on outline properties
            if (useOutline != useOutlineNew || useOutlineBlended != useOutlineBlendedNew)
            {
                SetTCP2Shader(useOutlineNew, useOutlineBlendedNew);
            }
            else if (useOutline != hasOutlineShader || useOutlineBlended != hasOutlineBlendedShader)
            {
                SetTCP2Shader(useOutline, useOutlineBlended);
            }
        }
        if (EditorGUI.EndChangeCheck())
        {
            foreach (var obj in blendMode.targets)
            {
                MaterialChanged((Material)obj, m_WorkflowMode);
            }
        }
    }
Beispiel #17
0
        protected override void DrawGUI(Rect position, TCP2_Config config)
        {
            //GUIMask(config, this.label, this.tooltip, this.maskKeyword, this.channelKeyword, this.keyword, this.Enabled(config), this.increaseIndent, helpTopic: this.helpTopic, helpIndent: this.helpIndent);

            var curMask = Array.IndexOf(masks, config.GetKeyword(maskKeyword));

            if (curMask < 0)
            {
                curMask = 0;
            }
            var curChannel = TCP2_Utils.FromShader(config.GetKeyword(channelKeyword));
            var uvKey      = (curMask > 1 && curMask < 5) ? "UV_" + masks[curMask] : null;
            var curUv      = Array.IndexOf(uvs, config.GetKeyword(uvKey));

            if (curUv < 0)
            {
                curUv = 0;
            }

            EditorGUI.BeginChangeCheck();

            //Calculate rects
            var helpButton = position;

            helpButton.width = 16f;
            helpButton.x    += 2f;
            position.width  -= helpButton.width;
            helpButton.x    += position.width;

            //Mask type (MainTex, 1, 2, 3)
            var sideRect = position;

            sideRect.width = position.width * 0.75f / 2f;
            curMask        = EditorGUI.Popup(sideRect, curMask, labels);

            //Mask Channel (RGBA)
            var middleRect = position;

            middleRect.width = position.width * 0.25f;
            middleRect.x    += sideRect.width;
            GUI.enabled     &= curMask > 0;
            curChannel       = (TCP2_Utils.TextureChannel)EditorGUI.EnumPopup(middleRect, curChannel);

            //Mask UVs
            sideRect.x  += sideRect.width + middleRect.width;
            GUI.enabled &= curMask > 1 && curMask < 5;
            curUv        = EditorGUI.Popup(sideRect, curUv, uvs);

            //Mask Help
            TCP2_GUI.HelpButton(helpButton, "Masks");

            if (EditorGUI.EndChangeCheck())
            {
                config.SetKeyword(maskKeyword, masks[curMask]);
                if (curMask > 0)
                {
                    config.SetKeyword(channelKeyword, curChannel.ToShader());
                }
                if (curMask > 1 && !string.IsNullOrEmpty(uvKey))
                {
                    config.SetKeyword(uvKey, uvs[curUv]);
                }
                config.ToggleFeature("VCOLORS_MASK", (curMask == 5));
                config.ToggleFeature(keyword, (curMask > 0));
            }
        }
    private bool GUIMask(string label, string tooltip, string maskKeyword, string channelKeyword, string feature = null, bool enabled = true, bool increaseIndentLevel = false, bool visible = true, string helpTopic = null)
    {
        string[] labelsAndKeywords = new string[] {
            "Off|",
            "Main Texture|mainTex",
            "Mask 1|mask1", "Mask 2|mask2", "Mask 3|mask3"
        };

        if (!enabled)
        {
            GUI.enabled = false;
        }
        if (increaseIndentLevel)
        {
            label = "▪  " + label;
        }

        string[] labels = new string[labelsAndKeywords.Length];
        string[] masks  = new string[labelsAndKeywords.Length];
        string[] uvs    = new string[] { "Main Tex UV", "Independent UV" };

        for (int i = 0; i < labelsAndKeywords.Length; i++)
        {
            string[] data = labelsAndKeywords[i].Split('|');
            labels[i] = data[0];
            masks[i]  = data[1];
        }

        int curMask = System.Array.IndexOf(masks, TCP2_ShaderGeneratorUtils.GetKeyword(mCurrentConfig, maskKeyword));

        if (curMask < 0)
        {
            curMask = 0;
        }
        TCP2_Utils.TextureChannel curChannel = TCP2_Utils.FromShader(TCP2_ShaderGeneratorUtils.GetKeyword(mCurrentConfig, channelKeyword));
        if (curMask <= 1)
        {
            curChannel = TCP2_Utils.TextureChannel.Alpha;
        }
        string uvKey = (curMask > 1) ? "UV_" + masks[curMask] : null;
        int    curUv = System.Array.IndexOf(uvs, TCP2_ShaderGeneratorUtils.GetKeyword(mCurrentConfig, uvKey));

        if (curUv < 0)
        {
            curUv = 0;
        }

        if (mHideDisabled)
        {
            visible = enabled;
        }

        if (visible)
        {
            EditorGUILayout.BeginHorizontal();
            float w = 166;
            if (!string.IsNullOrEmpty(helpTopic))
            {
                w -= 20;
                TCP2_GUI.HelpButton(label.TrimStart('▪', ' '), helpTopic);
            }
            TCP2_GUI.SubHeader(label, tooltip, (curMask > 0) && enabled, w);
            curMask     = EditorGUILayout.Popup(curMask, labels);
            GUI.enabled = curMask > 1;
            curChannel  = (TCP2_Utils.TextureChannel)EditorGUILayout.EnumPopup(curChannel);
            curUv       = EditorGUILayout.Popup(curUv, uvs);
            GUI.enabled = mGUIEnabled;
            TCP2_GUI.HelpButton("Masks");
            EditorGUILayout.EndHorizontal();
        }

        TCP2_ShaderGeneratorUtils.SetKeyword(mCurrentConfig.Keywords, maskKeyword, masks[curMask]);
        if (curMask > 0)
        {
            TCP2_ShaderGeneratorUtils.SetKeyword(mCurrentConfig.Keywords, channelKeyword, curChannel.ToShader());
        }
        if (curMask > 1 && !string.IsNullOrEmpty(uvKey))
        {
            TCP2_ShaderGeneratorUtils.SetKeyword(mCurrentConfig.Keywords, uvKey, uvs[curUv]);
        }
        TCP2_ShaderGeneratorUtils.ToggleSingleFeature(mCurrentConfig.Features, feature, (curMask > 0));

        if (!enabled)
        {
            GUI.enabled = mGUIEnabled;
        }

        return(curMask > 0);
    }
Beispiel #19
0
    void OnGUI()
    {
        sGUIEnabled = GUI.enabled;

        EditorGUILayout.BeginHorizontal();
        TCP2_GUI.HeaderBig("TOONY COLORS PRO 2 - SHADER GENERATOR");
        TCP2_GUI.HelpButton("Shader Generator");
        EditorGUILayout.EndHorizontal();
        TCP2_GUI.Separator();

        float lW = EditorGUIUtility.labelWidth;

        EditorGUIUtility.labelWidth = 105f;

        //Avoid refreshing Template meta at every Repaint
        EditorGUILayout.BeginHorizontal();
        TextAsset _tmpTemplate = EditorGUILayout.ObjectField("Template:", Template.textAsset, typeof(TextAsset), false) as TextAsset;

        if (_tmpTemplate != Template.textAsset)
        {
            Template.SetTextAsset(_tmpTemplate);
        }
        //Load template
        if (loadTemplateMenu != null)
        {
            if (GUILayout.Button("Load ▼", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
            {
                loadTemplateMenu.ShowAsContext();
            }
        }
        EditorGUILayout.EndHorizontal();

        //Template not found
        if (Template == null || Template.textAsset == null)
        {
            EditorGUILayout.HelpBox("Couldn't find template file!\n\nVerify that the file 'TCP2_ShaderTemplate_Default.txt' is in your project.\nPlease reimport the pack if you can't find it!", MessageType.Error);
            return;
        }

        //Infobox for custom templates
        if (!string.IsNullOrEmpty(Template.templateInfo))
        {
            EditorGUILayout.HelpBox(Template.templateInfo, MessageType.Info);
        }
        if (!string.IsNullOrEmpty(Template.templateWarning))
        {
            EditorGUILayout.HelpBox(Template.templateWarning, MessageType.Warning);
        }

        TCP2_GUI.Separator();

        EditorGUI.BeginChangeCheck();
        EditorGUILayout.BeginHorizontal();
        mCurrentShader = EditorGUILayout.ObjectField("Current Shader:", mCurrentShader, typeof(Shader), false) as Shader;
        if (EditorGUI.EndChangeCheck())
        {
            if (mCurrentShader != null)
            {
                LoadCurrentConfigFromShader(mCurrentShader);
            }
        }
        if (GUILayout.Button("Copy Shader", EditorStyles.miniButton, GUILayout.Width(78f)))
        {
            CopyShader();
        }
        if (GUILayout.Button("New Shader", EditorStyles.miniButton, GUILayout.Width(76f)))
        {
            NewShader();
        }
        EditorGUILayout.EndHorizontal();

        if (mCurrentConfig.isModifiedExternally)
        {
            EditorGUILayout.HelpBox("It looks like this shader has been modified externally/manually. Updating it will overwrite the changes.", MessageType.Warning);
        }

        if (mUserShaders != null && mUserShaders.Length > 0)
        {
            EditorGUI.BeginChangeCheck();
            int   prevChoice = mConfigChoice;
            Color gColor     = GUI.color;
            GUI.color = mDirtyConfig ? gColor * Color.yellow : GUI.color;
            GUILayout.BeginHorizontal();
            mConfigChoice = EditorGUILayout.Popup("Load Shader:", mConfigChoice, mUserShadersLabels.ToArray());
            if (GUILayout.Button("◄", EditorStyles.miniButtonLeft, GUILayout.Width(22)))
            {
                mConfigChoice--;
                if (mConfigChoice < 1)
                {
                    mConfigChoice = mUserShaders.Length;
                }
            }
            if (GUILayout.Button("►", EditorStyles.miniButtonRight, GUILayout.Width(22)))
            {
                mConfigChoice++;
                if (mConfigChoice > mUserShaders.Length)
                {
                    mConfigChoice = 1;
                }
            }
            GUILayout.EndHorizontal();
            GUI.color = gColor;
            if (EditorGUI.EndChangeCheck() && prevChoice != mConfigChoice)
            {
                bool load = true;
                if (mDirtyConfig)
                {
                    if (mCurrentShader != null)
                    {
                        load = EditorUtility.DisplayDialog("TCP2 : Shader Generation", "You have unsaved changes for the following shader:\n\n" + mCurrentShader.name + "\n\nDiscard the changes and load a new shader?", "Yes", "No");
                    }
                    else
                    {
                        load = EditorUtility.DisplayDialog("TCP2 : Shader Generation", "You have unsaved changes.\n\nDiscard the changes and load a new shader?", "Yes", "No");
                    }
                }

                if (load)
                {
                    //New Shader
                    if (mConfigChoice == 0)
                    {
                        NewShader();
                    }
                    else
                    {
                        //Load selected Shader
                        Shader selectedShader = mUserShaders[mConfigChoice - 1];
                        mCurrentShader = selectedShader;
                        LoadCurrentConfigFromShader(mCurrentShader);
                    }
                }
                else
                {
                    //Revert choice
                    mConfigChoice = prevChoice;
                }
            }
        }

        EditorGUIUtility.labelWidth = lW;

        if (mCurrentConfig == null)
        {
            NewShader();
        }

        //Name & Filename
        TCP2_GUI.Separator();
        GUI.enabled = (mCurrentShader == null);
        EditorGUI.BeginChangeCheck();
        mCurrentConfig.ShaderName = EditorGUILayout.TextField(new GUIContent("Shader Name", "Path will indicate how to find the Shader in Unity's drop-down list"), mCurrentConfig.ShaderName);
        mCurrentConfig.ShaderName = Regex.Replace(mCurrentConfig.ShaderName, @"[^a-zA-Z0-9 _!/]", "");
        if (EditorGUI.EndChangeCheck() && sAutoNames)
        {
            mCurrentConfig.AutoNames();
        }
        GUI.enabled &= !sAutoNames;
        EditorGUILayout.BeginHorizontal();
        mCurrentConfig.Filename = EditorGUILayout.TextField("File Name", mCurrentConfig.Filename);
        mCurrentConfig.Filename = Regex.Replace(mCurrentConfig.Filename, @"[^a-zA-Z0-9 _!/]", "");
        GUILayout.Label(".shader", GUILayout.Width(50f));
        EditorGUILayout.EndHorizontal();
        GUI.enabled = sGUIEnabled;

        Space();

        //########################################################################################################
        // FEATURES

        TCP2_GUI.Header("FEATURES");

        //Scroll view
        mScrollPosition = EditorGUILayout.BeginScrollView(mScrollPosition);
        EditorGUI.BeginChangeCheck();

        if (Template.newSystem)
        {
            //New UI embedded into Template
            Template.FeaturesGUI(mCurrentConfig);
        }
        else
        {
            EditorGUILayout.HelpBox("Old template versions aren't supported anymore.", MessageType.Warning);
        }

#if DEBUG_MODE
        TCP2_GUI.SeparatorBig();

        TCP2_GUI.SubHeaderGray("DEBUG MODE");

        GUILayout.BeginHorizontal();
        mDebugText = EditorGUILayout.TextField("Custom", mDebugText);
        if (GUILayout.Button("Add Feature", EditorStyles.miniButtonLeft, GUILayout.Width(80f)))
        {
            mCurrentConfig.Features.Add(mDebugText);
        }
        if (GUILayout.Button("Add Flag", EditorStyles.miniButtonRight, GUILayout.Width(80f)))
        {
            mCurrentConfig.Flags.Add(mDebugText);
        }

        GUILayout.EndHorizontal();
        GUILayout.Label("Features:");
        GUILayout.BeginHorizontal();
        int count = 0;
        for (int i = 0; i < mCurrentConfig.Features.Count; i++)
        {
            if (count >= 3)
            {
                count = 0;
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();
            }
            count++;
            if (GUILayout.Button(mCurrentConfig.Features[i], EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
            {
                mCurrentConfig.Features.RemoveAt(i);
                break;
            }
        }
        GUILayout.EndHorizontal();
        GUILayout.Label("Flags:");
        GUILayout.BeginHorizontal();
        count = 0;
        for (int i = 0; i < mCurrentConfig.Flags.Count; i++)
        {
            if (count >= 3)
            {
                count = 0;
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();
            }
            count++;
            if (GUILayout.Button(mCurrentConfig.Flags[i], EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
            {
                mCurrentConfig.Flags.RemoveAt(i);
                break;
            }
        }
        GUILayout.EndHorizontal();
        GUILayout.Label("Keywords:");
        GUILayout.BeginHorizontal();
        count = 0;
        foreach (KeyValuePair <string, string> kvp in mCurrentConfig.Keywords)
        {
            if (count >= 3)
            {
                count = 0;
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();
            }
            count++;
            if (GUILayout.Button(kvp.Key + ":" + kvp.Value, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
            {
                mCurrentConfig.Keywords.Remove(kvp.Key);
                break;
            }
        }
        GUILayout.EndHorizontal();

        //----------------------------------------------------------------

        Space();
        if (mCurrentShader != null)
        {
            if (mCurrentShaderImporter == null)
            {
                mCurrentShaderImporter = ShaderImporter.GetAtPath(AssetDatabase.GetAssetPath(mCurrentShader)) as ShaderImporter;
            }

            if (mCurrentShaderImporter != null && mCurrentShaderImporter.GetShader() == mCurrentShader)
            {
                mDebugExpandUserData = EditorGUILayout.Foldout(mDebugExpandUserData, "Shader UserData");
                if (mDebugExpandUserData)
                {
                    string[] userData = mCurrentShaderImporter.userData.Split(',');
                    foreach (var str in userData)
                    {
                        GUILayout.Label(str);
                    }
                }
            }
        }
#endif

        //Update config
        if (EditorGUI.EndChangeCheck())
        {
            int newHash = mCurrentConfig.ToHash();
            if (newHash != mCurrentHash)
            {
                mDirtyConfig = true;
            }
            else
            {
                mDirtyConfig = false;
            }
        }

        //Scroll view
        EditorGUILayout.EndScrollView();

        Space();

        //GENERATE

        EditorGUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        if (GUILayout.Button(mCurrentShader == null ? "Generate Shader" : "Update Shader", GUILayout.Width(120f), GUILayout.Height(30f)))
        {
            if (Template == null)
            {
                EditorUtility.DisplayDialog("TCP2 : Shader Generation", "Can't generate shader: no Template file defined!\n\nYou most likely want to link the TCP2_User.txt file to the Template field in the Shader Generator.", "Ok");
                return;
            }

            //Set config type
            if (Template.templateType != null)
            {
                mCurrentConfig.configType = Template.templateType;
            }

            //Set config file
            mCurrentConfig.templateFile = Template.textAsset.name;

            Shader generatedShader = TCP2_ShaderGeneratorUtils.Compile(mCurrentConfig, mCurrentShader, Template, true, !sOverwriteConfigs);
            ReloadUserShaders();
            if (generatedShader != null)
            {
                mDirtyConfig = false;
                LoadCurrentConfigFromShader(generatedShader);
            }
        }
        EditorGUILayout.EndHorizontal();
        TCP2_GUI.Separator();

        // OPTIONS
        TCP2_GUI.Header("OPTIONS");

        GUILayout.BeginHorizontal();
        sSelectGeneratedShader = GUILayout.Toggle(sSelectGeneratedShader, new GUIContent("Select Generated Shader", "Will select the generated file in the Project view"), GUILayout.Width(180f));
        sAutoNames             = GUILayout.Toggle(sAutoNames, new GUIContent("Automatic Name", "Will automatically generate the shader filename based on its UI name"), GUILayout.ExpandWidth(false));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        sOverwriteConfigs = GUILayout.Toggle(sOverwriteConfigs, new GUIContent("Always overwrite shaders", "Overwrite shaders when generating/updating (no prompt)"), GUILayout.Width(180f));
        sHideDisabled     = GUILayout.Toggle(sHideDisabled, new GUIContent("Hide disabled fields", "Hide properties settings when they cannot be accessed"), GUILayout.ExpandWidth(false));
        GUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        EditorGUI.BeginChangeCheck();
        TCP2_ShaderGeneratorUtils.CustomOutputDir = GUILayout.Toggle(TCP2_ShaderGeneratorUtils.CustomOutputDir, new GUIContent("Custom Output Directory:", "Will save the generated shaders in a custom directory within the Project"), GUILayout.Width(165f));
        GUI.enabled &= TCP2_ShaderGeneratorUtils.CustomOutputDir;
        if (TCP2_ShaderGeneratorUtils.CustomOutputDir)
        {
            TCP2_ShaderGeneratorUtils.OutputPath = EditorGUILayout.TextField("", TCP2_ShaderGeneratorUtils.OutputPath);
            if (GUILayout.Button("Select...", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
            {
                string path = EditorUtility.OpenFolderPanel("Choose custom output directory for TCP2 generated shaders", Application.dataPath, "");
                if (!string.IsNullOrEmpty(path))
                {
                    bool validPath = TCP2_Utils.SystemToUnityPath(ref path);
                    if (validPath)
                    {
                        if (path == "Assets")
                        {
                            TCP2_ShaderGeneratorUtils.OutputPath = "/";
                        }
                        else
                        {
                            TCP2_ShaderGeneratorUtils.OutputPath = path.Substring("Assets/".Length);
                        }
                    }
                    else
                    {
                        EditorApplication.Beep();
                        EditorUtility.DisplayDialog("Invalid Path", "The selected path is invalid.\n\nPlease select a folder inside the \"Assets\" folder of your project!", "Ok");
                    }
                }
            }
        }
        else
        {
            EditorGUILayout.TextField("", TCP2_ShaderGeneratorUtils.OUTPUT_PATH);
        }
        if (EditorGUI.EndChangeCheck())
        {
            ReloadUserShaders();
        }

        GUI.enabled = sGUIEnabled;
        EditorGUILayout.EndHorizontal();

        EditorGUI.BeginChangeCheck();
        sLoadAllShaders = GUILayout.Toggle(sLoadAllShaders, new GUIContent("Reload Shaders from all Project", "Load shaders from all your Project folders instead of just Toony Colors Pro 2.\nEnable it if you move your generated shader files outside of the default TCP2 Generated folder."), GUILayout.ExpandWidth(false));
        if (EditorGUI.EndChangeCheck())
        {
            ReloadUserShaders();
        }

        TCP2_ShaderGeneratorUtils.SelectGeneratedShader = sSelectGeneratedShader;
    }
Beispiel #20
0
    void OnGUI()
    {
        sGUIEnabled = GUI.enabled;

        EditorGUILayout.BeginHorizontal();
        TCP2_GUI.HeaderBig("TOONY COLORS PRO 2 - SHADER GENERATOR");
        TCP2_GUI.HelpButton("Shader Generator");
        EditorGUILayout.EndHorizontal();
        TCP2_GUI.Separator();

        var lW = EditorGUIUtility.labelWidth;

        EditorGUIUtility.labelWidth = 105f;

        //Avoid refreshing Template meta at every Repaint
        EditorGUILayout.BeginHorizontal();
        var _tmpTemplate = EditorGUILayout.ObjectField("Template:", Template.textAsset, typeof(TextAsset), false) as TextAsset;

        if (_tmpTemplate != Template.textAsset)
        {
            Template.SetTextAsset(_tmpTemplate);
        }
        //Load template
        if (loadTemplateMenu != null)
        {
            if (GUILayout.Button("Load ▼", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
            {
                loadTemplateMenu.ShowAsContext();
            }
        }

        /*
         * if(GUILayout.Button("Reload", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
         * {
         *      Template.Reload();
         * }
         */
        EditorGUILayout.EndHorizontal();

        //Template not found
        if (Template == null || Template.textAsset == null)
        {
            EditorGUILayout.HelpBox("Couldn't find template file!\n\nVerify that the file 'TCP2_ShaderTemplate_Default.txt' is in your project.\nPlease reimport the pack if you can't find it!", MessageType.Error);
            return;
        }

        //Infobox for custom templates
        if (!string.IsNullOrEmpty(Template.templateInfo))
        {
            TCP2_GUI.HelpBoxLayout(Template.templateInfo, MessageType.Info);
        }
        if (!string.IsNullOrEmpty(Template.templateWarning))
        {
            TCP2_GUI.HelpBoxLayout(Template.templateWarning, MessageType.Warning);
        }

        TCP2_GUI.Separator();

        //If current shader is unsaved, show yellow color
        var gColor = GUI.color;

        GUI.color = mDirtyConfig ? gColor * unsavedChangesColor : GUI.color;

        EditorGUI.BeginChangeCheck();
        mCurrentShader = EditorGUILayout.ObjectField("Current Shader:", mCurrentShader, typeof(Shader), false) as Shader;
        if (EditorGUI.EndChangeCheck())
        {
            if (mCurrentShader != null)
            {
                LoadCurrentConfigFromShader(mCurrentShader);
            }
        }
        EditorGUILayout.BeginHorizontal();

        GUILayout.Space(EditorGUIUtility.labelWidth + 4);
        if (mDirtyConfig)
        {
            var guiContent = new GUIContent("Unsaved changes");
            var rect       = GUILayoutUtility.GetRect(guiContent, EditorStyles.helpBox, GUILayout.Height(16));
            rect.y -= 2;
            GUI.Label(rect, guiContent, EditorStyles.helpBox);
        }

        GUILayout.FlexibleSpace();
        using (new EditorGUI.DisabledScope(mCurrentShader == null))
        {
            if (GUILayout.Button("Copy", EditorStyles.miniButtonLeft, GUILayout.Width(60f), GUILayout.Height(16)))
            {
                CopyShader();
            }
        }
        if (GUILayout.Button("Load ▼", EditorStyles.miniButtonMid, GUILayout.Width(60f), GUILayout.Height(16)))
        {
            loadShadersMenu.ShowAsContext();
        }
        if (GUILayout.Button("New", EditorStyles.miniButtonRight, GUILayout.Width(60f), GUILayout.Height(16)))
        {
            NewShader();
        }
        GUILayout.Space(18);            //leave space to align with the Object Field box
        EditorGUILayout.EndHorizontal();
        GUI.color = gColor;

        if (mCurrentConfig == null)
        {
            NewShader();
        }

        if (mCurrentConfig.isModifiedExternally)
        {
            EditorGUILayout.HelpBox("It looks like this shader has been modified externally/manually. Updating it will overwrite the changes.", MessageType.Warning);
        }

        EditorGUIUtility.labelWidth = lW;

        //Name & Filename
        TCP2_GUI.Separator();
        GUI.enabled = (mCurrentShader == null);
        EditorGUI.BeginChangeCheck();
        mCurrentConfig.ShaderName = EditorGUILayout.TextField(new GUIContent("Shader Name", "Path will indicate how to find the Shader in Unity's drop-down list"), mCurrentConfig.ShaderName);
        mCurrentConfig.ShaderName = Regex.Replace(mCurrentConfig.ShaderName, @"[^a-zA-Z0-9 _!/]", "");
        if (EditorGUI.EndChangeCheck() && sAutoNames)
        {
            mCurrentConfig.AutoNames();
        }
        GUI.enabled &= !sAutoNames;
        EditorGUILayout.BeginHorizontal();
        mCurrentConfig.Filename = EditorGUILayout.TextField("File Name", mCurrentConfig.Filename);
        mCurrentConfig.Filename = Regex.Replace(mCurrentConfig.Filename, @"[^a-zA-Z0-9 _!/]", "");
        GUILayout.Label(".shader", GUILayout.Width(50f));
        EditorGUILayout.EndHorizontal();
        GUI.enabled = sGUIEnabled;

        TCP2_GUI.Separator();

        //########################################################################################################
        // FEATURES

        TCP2_GUI.Header("FEATURES");

        //Scroll view
        mScrollPosition = EditorGUILayout.BeginScrollView(mScrollPosition);
        EditorGUI.BeginChangeCheck();

        if (Template.newSystem)
        {
            //New UI embedded into Template
            Template.FeaturesGUI(mCurrentConfig);

            if (mFirstHashPass)
            {
                mCurrentHash   = mCurrentConfig.ToHash();
                mFirstHashPass = false;
            }
        }
        else
        {
            EditorGUILayout.HelpBox("Old template versions aren't supported anymore.", MessageType.Warning);
        }

#if DEBUG_MODE
        TCP2_GUI.SeparatorBig();

        TCP2_GUI.SubHeaderGray("DEBUG MODE");

        GUILayout.BeginHorizontal();
        mDebugText = EditorGUILayout.TextField("Custom", mDebugText);
        if (GUILayout.Button("Add Feature", EditorStyles.miniButtonLeft, GUILayout.Width(80f)))
        {
            mCurrentConfig.Features.Add(mDebugText);
        }
        if (GUILayout.Button("Add Flag", EditorStyles.miniButtonRight, GUILayout.Width(80f)))
        {
            mCurrentConfig.Flags.Add(mDebugText);
        }

        GUILayout.EndHorizontal();
        GUILayout.Label("Features:");
        GUILayout.BeginHorizontal();
        int count = 0;
        for (int i = 0; i < mCurrentConfig.Features.Count; i++)
        {
            if (count >= 3)
            {
                count = 0;
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();
            }
            count++;
            if (GUILayout.Button(mCurrentConfig.Features[i], EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
            {
                mCurrentConfig.Features.RemoveAt(i);
                break;
            }
        }
        GUILayout.EndHorizontal();
        GUILayout.Label("Flags:");
        GUILayout.BeginHorizontal();
        count = 0;
        for (int i = 0; i < mCurrentConfig.Flags.Count; i++)
        {
            if (count >= 3)
            {
                count = 0;
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();
            }
            count++;
            if (GUILayout.Button(mCurrentConfig.Flags[i], EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
            {
                mCurrentConfig.Flags.RemoveAt(i);
                break;
            }
        }
        GUILayout.EndHorizontal();
        GUILayout.Label("Keywords:");
        GUILayout.BeginHorizontal();
        count = 0;
        foreach (KeyValuePair <string, string> kvp in mCurrentConfig.Keywords)
        {
            if (count >= 3)
            {
                count = 0;
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal();
            }
            count++;
            if (GUILayout.Button(kvp.Key + ":" + kvp.Value, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
            {
                mCurrentConfig.Keywords.Remove(kvp.Key);
                break;
            }
        }
        GUILayout.EndHorizontal();

        //----------------------------------------------------------------

        Space();
        if (mCurrentShader != null)
        {
            if (mCurrentShaderImporter == null)
            {
                mCurrentShaderImporter = ShaderImporter.GetAtPath(AssetDatabase.GetAssetPath(mCurrentShader)) as ShaderImporter;
            }

            if (mCurrentShaderImporter != null && mCurrentShaderImporter.GetShader() == mCurrentShader)
            {
                mDebugExpandUserData = EditorGUILayout.Foldout(mDebugExpandUserData, "Shader UserData");
                if (mDebugExpandUserData)
                {
                    string[] userData = mCurrentShaderImporter.userData.Split(',');
                    foreach (var str in userData)
                    {
                        GUILayout.Label(str);
                    }
                }
            }
        }
#endif

        //Update config
        if (EditorGUI.EndChangeCheck())
        {
            var newHash = mCurrentConfig.ToHash();
            if (newHash != mCurrentHash)
            {
                mDirtyConfig = true;
            }
            else
            {
                mDirtyConfig = false;
            }
        }

        //Scroll view
        EditorGUILayout.EndScrollView();

        Space();

        //GENERATE

        EditorGUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        GUI.color = mDirtyConfig ? gColor * unsavedChangesColor : GUI.color;
        if (GUILayout.Button(mCurrentShader == null ? "Generate Shader" : "Update Shader", GUILayout.Width(120f), GUILayout.Height(30f)))
        {
            if (Template == null)
            {
                EditorUtility.DisplayDialog("TCP2 : Shader Generation", "Can't generate shader: no Template file defined!\n\nYou most likely want to link the TCP2_User.txt file to the Template field in the Shader Generator.", "Ok");
                return;
            }

            //Set config type
            if (Template.templateType != null)
            {
                mCurrentConfig.configType = Template.templateType;
            }

            //Set config file
            mCurrentConfig.templateFile = Template.textAsset.name;

            var generatedShader = TCP2_ShaderGeneratorUtils.Compile(mCurrentConfig, mCurrentShader, Template, true, !sOverwriteConfigs);
            ReloadUserShaders();
            if (generatedShader != null)
            {
                mDirtyConfig = false;
                LoadCurrentConfigFromShader(generatedShader);
            }

            //Workaround to force the inspector to refresh, so that state is reset.
            //Needed in case of switching between specular/metallic and related
            //options, while the inspector is opened, so that it shows/hides the
            //relevant properties according to the changes.
            TCP2_MaterialInspector_SurfacePBS_SG.InspectorNeedsUpdate = true;
        }
        GUI.color = gColor;
        EditorGUILayout.EndHorizontal();
        TCP2_GUI.Separator();

        // OPTIONS
        TCP2_GUI.Header("OPTIONS");

        GUILayout.BeginHorizontal();
        sSelectGeneratedShader = GUILayout.Toggle(sSelectGeneratedShader, new GUIContent("Select Generated Shader", "Will select the generated file in the Project view"), GUILayout.Width(180f));
        sAutoNames             = GUILayout.Toggle(sAutoNames, new GUIContent("Automatic Name", "Will automatically generate the shader filename based on its UI name"), GUILayout.ExpandWidth(false));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        sOverwriteConfigs = GUILayout.Toggle(sOverwriteConfigs, new GUIContent("Always overwrite shaders", "Overwrite shaders when generating/updating (no prompt)"), GUILayout.Width(180f));
        sHideDisabled     = GUILayout.Toggle(sHideDisabled, new GUIContent("Hide disabled fields", "Hide properties settings when they cannot be accessed"), GUILayout.ExpandWidth(false));
        GUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        EditorGUI.BeginChangeCheck();
        TCP2_ShaderGeneratorUtils.CustomOutputDir = GUILayout.Toggle(TCP2_ShaderGeneratorUtils.CustomOutputDir, new GUIContent("Custom Output Directory:", "Will save the generated shaders in a custom directory within the Project"), GUILayout.Width(165f));
        GUI.enabled &= TCP2_ShaderGeneratorUtils.CustomOutputDir;
        if (TCP2_ShaderGeneratorUtils.CustomOutputDir)
        {
            TCP2_ShaderGeneratorUtils.OutputPath = EditorGUILayout.TextField("", TCP2_ShaderGeneratorUtils.OutputPath);
            if (GUILayout.Button("Select...", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
            {
                var outputPath = TCP2_Utils.OpenFolderPanel_ProjectPath("Choose custom output directory for TCP2 generated shaders");
                if (!string.IsNullOrEmpty(outputPath))
                {
                    TCP2_ShaderGeneratorUtils.OutputPath = outputPath;
                }
            }
        }
        else
        {
            EditorGUILayout.TextField("", TCP2_ShaderGeneratorUtils.OUTPUT_PATH);
        }
        if (EditorGUI.EndChangeCheck())
        {
            ReloadUserShaders();
        }

        GUI.enabled = sGUIEnabled;
        EditorGUILayout.EndHorizontal();

        EditorGUI.BeginChangeCheck();
        sLoadAllShaders = GUILayout.Toggle(sLoadAllShaders, new GUIContent("Reload Shaders from all Project", "Load shaders from all your Project folders instead of just Toony Colors Pro 2.\nEnable it if you move your generated shader files outside of the default TCP2 Generated folder."), GUILayout.ExpandWidth(false));
        if (EditorGUI.EndChangeCheck())
        {
            ReloadUserShaders();
        }

        TCP2_ShaderGeneratorUtils.SelectGeneratedShader = sSelectGeneratedShader;
    }
Beispiel #21
0
    //Generate the source code for the shader as a string
    static private string GenerateShaderSource(this TCP2_Config config, string template, Shader existingShader = null)
    {
        if (config == null)
        {
            string error = "[TCP2 Shader Generator] Config file is null";
            Debug.LogError(error);
            return(error);
        }

        if (string.IsNullOrEmpty(template))
        {
            string error = "[TCP2 Shader Generator] Template string is null or empty";
            Debug.LogError(error);
            return(error);
        }

        //------------------------------------------------
        // SHADER PARAMETERS

        //Custom Lighting
        bool customLighting = NeedCustomLighting(config) || HasFeatures(config, "CUSTOM_LIGHTING_FORCE");

        if (customLighting && !config.Features.Contains("CUSTOM_LIGHTING"))
        {
            config.Features.Add("CUSTOM_LIGHTING");
        }
        else if (!customLighting && config.Features.Contains("CUSTOM_LIGHTING"))
        {
            config.Features.Remove("CUSTOM_LIGHTING");
        }

        //Custom Ambient
        bool customAmbient = NeedCustomAmbient(config) || HasFeatures(config, "CUSTOM_AMBIENT_FORCE");

        if (customAmbient && !config.Features.Contains("CUSTOM_AMBIENT"))
        {
            config.Features.Add("CUSTOM_AMBIENT");
        }
        else if (!customAmbient && config.Features.Contains("CUSTOM_AMBIENT"))
        {
            config.Features.Remove("CUSTOM_AMBIENT");
        }

        //Specific dependencies
        if (HasFeatures(config, "MATCAP_ADD", "MATCAP_MULT"))
        {
            if (!config.Features.Contains("MATCAP"))
            {
                config.Features.Add("MATCAP");
            }
        }
        else
        {
            if (config.Features.Contains("MATCAP"))
            {
                config.Features.Remove("MATCAP");
            }
        }

        //Masks
        bool   mask1 = false, mask2 = false, mask3 = false;
        string mask1features = "";
        string mask2features = "";
        string mask3features = "";

        foreach (KeyValuePair <string, string> kvp in config.Keywords)
        {
            if (kvp.Key == "UV_mask1")
            {
                ToggleFlag(config.Features, "UVMASK1", kvp.Value == "Independent UV");
            }
            else if (kvp.Key == "UV_mask2")
            {
                ToggleFlag(config.Features, "UVMASK2", kvp.Value == "Independent UV");
            }
            else if (kvp.Key == "UV_mask3")
            {
                ToggleFlag(config.Features, "UVMASK3", kvp.Value == "Independent UV");
            }

            else if (kvp.Value == "mask1")
            {
                mask1 |= GetMaskDependency(config, kvp.Key); if (mask1)
                {
                    mask1features += GetDisplayNameForMask(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "mask2")
            {
                mask2 |= GetMaskDependency(config, kvp.Key); if (mask2)
                {
                    mask2features += GetDisplayNameForMask(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "mask3")
            {
                mask3 |= GetMaskDependency(config, kvp.Key); if (mask3)
                {
                    mask3features += GetDisplayNameForMask(kvp.Key) + ",";
                }
            }
        }
        mask1features = mask1features.TrimEnd(',');
        mask2features = mask2features.TrimEnd(',');
        mask3features = mask3features.TrimEnd(',');

        ToggleFlag(config.Features, "MASK1", mask1);
        ToggleFlag(config.Features, "MASK2", mask2);
        ToggleFlag(config.Features, "MASK3", mask3);

        //---

        Dictionary <string, string> keywords = new Dictionary <string, string>(config.Keywords);
        List <string> flags    = new List <string>(config.Flags);
        List <string> features = new List <string>(config.Features);

        //Unity version
#if UNITY_5_4_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_5_4");
#endif

        //Masks
        keywords.Add("MASK1", mask1features);
        keywords.Add("MASK2", mask2features);
        keywords.Add("MASK3", mask3features);

        //Shader name
        keywords.Add("SHADER_NAME", config.ShaderName);

        //Include path
        string include = GetIncludePrefix(config) + GetIncludeRelativePath(config, existingShader) + GetIncludeFile(config);
        keywords.Add("INCLUDE_PATH", include);

        //Lighting Model
        string lightingModel = GetLightingFunction(config);
        keywords.Add("LIGHTING_MODEL", lightingModel);

        //SurfaceOutput struct
        string surfOut = GetSurfaceOutput(config);
        keywords.Add("SURFACE_OUTPUT", surfOut);

        //Shader Model target
        string target = GetShaderTarget(config);
        keywords.Add("SHADER_TARGET", target);
        if (config.shaderTarget == 20)
        {
            TCP2_Utils.AddIfMissing(features, "FORCE_SM2");
        }

        //Vertex Function
        bool vertexFunction = NeedVertexFunction(config);
        if (vertexFunction)
        {
            TCP2_Utils.AddIfMissing(flags, "vertex:vert");
            features.Add("VERTEX_FUNC");
        }

        //Final Colors Function
        bool finalColorFunction = NeedFinalColorFunction(config);
        if (finalColorFunction)
        {
            TCP2_Utils.AddIfMissing(flags, "finalcolor:fcolor");
            features.Add("FINAL_COLOR");
        }

        //Alpha Testing (Cutout)
        if (HasFeatures(config, "CUTOUT"))
        {
            TCP2_Utils.AddIfMissing(flags, "alphatest:_Cutoff");
        }

#if UNITY_5
        //Alpha
        if (HasFeatures(config, "ALPHA"))
        {
            TCP2_Utils.AddIfMissing(flags, "keepalpha");
        }
#endif

        //Shadows
        if (HasFeatures(config, "CUTOUT"))
        {
            TCP2_Utils.AddIfMissing(flags, "addshadow");
        }

        //No/Custom Ambient
        if (HasFeatures(config, "CUSTOM_AMBIENT"))
        {
            TCP2_Utils.AddIfMissing(flags, "noambient");
        }

        //Generate Surface parameters
        string strFlags = ArrayToString(flags.ToArray(), " ");
        keywords.Add("SURF_PARAMS", strFlags);

        //------------------------------------------------
        // PARSING & GENERATION

        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        string[] templateLines       = template.Split(new string[] { "\r\n", "\n" }, System.StringSplitOptions.None);

        int         depth = -1;
        List <bool> stack = new List <bool>();
        List <bool> done  = new List <bool>();

        //Parse template file
        string line = null;
        for (int i = 0; i < templateLines.Length; i++)
        {
            line = templateLines[i];

            //Comment
            if (line.StartsWith("#"))
            {
                //Meta
                if (line.StartsWith("#CONFIG="))
                {
                    config.configType = line.Substring(8).TrimEnd().ToLower();
                }

                //Debugging
                if (line.StartsWith("#break"))
                {
                    Debug.Log("[TCP2] Parse Break @ " + i);
                }

                continue;
            }

            //Line break
            if (string.IsNullOrEmpty(line) && ((depth >= 0 && stack[depth]) || depth < 0))
            {
                sb.AppendLine(line);
                continue;
            }

            //Conditions
            if (line.Contains("///"))
            {
                //Remove leading white spaces
                line = line.TrimStart();

                string[] parts = line.Split(new string[] { " " }, System.StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length == 1)                  //END TAG
                {
                    if (depth < 0)
                    {
                        string error = "[TCP2 Shader Generator] Found end tag /// without any beginning! Aborting shader generation.\n@ line: " + i;
                        Debug.LogError(error);
                        return(error);
                    }

                    stack.RemoveAt(depth);
                    done.RemoveAt(depth);
                    depth--;
                }
                else if (parts.Length >= 2)
                {
                    if (parts[1] == "IF")
                    {
                        bool cond = EvaluateExpression(i, features, parts);

                        depth++;
                        stack.Add(cond && ((depth <= 0) ? true : stack[depth - 1]));
                        done.Add(cond);
                    }
                    else if (parts[1] == "ELIF")
                    {
                        if (done[depth])
                        {
                            stack[depth] = false;
                            continue;
                        }

                        bool cond = EvaluateExpression(i, features, parts);

                        stack[depth] = cond && ((depth <= 0) ? true : stack[depth - 1]);
                        done[depth]  = cond;
                    }
                    else if (parts[1] == "ELSE")
                    {
                        if (done[depth])
                        {
                            stack[depth] = false;
                            continue;
                        }
                        else
                        {
                            stack[depth] = ((depth <= 0) ? true : stack[depth - 1]);
                            done[depth]  = true;
                        }
                    }
                }
            }
            //Regular line
            else
            {
                //Replace keywords
                line = ReplaceKeywords(line, keywords);

                if ((depth >= 0 && stack[depth]) || depth < 0)
                {
                    sb.AppendLine(line);
                }
            }
        }

        if (depth >= 0)
        {
            Debug.LogWarning("[TCP2 Shader Generator] Missing " + (depth + 1) + " ending '///' tags");
        }

        string sourceCode = sb.ToString();
        return(sourceCode);
    }
Beispiel #22
0
    //Extract an archive into an array of PackedFile
    static public PackedFile[] ExtractArchive(string archivePath, string filter = null)
    {
        string archive = File.ReadAllText(archivePath);

        string[] archiveLines = File.ReadAllLines(archivePath);

        if (archiveLines[0] != "# TCP2 PACKED SHADERS")
        {
            EditorApplication.Beep();
            Debug.LogError("[TCP2 ExtractArchive] Invalid TCP2 archive:\n" + archivePath);
            return(null);
        }

        //Find offset
        int offset = archive.IndexOf("###") + 4;

        if (offset < 20)
        {
            Debug.LogError("[TCP2 ExtractArchive] Invalid TCP2 archive:\n" + archivePath);
            return(null);
        }

        string            tcpRoot         = TCP2_Utils.FindReadmePath();
        List <PackedFile> packedFilesList = new List <PackedFile>();

        for (int line = 1; line < archiveLines.Length; line++)
        {
            //Index end, start content parsing
            if (archiveLines[line].StartsWith("#"))
            {
                break;
            }

            string[] shaderIndex = archiveLines[line].Split(new string[] { ";" }, System.StringSplitOptions.RemoveEmptyEntries);
            if (shaderIndex.Length != 3)
            {
                EditorApplication.Beep();
                Debug.LogError("[TCP2 ExtractArchive] Invalid format in TCP2 archive, at line " + line + ":\n" + archivePath);
                return(null);
            }

            //Get data
            string relativePath = shaderIndex[0];
            int    start        = int.Parse(shaderIndex[1]);
            int    length       = int.Parse(shaderIndex[2]);
            //Get content
            string content = archive.Substring(offset + start, length);

            //Skip if file already extracted
            if (File.Exists(tcpRoot + relativePath))
            {
                continue;
            }

            //Filter?
            if (!string.IsNullOrEmpty(filter))
            {
                string[] filters = filter.Split(new string[] { " " }, System.StringSplitOptions.RemoveEmptyEntries);
                bool     skip    = false;
                foreach (string f in filters)
                {
                    if (!relativePath.ToLower().Contains(f.ToLower()))
                    {
                        skip = true;
                        break;
                    }
                }
                if (skip)
                {
                    continue;
                }
            }

            //Add File
            packedFilesList.Add(new PackedFile(relativePath, content));
        }

        return(packedFilesList.ToArray());
    }
    void OnGUI()
    {
        EditorGUILayout.BeginHorizontal();
        TCP2_GUI.HeaderBig("TCP 2 - SMOOTHED NORMALS UTILITY");
        TCP2_GUI.HelpButton("Smoothed Normals Utility");
        EditorGUILayout.EndHorizontal();
        TCP2_GUI.Separator();

        /*
         * mFormat = EditorGUILayout.TextField(new GUIContent("Axis format", "Normals axis may need to be swapped before being packed into vertex colors/tangent/uv2 data. See documentation for more information."), mFormat);
         * mFormat = Regex.Replace(mFormat, @"[^xyzXYZ-]", "");
         * EditorGUILayout.BeginHorizontal();
         * GUILayout.Label("Known formats:");
         * if(GUILayout.Button("XYZ", EditorStyles.miniButtonLeft)) { mFormat = "XYZ"; GUI.FocusControl(null); }
         * if(GUILayout.Button("-YZ-X", EditorStyles.miniButtonMid)) { mFormat = "-YZ-X"; GUI.FocusControl(null); }
         * if(GUILayout.Button("-Z-Y-X", EditorStyles.miniButtonRight)) { mFormat = "-Z-Y-X"; GUI.FocusControl(null); }
         * EditorGUILayout.EndHorizontal();
         */

        if (mMeshes != null && mMeshes.Count > 0)
        {
            GUILayout.Space(4);
            TCP2_GUI.Header("Meshes ready to be processed:", null, true);
            mScroll = EditorGUILayout.BeginScrollView(mScroll);
            TCP2_GUI.GUILine(Color.gray, 1);
            foreach (var sm in mMeshes.Values)
            {
                GUILayout.Space(2);
                GUILayout.BeginHorizontal();
                var label = sm.name;
                if (label.Contains(MESH_SUFFIX))
                {
                    label = label.Replace(MESH_SUFFIX, "\n" + MESH_SUFFIX);
                }
                GUILayout.Label(label, EditorStyles.wordWrappedMiniLabel, GUILayout.Width(270));
                sm.isSkinned = GUILayout.Toggle(sm.isSkinned, new GUIContent("Skinned", "Should be checked if the mesh will be used on a SkinnedMeshRenderer"), EditorStyles.toolbarButton);
                GUILayout.Space(6);
                GUILayout.EndHorizontal();
                GUILayout.Space(2);
                TCP2_GUI.GUILine(Color.gray, 1);
            }
            EditorGUILayout.EndScrollView();

            GUILayout.FlexibleSpace();
            if (GUILayout.Button(mMeshes.Count == 1 ? "Generate Smoothed Mesh" : "Generate Smoothed Meshes", GUILayout.Height(30)))
            {
                try
                {
                    var   selection = new List <Object>();
                    float progress  = 1;
                    float total     = mMeshes.Count;
                    foreach (var sm in mMeshes.Values)
                    {
                        if (sm == null)
                        {
                            continue;
                        }

                        EditorUtility.DisplayProgressBar("Hold On", (mMeshes.Count > 1 ? "Generating Smoothed Meshes:\n" : "Generating Smoothed Mesh:\n") + sm.name, progress / total);
                        progress++;
                        Object o = CreateSmoothedMeshAsset(sm);
                        if (o != null)
                        {
                            selection.Add(o);
                        }
                    }
                    Selection.objects = selection.ToArray();
                }
                finally
                {
                    EditorUtility.ClearProgressBar();
                }
            }
        }
        else
        {
            EditorGUILayout.HelpBox("Select one or multiple meshes to create a smoothed normals version.\n\nYou can also select models directly in the Scene, the new mesh will automatically be assigned.", MessageType.Info);
            GUILayout.FlexibleSpace();
            using (new EditorGUI.DisabledScope(true))
                GUILayout.Button("Generate Smoothed Mesh", GUILayout.Height(30));
        }

        TCP2_GUI.Separator();

        TCP2_GUI.Header("Store smoothed normals in:", "You will have to select the correct option in the Material Inspector when using outlines", true);

        var choice = 0;

        if (mTangents)
        {
            choice = 1;
        }
        if (mUV2)
        {
            choice = 2;
        }
        choice = TCP2_GUI.RadioChoice(choice, true, "Vertex Colors", "Tangents", "UV2");
        EditorGUILayout.HelpBox("Smoothed Normals for Skinned meshes will be stored in Tangents only. See Help to know why.", MessageType.Warning);

        mVColors  = (choice == 0);
        mTangents = (choice == 1);
        mUV2      = (choice == 2);

        TCP2_GUI.Separator();

        TCP2_GUI.Header("Options", null, true);
        mAlwaysOverwrite = EditorGUILayout.Toggle(new GUIContent("Always Overwrite", "Will always overwrite existing [TCP2 Smoothed] meshes"), mAlwaysOverwrite);
        mCustomDirectory = EditorGUILayout.Toggle(new GUIContent("Custom Output Directory", "Save the generated smoothed meshes in a custom directory"), mCustomDirectory);
        using (new EditorGUI.DisabledScope(!mCustomDirectory))
        {
            EditorGUILayout.BeginHorizontal();
            mCustomDirectoryPath = EditorGUILayout.TextField(GUIContent.none, mCustomDirectoryPath);
            if (GUILayout.Button("Select...", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
            {
                var outputPath = TCP2_Utils.OpenFolderPanel_ProjectPath("Choose custom output directory for generated smoothed meshes");
                if (!string.IsNullOrEmpty(outputPath))
                {
                    mCustomDirectoryPath = outputPath;
                }
            }
            EditorGUILayout.EndHorizontal();
        };

        GUILayout.Space(10);
    }
    public override void OnInspectorGUI()
    {
        if (!this.isVisible)
        {
            return;
        }

#if SHOW_DEFAULT_INSPECTOR
        base.OnInspectorGUI();
        return;
#else
        //Detect if Shader has changed
        if (targetMaterial.shader != mCurrentShader)
        {
            mCurrentShader = targetMaterial.shader;
        }

        UpdateFeaturesFromShader();

        //Get material keywords
        List <string> keywordsList   = new List <string>(targetMaterial.shaderKeywords);
        bool          updateKeywords = false;

        //Header
        TCP2_GUI.HeaderBig("TOONY COLORS PRO 2 - Outlines Only");
        TCP2_GUI.Separator();

        //Iterate Shader properties
        serializedObject.Update();
        SerializedProperty mShader = serializedObject.FindProperty("m_Shader");
        if (isVisible && !mShader.hasMultipleDifferentValues && mShader.objectReferenceValue != null)
        {
            EditorGUIUtility.labelWidth = TCP2_Utils.ScreenWidthRetina - 120f;
            EditorGUIUtility.fieldWidth = 64f;

            EditorGUI.BeginChangeCheck();

            MaterialProperty[] props = GetMaterialProperties(this.targets);

            //UNFILTERED PARAMETERS ==============================================================

            if (ShowFilteredProperties(null, props, false))
            {
                TCP2_GUI.Separator();
            }

            //FILTERED PARAMETERS ================================================================

            //Outline Type ---------------------------------------------------------------------------
            ShowFilteredProperties("#OUTLINE#", props, false);
            if (!mShaderModel2)
            {
                bool texturedOutline = TCP2_Utils.ShaderKeywordToggle("TCP2_OUTLINE_TEXTURED", "Outline Color from Texture", "If enabled, outline will take an averaged color from the main texture multiplied by Outline Color", keywordsList, ref updateKeywords);
                if (texturedOutline)
                {
                    ShowFilteredProperties("#OUTLINETEX#", props);
                }
            }

            TCP2_Utils.ShaderKeywordToggle("TCP2_OUTLINE_CONST_SIZE", "Constant Size Outline", "If enabled, outline will have a constant size independently from camera distance", keywordsList, ref updateKeywords);
            if (TCP2_Utils.ShaderKeywordToggle("TCP2_ZSMOOTH_ON", "Correct Z Artefacts", "Enable the outline z-correction to try to hide artefacts from complex models", keywordsList, ref updateKeywords))
            {
                ShowFilteredProperties("#OUTLINEZ#", props);
            }

            //Smoothed Normals -----------------------------------------------------------------------
            TCP2_GUI.Header("OUTLINE NORMALS", "Defines where to take the vertex normals from to draw the outline.\nChange this when using a smoothed mesh to fill the gaps shown in hard-edged meshes.");
            TCP2_Utils.ShaderKeywordRadio(null, new string[] { "TCP2_NONE", "TCP2_COLORS_AS_NORMALS", "TCP2_TANGENT_AS_NORMALS", "TCP2_UV2_AS_NORMALS" }, new GUIContent[]
            {
                new GUIContent("Regular", "Use regular vertex normals"),
                new GUIContent("Vertex Colors", "Use vertex colors as normals (with smoothed mesh)"),
                new GUIContent("Tangents", "Use tangents as normals (with smoothed mesh)"),
                new GUIContent("UV2", "Use second texture coordinates as normals (with smoothed mesh)"),
            },
                                          keywordsList, ref updateKeywords);

            //Outline Blending -----------------------------------------------------------------------

            if (mIsOutlineBlending)
            {
                MaterialProperty[] blendProps = GetFilteredProperties("#BLEND#", props);

                if (blendProps.Length != 2)
                {
                    EditorGUILayout.HelpBox("Couldn't find Blending properties!", MessageType.Error);
                }
                else
                {
                    TCP2_GUI.Header("OUTLINE BLENDING", "BLENDING EXAMPLES:\nAlpha Transparency: SrcAlpha / OneMinusSrcAlpha\nMultiply: DstColor / Zero\nAdd: One / One\nSoft Add: OneMinusDstColor / One");

                    UnityEngine.Rendering.BlendMode blendSrc = (UnityEngine.Rendering.BlendMode)blendProps[0].floatValue;
                    UnityEngine.Rendering.BlendMode blendDst = (UnityEngine.Rendering.BlendMode)blendProps[1].floatValue;

                    EditorGUI.BeginChangeCheck();
                    float f = EditorGUIUtility.fieldWidth;
                    float l = EditorGUIUtility.labelWidth;
                    EditorGUIUtility.fieldWidth  = 110f;
                    EditorGUIUtility.labelWidth -= Mathf.Abs(f - EditorGUIUtility.fieldWidth);
                    blendSrc = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup("Source Factor", blendSrc);
                    blendDst = (UnityEngine.Rendering.BlendMode)EditorGUILayout.EnumPopup("Destination Factor", blendDst);
                    EditorGUIUtility.fieldWidth = f;
                    EditorGUIUtility.labelWidth = l;
                    if (EditorGUI.EndChangeCheck())
                    {
                        blendProps[0].floatValue = (float)blendSrc;
                        blendProps[1].floatValue = (float)blendDst;
                    }
                }
            }

            TCP2_GUI.Separator();

            //--------------------------------------------------------------------------------------

            if (EditorGUI.EndChangeCheck())
            {
                PropertiesChanged();
            }
        }

        //Update Keywords
        if (updateKeywords)
        {
            if (targets != null && targets.Length > 0)
            {
                foreach (Object t in targets)
                {
                    (t as Material).shaderKeywords = keywordsList.ToArray();
                    EditorUtility.SetDirty(t);
                }
            }
            else
            {
                targetMaterial.shaderKeywords = keywordsList.ToArray();
                EditorUtility.SetDirty(targetMaterial);
            }
        }
#endif
    }
    //Generate the source code for the shader as a string
    static private string GenerateShaderSource(this TCP2_Config config, TCP2_ShaderGenerator.ShaderGeneratorTemplate template, Shader existingShader = null)
    {
        if (config == null)
        {
            string error = "[TCP2 Shader Generator] Config file is null";
            Debug.LogError(error);
            return(error);
        }

        if (template == null)
        {
            string error = "[TCP2 Shader Generator] Template is null";
            Debug.LogError(error);
            return(error);
        }

        if (template.textAsset == null || string.IsNullOrEmpty(template.textAsset.text))
        {
            string error = "[TCP2 Shader Generator] Template string is null or empty";
            Debug.LogError(error);
            return(error);
        }

        //------------------------------------------------
        // SHADER PARAMETERS

        //Old hard-coded dependencies
        if (!template.newSystem)
        {
            //Custom Lighting
            bool customLighting = NeedCustomLighting(config) || HasFeatures(config, "CUSTOM_LIGHTING_FORCE");
            if (customLighting && !config.Features.Contains("CUSTOM_LIGHTING"))
            {
                config.Features.Add("CUSTOM_LIGHTING");
            }
            else if (!customLighting && config.Features.Contains("CUSTOM_LIGHTING"))
            {
                config.Features.Remove("CUSTOM_LIGHTING");
            }

            //Custom Ambient
            bool customAmbient = NeedCustomAmbient(config) || HasFeatures(config, "CUSTOM_AMBIENT_FORCE");
            if (customAmbient && !config.Features.Contains("CUSTOM_AMBIENT"))
            {
                config.Features.Add("CUSTOM_AMBIENT");
            }
            else if (!customAmbient && config.Features.Contains("CUSTOM_AMBIENT"))
            {
                config.Features.Remove("CUSTOM_AMBIENT");
            }

            //Specific dependencies
            if (HasFeatures(config, "MATCAP_ADD", "MATCAP_MULT"))
            {
                if (!config.Features.Contains("MATCAP"))
                {
                    config.Features.Add("MATCAP");
                }
            }
            else
            {
                if (config.Features.Contains("MATCAP"))
                {
                    config.Features.Remove("MATCAP");
                }
            }
        }

        //Masks
        bool   mask1 = false, mask2 = false, mask3 = false, vcolors_mask = false;
        string mask1features = "";
        string mask2features = "";
        string mask3features = "";

        //Enable Masks according to their dependencies
        foreach (KeyValuePair <string, string> kvp in config.Keywords)
        {
            if (kvp.Value == "mask1")
            {
                mask1 |= GetMaskDependency(config, kvp.Key); if (mask1)
                {
                    mask1features += GetDisplayNameForMask(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "mask2")
            {
                mask2 |= GetMaskDependency(config, kvp.Key); if (mask2)
                {
                    mask2features += GetDisplayNameForMask(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "mask3")
            {
                mask3 |= GetMaskDependency(config, kvp.Key); if (mask3)
                {
                    mask3features += GetDisplayNameForMask(kvp.Key) + ",";
                }
            }
            else if (kvp.Value == "IN.color")
            {
                vcolors_mask |= GetMaskDependency(config, kvp.Key);
            }
        }

        //Only enable Independent UVs if relevant Mask is actually enabled
        foreach (KeyValuePair <string, string> kvp in config.Keywords)
        {
            if (kvp.Key == "UV_mask1")
            {
                ToggleSingleFeature(config.Features, "UVMASK1", kvp.Value == "Independent UV" && mask1);
            }
            else if (kvp.Key == "UV_mask2")
            {
                ToggleSingleFeature(config.Features, "UVMASK2", kvp.Value == "Independent UV" && mask2);
            }
            else if (kvp.Key == "UV_mask3")
            {
                ToggleSingleFeature(config.Features, "UVMASK3", kvp.Value == "Independent UV" && mask3);
            }
        }
        mask1features = mask1features.TrimEnd(',');
        mask2features = mask2features.TrimEnd(',');
        mask3features = mask3features.TrimEnd(',');

        ToggleSingleFeature(config.Features, "MASK1", mask1);
        ToggleSingleFeature(config.Features, "MASK2", mask2);
        ToggleSingleFeature(config.Features, "MASK3", mask3);
        ToggleSingleFeature(config.Features, "VCOLORS_MASK", vcolors_mask);

        //---

        Dictionary <string, string> keywords = new Dictionary <string, string>(config.Keywords);
        List <string> flags    = new List <string>(config.Flags);
        List <string> features = new List <string>(config.Features);

        //Unity version
#if UNITY_5_4_OR_NEWER
        TCP2_Utils.AddIfMissing(features, "UNITY_5_4");
#endif

        //Masks
        keywords.Add("MASK1", mask1features);
        keywords.Add("MASK2", mask2features);
        keywords.Add("MASK3", mask3features);

        //Shader name
        keywords.Add("SHADER_NAME", config.ShaderName);

        //Include path
        string include = GetIncludePrefix(config) + GetIncludeRelativePath(config, existingShader) + GetIncludeFile(config);
        keywords.Add("INCLUDE_PATH", include);

        //Lighting Model
        if (!template.newSystem)
        {
            string lightingModel = GetLightingFunction(config);
            keywords.Add("LIGHTING_MODEL", lightingModel);
        }

        //SurfaceOutput struct
        string surfOut = GetSurfaceOutput(config);
        keywords.Add("SURFACE_OUTPUT", surfOut);

        //Shader Model target
        string target = GetShaderTarget(config);
        keywords.Add("SHADER_TARGET", target);
        if (config.shaderTarget == 20)
        {
            TCP2_Utils.AddIfMissing(features, "FORCE_SM2");
        }

        if (!template.newSystem)
        {
            //Vertex Function
            bool vertexFunction = NeedVertexFunction(config);
            if (vertexFunction)
            {
                TCP2_Utils.AddIfMissing(flags, "vertex:vert");
                features.Add("VERTEX_FUNC");
            }

            //Final Colors Function
            bool finalColorFunction = NeedFinalColorFunction(config);
            if (finalColorFunction)
            {
                TCP2_Utils.AddIfMissing(flags, "finalcolor:fcolor");
                features.Add("FINAL_COLOR");
            }

            //Alpha Testing (Cutout)
            if (HasFeatures(config, "CUTOUT"))
            {
                TCP2_Utils.AddIfMissing(flags, "alphatest:_Cutoff");
            }
        }

#if UNITY_5
        //Alpha
        if (HasFeatures(config, "ALPHA"))
        {
            TCP2_Utils.AddIfMissing(flags, "keepalpha");
        }
#endif

        //Shadows
        if (HasFeatures(config, "CUTOUT"))
        {
            TCP2_Utils.AddIfMissing(flags, "addshadow");
        }

        //No/Custom Ambient
        if (HasFeatures(config, "CUSTOM_AMBIENT"))
        {
            TCP2_Utils.AddIfMissing(flags, "noambient");
        }

        //Generate Surface parameters
        string strFlags = ArrayToString(flags.ToArray(), " ");
        keywords.Add("SURF_PARAMS", strFlags);

        //------------------------------------------------
        // PARSING & GENERATION

        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        string[] templateLines       = template.textAsset.text.Split(new string[] { "\r\n", "\n" }, System.StringSplitOptions.None);

        int         depth = -1;
        List <bool> stack = new List <bool>();
        List <bool> done  = new List <bool>();

        //Parse template file
        string line = null;
        for (int i = 0; i < templateLines.Length; i++)
        {
            line = templateLines[i];

            //Comment
            if (line.StartsWith("#"))
            {
                //Meta
                if (line.StartsWith("#CONFIG="))
                {
                    config.configType = line.Substring(8).TrimEnd().ToLower();
                }

                //Features UI
                if (line.StartsWith("#FEATURES"))
                {
                    while (i < templateLines.Length)
                    {
                        i++;
                        if (templateLines[i] == "#END")
                        {
                            break;
                        }
                    }
                    continue;
                }

                //Keywords
                if (line.StartsWith("#KEYWORDS"))
                {
                    while (i < templateLines.Length)
                    {
                        i++;
                        if (templateLines[i] == "#END")
                        {
                            break;
                        }

                        string error = ProcessKeywords(templateLines[i], ref features, ref flags, ref keywords, ref i, ref depth, ref stack, ref done, template.newSystem);
                        if (!string.IsNullOrEmpty(error))
                        {
                            return(error);
                        }
                    }

                    //Update Surface parameters
                    strFlags = ArrayToString(flags.ToArray(), " ");
                    if (keywords.ContainsKey("SURF_PARAMS"))
                    {
                        keywords["SURF_PARAMS"] = strFlags;
                    }
                    else
                    {
                        keywords.Add("SURF_PARAMS", strFlags);
                    }
                }

                //Debugging
                if (line.StartsWith("#break"))
                {
                    Debug.Log("[TCP2] Parse Break @ " + i);
                }

                continue;
            }

            //Line break
            if (string.IsNullOrEmpty(line) && ((depth >= 0 && stack[depth]) || depth < 0))
            {
                sb.AppendLine(line);
                continue;
            }

            //Conditions
            if (line.Contains("///"))
            {
                string error = ProcessCondition(line, ref features, ref i, ref depth, ref stack, ref done, template.newSystem);
                if (!string.IsNullOrEmpty(error))
                {
                    return(error);
                }
            }
            //Regular line
            else
            {
                //Replace keywords
                line = ReplaceKeywords(line, keywords);

                //Append line if inside valid condition block
                if ((depth >= 0 && stack[depth]) || depth < 0)
                {
                    sb.AppendLine(line);
                }
            }
        }

        if (depth >= 0)
        {
            Debug.LogWarning("[TCP2 Shader Generator] Missing " + (depth + 1) + " ending '///' tags");
        }

        string sourceCode = sb.ToString();

        //Normalize line endings
        sourceCode = sourceCode.Replace("\r\n", "\n");

        return(sourceCode);
    }