//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); }