示例#1
0
    //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);
    }
    //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 = "";

        if (template.newSystem)
        {
            //Enable Masks according to their dependencies (new system using Template)
            foreach (KeyValuePair <string, string> kvp in config.Keywords)
            {
                if (kvp.Value == "mask1")
                {
                    bool maskEnabled = template.GetMaskDependency(kvp.Key, config);
                    mask1 |= maskEnabled;
                    if (maskEnabled)
                    {
                        mask1features += template.GetMaskDisplayName(kvp.Key) + ",";
                    }
                }
                else if (kvp.Value == "mask2")
                {
                    bool maskEnabled = template.GetMaskDependency(kvp.Key, config);
                    mask2 |= maskEnabled;
                    if (maskEnabled)
                    {
                        mask2features += template.GetMaskDisplayName(kvp.Key) + ",";
                    }
                }
                else if (kvp.Value == "mask3")
                {
                    bool maskEnabled = template.GetMaskDependency(kvp.Key, config);
                    mask3 |= maskEnabled;
                    if (maskEnabled)
                    {
                        mask3features += template.GetMaskDisplayName(kvp.Key) + ",";
                    }
                }
                else if (kvp.Value == "IN.color")
                {
                    vcolors_mask |= template.GetMaskDependency(kvp.Key, config);
                }
            }
        }
        else
        {
            //Enable Masks according to their dependencies (old hard-coded system)
            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);
    }