//Deserialize without knowing type public static object Deserialize(string data, object[] args = null) { //extract serialized class name var index = data.IndexOf('('); var serializedClassName = data.Substring(0, index); //fetch all serialized classes names, and try to match it Type type = null; var allTypes = typeof(Serialization).Assembly.GetTypes(); foreach (var t in allTypes) { var classAttributes = t.GetCustomAttributes(typeof(SerializeAsAttribute), false); if (classAttributes != null && classAttributes.Length == 1) { var name = (classAttributes[0] as SerializeAsAttribute).serializedName; if (name == serializedClassName) { //match! type = t; } } } if (type == null) { Debug.LogError(ShaderGenerator2.ErrorMsg("Can't find proper Type for serialized class named '<b>" + serializedClassName + "</b>'")); return(null); } //return new object with correct type return(Deserialize(data, type, args)); }
string[] ArgumentLines(string[] array, Argument[] arguments, List <string> suppliedArguments) { if (arguments == null || arguments.Length == 0) { return(array); } else { if (suppliedArguments.Count != arguments.Length) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("[Module {4}] Invalid number of arguments provided: got <b>{0}</b>, expected <b>{1}</b>:\nExpected: {2}\nSupplied: {3}", suppliedArguments.Count, arguments.Length, string.Join(", ", System.Array.ConvertAll(arguments, a => a.ToString())), string.Join(", ", suppliedArguments.ToArray()), this.name))); } var list = new List <string>(); foreach (var line in array) { string lineWithArgs = line; for (int i = 0; i < arguments.Length; i++) { lineWithArgs = System.Text.RegularExpressions.Regex.Replace(lineWithArgs, @"\b" + arguments[i].name + @"\b", suppliedArguments[i]); } list.Add(lineWithArgs); } return(list.ToArray()); } }
//Process the #KEYWORDS block for this config internal void ProcessKeywordsBlock(Config config, List <string> conditionalFeatures, List <string> tempFeatures, List <string> tempFlags) { var depth = -1; var stack = new List <bool>(); var done = new List <bool>(); for (var i = 0; i < textLines.Length; i++) { var line = textLines[i]; if (line.StartsWith("#KEYWORDS")) { int keywordsStartIndex = i + 1; while (i < textLines.Length) { line = textLines[i]; i++; if (line.StartsWith("#END")) { return; } //Conditions if (line.Contains("///")) { var error = ExpressionParser.ProcessCondition(line, conditionalFeatures, ref depth, ref stack, ref done); if (!string.IsNullOrEmpty(error)) { Debug.LogError(ShaderGenerator2.ErrorMsg(error)); } } //Regular line else { //Process line if inside valid condition block if ((depth >= 0 && stack[depth]) || depth < 0) { if (config.ProcessKeywords(line, tempFeatures, tempFlags)) { // add the new toggled features, if any foreach (var f in tempFeatures) { Utils.AddIfMissing(conditionalFeatures, f); } // reset the loop, so that the #keywords order doesn't matter in the template i = keywordsStartIndex; continue; } } } } } } }
//Return the Fragment Lines with the arguments replaced with their proper names public string[] FragmentLines(List <string> arguments, string key = "") { Argument[] args; string[] lines; FragmentsArgs.TryGetValue(key, out args); Fragments.TryGetValue(key, out lines); if (lines == null) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Can't find #FRAGMENT/#LIGHTING for Module '{0}{1}'", this.name, string.IsNullOrEmpty(key) ? "" : ":" + key))); return(null); } return(ArgumentLines(lines, args, arguments)); }
//Process the #INPUT block: retrieve all necessary variables //for Input struct (surface shader) or v2f struct (vert/frag shader) internal string[] GetInputBlock(ParsedLine[] parsedLines, int pass) { var variablesList = new List <string>(); int currentPass = -1; for (var i = 0; i < parsedLines.Length; i++) { var line = parsedLines[i].line; if (line.StartsWith("#PASS")) { currentPass++; } if (line.StartsWith("#INPUT_VARIABLES") && currentPass == pass) { i++; while (i < parsedLines.Length) { line = parsedLines[i].line; i++; if (line.StartsWith("#END")) { return(variablesList.ToArray()); } if (line.StartsWith("#") || string.IsNullOrEmpty(line.Trim())) { continue; } //Conditions if (line.Contains("///")) { Debug.LogError(ShaderGenerator2.ErrorMsg("GetInputBlock: template lines should already have been parsed and cleared of conditions")); } //Regular line else { variablesList.Add(line.Trim()); } } } } return(null); }
//-------- private static TextAsset LoadTextAsset(string filename) { string rootPath = Utils.FindReadmePath(true); var asset = AssetDatabase.LoadAssetAtPath <TextAsset>(string.Format("{0}/Editor/Shader Templates/{1}", rootPath, filename)); if (asset == null) { var filenameNoExtension = Path.GetFileNameWithoutExtension(filename); var guids = AssetDatabase.FindAssets(string.Format("{0} t:TextAsset", filenameNoExtension)); if (guids.Length >= 1) { var path = AssetDatabase.GUIDToAssetPath(guids[0]); asset = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)) as TextAsset; } else { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Can't find template using Unity's search system. Make sure that the file '{0}' is in the project!", filename))); } } return(asset); }
static public Module CreateFromName(string moduleName) { string moduleFile = string.Format("Module_{0}.txt", moduleName); string rootPath = Utils.FindReadmePath(true); string modulePath = string.Format("{0}/Shader Templates 2/Modules/{1}", rootPath, moduleFile); var textAsset = AssetDatabase.LoadAssetAtPath <TextAsset>(modulePath); //Can't find through default path, try to search for the file using AssetDatabase if (textAsset == null) { var matches = AssetDatabase.FindAssets(string.Format("Module_{0} t:textasset", moduleName)); if (matches.Length > 0) { // Get the first result textAsset = AssetDatabase.LoadAssetAtPath <TextAsset>(AssetDatabase.GUIDToAssetPath(matches[0])); } else { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Can't find module using Unity's search system. Make sure that the file 'Module_{0}' is in the project!", moduleName))); } } if (textAsset == null) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Can't load module: '{0}'", moduleName))); return(null); } var lines = textAsset.text.Split(new string[] { "\r\n", "\n" }, System.StringSplitOptions.None); List <string> features = new List <string>(); List <string> propertiesNew = new List <string>(); List <string> keywords = new List <string>(); List <string> shaderFeaturesBlock = new List <string>(); List <string> propertiesBlock = new List <string>(); List <string> variables = new List <string>(); List <string> functions = new List <string>(); List <string> inputStruct = new List <string>(); bool explicitFunctions = false; Dictionary <string, List <Argument> > verticesArgs = new Dictionary <string, List <Argument> >(); Dictionary <string, List <Argument> > fragmentsArgs = new Dictionary <string, List <Argument> >(); Dictionary <string, List <string> > vertices = new Dictionary <string, List <string> >(); Dictionary <string, List <string> > fragments = new Dictionary <string, List <string> >(); Dictionary <string, List <string> > arbitraryBlocks = new Dictionary <string, List <string> >(); List <string> currentList = null; foreach (var line in lines) { if (line.StartsWith("#") && !line.Contains("_IMPL")) { var lineTrim = line.Trim(); //fragment can have arguments, so check the start of the line instead of exact word if (lineTrim.StartsWith("#VERTEX")) { var key = ""; if (lineTrim.Contains(":")) { int start = "#VERTEX:".Length; int end = lineTrim.IndexOf('('); key = lineTrim.Substring(start, end - start); } currentList = new List <string>(); vertices.Add(key, currentList); if (lineTrim.Contains("(") && lineTrim.Contains(")")) { //parse arguments var vertexArgs = ParseArguments(lineTrim); verticesArgs.Add(key, vertexArgs); } } //#LIGHTING is an alias for fragment here, just to differentiate in the template code else if (lineTrim.StartsWith("#FRAGMENT") || lineTrim.StartsWith("#LIGHTING")) { var key = ""; if (lineTrim.Contains(":")) { int start = "#FRAGMENT:".Length; // same character count for #LIGHTING int end = lineTrim.IndexOf('('); if (end >= 0) { key = lineTrim.Substring(start, end - start); } else { key = lineTrim.Substring(start); } } currentList = new List <string>(); fragments.Add(key, currentList); if (lineTrim.Contains("(") && lineTrim.Contains(")")) { //parse arguments var fragmentArgs = ParseArguments(lineTrim); fragmentsArgs.Add(key, fragmentArgs); } } else if (lineTrim.StartsWith("#FUNCTIONS:EXPLICIT")) { // Explicit functions that have to be declared in the template with [[Module:FUNCTIONS:module_name]] currentList = functions; explicitFunctions = true; } else { switch (lineTrim) { case "#FEATURES": currentList = features; break; case "#PROPERTIES_NEW": currentList = propertiesNew; break; case "#KEYWORDS": currentList = keywords; break; case "#PROPERTIES_BLOCK": currentList = propertiesBlock; break; case "#SHADER_FEATURES_BLOCK": currentList = shaderFeaturesBlock; break; case "#FUNCTIONS": currentList = functions; break; case "#VARIABLES": currentList = variables; break; case "#INPUT": currentList = inputStruct; break; case "#END": currentList = null; break; default: { // An "arbitrary block" is parsed if not using a predefine keyword like above, and we are not iterating over an existing block if (currentList == null) { string block = lineTrim.Substring(1); if (block.Length > 0 && !char.IsWhiteSpace(block[0])) { currentList = new List <string>(); arbitraryBlocks.Add(block, currentList); } } break; } } } } else { if (currentList != null) { currentList.Add(line); } } } Module module = new Module(); module.name = moduleName; module.Features = features.ToArray(); module.PropertiesNew = propertiesNew.ToArray(); module.Keywords = keywords.ToArray(); module.ShaderFeaturesBlock = shaderFeaturesBlock.ToArray(); module.PropertiesBlock = propertiesBlock.ToArray(); module.Functions = functions.ToArray(); module.Variables = variables.ToArray(); module.InputStruct = inputStruct.ToArray(); module.ExplicitFunctionsDeclaration = explicitFunctions; module.ArbitraryBlocks = arbitraryBlocks; // #VERTEX if (vertices.Count == 0) { vertices.Add("", new List <string>()); verticesArgs.Add("", new List <Argument>()); } foreach (var vertexPair in vertices) { var key = vertexPair.Key; module.Vertices.Add(key, vertexPair.Value.ToArray()); if (verticesArgs.ContainsKey(key)) { module.VerticesArgs.Add(key, verticesArgs[key].ToArray()); } } // #FRAGMENT if (fragments.Count == 0) { fragments.Add("", new List <string>()); fragmentsArgs.Add("", new List <Argument>()); } foreach (var fragmentPair in fragments) { var key = fragmentPair.Key; module.Fragments.Add(key, fragmentPair.Value.ToArray()); if (fragmentsArgs.ContainsKey(key)) { module.FragmentsArgs.Add(key, fragmentsArgs[key].ToArray()); } } module.ProcessIndentation(); return(module); }
//-------------------------------------------------------------------------------------------------- public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { _materialEditor = materialEditor; _properties = properties; hasAutoTransparency = System.Array.Exists(_properties, prop => prop.name == PROP_RENDERING_MODE); #if SHOW_DEFAULT_INSPECTOR base.OnGUI(); return; #else //Header EditorGUILayout.BeginHorizontal(); var label = (Screen.width > 450f) ? "TOONY COLORS PRO 2 - INSPECTOR (Generated Shader)" : (Screen.width > 300f ? "TOONY COLORS PRO 2 - INSPECTOR" : "TOONY COLORS PRO 2"); TCP2_GUI.HeaderBig(label); if (TCP2_GUI.Button(TCP2_GUI.CogIcon2, "SG2", "Open in Shader Generator")) { if (targetMaterial.shader != null) { ShaderGenerator2.OpenWithShader(targetMaterial.shader); } } EditorGUILayout.EndHorizontal(); TCP2_GUI.Separator(); //Iterate Shader properties materialEditor.serializedObject.Update(); var mShader = materialEditor.serializedObject.FindProperty("m_Shader"); toggledGroups.Clear(); // Auto-transparency if (hasAutoTransparency) { int indent = EditorGUI.indentLevel; EditorGUI.indentLevel++; { EditorGUILayout.BeginHorizontal(); { GUILayout.Space(15); GUILayout.Label(TCP2_GUI.TempContent("Transparency"), EditorStyles.boldLabel); } EditorGUILayout.EndHorizontal(); HandleRenderingMode(); } EditorGUI.indentLevel = indent; } if (materialEditor.isVisible && !mShader.hasMultipleDifferentValues && mShader.objectReferenceValue != null) { //Retina display fix EditorGUIUtility.labelWidth = Utils.ScreenWidthRetina - 120f; EditorGUIUtility.fieldWidth = 64f; EditorGUI.BeginChangeCheck(); EditorGUI.indentLevel++; foreach (var p in properties) { var visible = (toggledGroups.Count == 0 || toggledGroups.Peek()); //Hacky way to separate material inspector properties into foldout groups if (p.name.StartsWith("__BeginGroup")) { //Foldout if (visible) { GUILayout.Space(2f); Rect propertyRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight, EditorStyles.layerMaskField); propertyRect.x += 12; propertyRect.width -= 12; p.floatValue = EditorGUI.Foldout(propertyRect, p.floatValue > 0, p.displayName, true) ? 1 : 0; } EditorGUI.indentLevel++; toggledGroups.Push((p.floatValue > 0) && visible); } else if (p.name.StartsWith("__EndGroup")) { EditorGUI.indentLevel--; toggledGroups.Pop(); GUILayout.Space(2f); } else { //Draw regular property if (visible && (p.flags & (MaterialProperty.PropFlags.PerRendererData | MaterialProperty.PropFlags.HideInInspector)) == MaterialProperty.PropFlags.None) { _materialEditor.ShaderProperty(p, p.displayName); } } } EditorGUI.indentLevel--; if (EditorGUI.EndChangeCheck()) { materialEditor.PropertiesChanged(); } } #endif // !SHOW_DEFAULT_INSPECTOR #if UNITY_5_5_OR_NEWER TCP2_GUI.Separator(); materialEditor.RenderQueueField(); #endif #if UNITY_5_6_OR_NEWER materialEditor.EnableInstancingField(); #endif }
internal List <List <ShaderProperty> > FindUsedShaderPropertiesPerPass(ParsedLine[] parsedLines) { // Find used shader properties depending on the current pass, to extract used features per pass var shaderPropertiesPerPass = new List <List <ShaderProperty> >(); // Find available Generic Implementations based on the current features ShaderProperty.Imp_GenericFromTemplate.InitList(); int passIndex = -1; string program = "undefined"; for (var i = 0; i < parsedLines.Length; i++) { var line = parsedLines[i].line.Trim(); if (line.StartsWith("#PASS")) { passIndex++; shaderPropertiesPerPass.Add(new List <ShaderProperty>()); continue; } if (line.StartsWith("#VERTEX")) { program = "vertex"; continue; } if (line.StartsWith("#FRAGMENT")) { program = "fragment"; continue; } if (line.StartsWith("#LIGHTING")) { program = "lighting"; continue; } if (passIndex < 0) { continue; } // enabled generic implementation if (line.StartsWith("#ENABLE_IMPL")) { ShaderProperty.Imp_GenericFromTemplate.EnableFromLine(line, passIndex, program); continue; } // disabled generic implementation if (line.StartsWith("#DISABLE_IMPL")) { if (line.Contains("DISABLE_IMPL_ALL")) { ShaderProperty.Imp_GenericFromTemplate.DisableAll(); } else { ShaderProperty.Imp_GenericFromTemplate.DisableFromLine(line, passIndex, program); } continue; } var end = 0; while (line.IndexOf("[[", end) >= 0) { var start = line.IndexOf("[[", end); end = line.IndexOf("]]", end + 1); var tag = line.Substring(start + 2, end - start - 2); if (tag.StartsWith("VALUE:") || tag.StartsWith("SAMPLE_VALUE_SHADER_PROPERTY:")) { var propName = tag.Substring(tag.IndexOf(':') + 1); int argsStart = propName.IndexOf('('); if (argsStart > 0) { propName = propName.Substring(0, argsStart); } var sp = GetShaderPropertyByName(propName); if (sp != null) { //add to used Shader Properties for current parsed pass if (!shaderPropertiesPerPass[passIndex].Contains(sp)) { shaderPropertiesPerPass[passIndex].Add(sp); } ShaderProperty.Imp_GenericFromTemplate.AddCompatibleShaderProperty(sp); } else { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("No match for used Shader Property in code: '<b>{0}</b>'", tag))); } } } } ShaderProperty.Imp_GenericFromTemplate.ListCompleted(); // Iterate through properties, and take into account referenced ones Action <ShaderProperty, List <ShaderProperty> > findAndAddLinkedShaderProperties = null; findAndAddLinkedShaderProperties = (sp, list) => { foreach (var imp in sp.implementations) { var impSpRef = imp as ShaderProperty.Imp_ShaderPropertyReference; if (impSpRef != null) { // linked shader property can't be null during compilation, or something went wrong if (!list.Contains(impSpRef.LinkedShaderProperty)) { list.Add(impSpRef.LinkedShaderProperty); // recursive findAndAddLinkedShaderProperties(impSpRef.LinkedShaderProperty, list); } } } }; for (int i = 0; i < shaderPropertiesPerPass.Count; i++) { var list = shaderPropertiesPerPass[i]; foreach (var sp in list.ToArray()) { findAndAddLinkedShaderProperties(sp, list); } } return(shaderPropertiesPerPass); }
internal ShaderProperty[] GetConditionalShaderProperties(ParsedLine[] parsedLines, out Dictionary <int, GUIContent> headers) { headers = new Dictionary <int, GUIContent>(); var shaderPropertiesList = new List <ShaderProperty>(); for (var i = 0; i < parsedLines.Length; i++) { var line = parsedLines[i].line; if (line.StartsWith("#PROPERTIES_NEW")) { while (i < parsedLines.Length) { line = parsedLines[i].line; i++; if (line.StartsWith("#END")) { return(shaderPropertiesList.ToArray()); } if (line.StartsWith("//") || line.StartsWith("#") || string.IsNullOrEmpty(line)) { continue; } if (line.Trim().StartsWith("header")) { var data = line.Split(new string[] { "\t" }, System.StringSplitOptions.RemoveEmptyEntries); var gc = new GUIContent(data[1], data.Length > 2 ? data[2].Trim('\"') : null); if (!headers.ContainsKey(shaderPropertiesList.Count)) { headers.Add(shaderPropertiesList.Count, null); } headers[shaderPropertiesList.Count] = gc; // only take the last one into account, so that empty headers will be ignored continue; } try { var shaderProperty = ShaderProperty.CreateFromTemplateData(line); var match = GetShaderPropertyByName(shaderProperty.Name); if (match == null) { Debug.LogError(ShaderGenerator2.ErrorMsg("Can't find Shader Property in Template, yet it was found for Config")); } else { shaderPropertiesList.Add(match); } } catch (Exception e) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Parsing error in <b>#PROPERTIES_NEW</b> block:\n'{0}'\n{1}", e.Message, e.StackTrace))); } } } } return(shaderPropertiesList.ToArray()); }
//Get all Shader Properties regardless of conditions, only their visibility will be affected by the Config //This ensures that they are always in the correct order //Also link the pending Imp_ShaderPropertyReferences at this time, if any //and assign the correct pass bitmask based on usage static ShaderProperty[] GetShaderProperties(string[] lines, int i) { var shaderPropertiesList = new List <ShaderProperty>(); string subline; do { subline = lines[i]; i++; if (subline == "#END") { break; } if (subline.Trim().StartsWith("//") || subline.StartsWith("#") || string.IsNullOrEmpty(subline)) { continue; } if (subline.Trim().StartsWith("header")) { continue; } try { var shaderProperty = ShaderProperty.CreateFromTemplateData(subline); shaderPropertiesList.Add(shaderProperty); } catch (Exception e) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Parsing error in <b>#PROPERTIES_NEW</b> block:\n\nError: '{0}'\n\nLine: '{1}'", e.ToString(), subline))); } }while (subline != "#END" && subline != null); //link shader property references foreach (var shaderProperty in shaderPropertiesList) { if (shaderProperty.implementations != null && shaderProperty.implementations.Count > 0) { foreach (var imp in shaderProperty.implementations) { var impSpRef = imp as ShaderProperty.Imp_ShaderPropertyReference; if (impSpRef != null && !string.IsNullOrEmpty(impSpRef.LinkedShaderPropertyName)) { var match = shaderPropertiesList.Find(sp => sp.Name == impSpRef.LinkedShaderPropertyName); if (match != null) { var channels = impSpRef.Channels; impSpRef.LinkedShaderProperty = match; //restore channels from template data, it's up to the template to match the referenced shader property if (!string.IsNullOrEmpty(channels)) { impSpRef.Channels = channels.ToUpperInvariant(); } impSpRef.ForceUpdateParentDefaultHash(); } else { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Can't find referenced Shader Property in template.\n'{0}' tried to reference '{1}'", shaderProperty.Name, impSpRef.LinkedShaderPropertyName))); } } } } } //iterate rest of template to check usage of each shader property per pass int currentPass = -1; for (; i < lines.Length; i++) { var line = lines[i].Trim(); // update pass if (line.StartsWith("#PASS")) { currentPass++; continue; } // check value usage: used in which pass(es), and which generic implementation they can use var end = 0; while (line.IndexOf("[[", end) >= 0) { var start = line.IndexOf("[[", end); end = line.IndexOf("]]", end + 1); var tag = line.Substring(start + 2, end - start - 2); if (tag.StartsWith("VALUE:") || tag.StartsWith("SAMPLE_VALUE_SHADER_PROPERTY:")) { var propName = tag.Substring(tag.IndexOf(':') + 1); int argsStart = propName.IndexOf('('); if (argsStart > 0) { propName = propName.Substring(0, argsStart); } var sp = shaderPropertiesList.Find(x => x.Name == propName); if (sp != null) { // found used Shader Property sp.AddPassUsage(currentPass); } else { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("No match for used Shader Property in code: '<b>{0}</b>'", tag))); } } } } return(shaderPropertiesList.ToArray()); }
private void UpdateTemplateMeta() { uiFeatures = null; templateInfo = null; templateWarning = null; templateType = null; templateKeywords = null; id = null; UIFeature.ClearFoldoutStack(); if (textAsset != null && !string.IsNullOrEmpty(textAsset.text)) { //First pass: parse #MODULES and replace related keywords var newTemplateLines = new List <string>(); Dictionary <string, Module> modules = new Dictionary <string, Module>(); var usedModulesVariables = new HashSet <Module>(); var usedModulesInput = new HashSet <Module>(); for (int i = 0; i < originalTextLines.Length; i++) { string line = originalTextLines[i]; //Parse #MODULES if (line.StartsWith("#MODULES")) { //Iterate module names and try to find matching TextAssets while (line != "#END" && i < originalTextLines.Length) { line = originalTextLines[i]; i++; if (line == "#END") { break; } if (line.StartsWith("//") || line.StartsWith("#") || string.IsNullOrEmpty(line)) { continue; } try { var moduleName = line.Trim(); var module = Module.CreateFromName(moduleName); if (module != null) { modules.Add(moduleName, module); } } catch (Exception e) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Parsing error in <b>#MODULES</b> block:\nLine: '{0}'\n'{1}'\n{2}", line, e.Message, e.StackTrace))); } } } //Replace module keywords if (line.Trim().StartsWith("[[MODULE") && i < originalTextLines.Length) { //extract indentation var indent = ""; foreach (var c in line) { if (char.IsWhiteSpace(c)) { indent += c; } else { break; } } var start = line.IndexOf("[[MODULE:"); var end = line.LastIndexOf("]]"); var tag = line.Substring(start + "[[MODULE:".Length, end - start - "[[MODULE:".Length); var moduleName = ""; var key = ""; if (tag.IndexOf(':') > 0) { moduleName = tag.Substring(tag.IndexOf(':') + 1); //remove arguments if any if (moduleName.Contains("(")) { moduleName = moduleName.Substring(0, moduleName.IndexOf("(")); } //extract key, if any int keyStart = moduleName.IndexOf(':'); if (keyStart > 0) { key = moduleName.Substring(keyStart + 1); moduleName = moduleName.Substring(0, keyStart); } } if (!string.IsNullOrEmpty(moduleName) && !modules.ContainsKey(moduleName)) { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Can't find module: '{0}' for '{1}'", moduleName, line.Trim()))); continue; } if (tag.StartsWith("INPUT:")) { //Print Input block from specific module foreach (var module in modules.Values) { if (module.name == moduleName) { AddRangeWithIndent(newTemplateLines, module.InputStruct, indent); usedModulesInput.Add(module); } } } if (tag == "INPUT") { //Print all Input lines from all modules foreach (var module in modules.Values) { if (!usedModulesInput.Contains(module)) { AddRangeWithIndent(newTemplateLines, module.InputStruct, indent); } } } else if (tag.StartsWith("VARIABLES:")) { //Print Variables line from specific module foreach (var module in modules.Values) { if (module.name == moduleName) { AddRangeWithIndent(newTemplateLines, module.Variables, indent); usedModulesVariables.Add(module); } } } else if (tag == "VARIABLES") { //Print all Variables lines from all modules foreach (var module in modules.Values) { if (!usedModulesVariables.Contains(module)) { AddRangeWithIndent(newTemplateLines, module.Variables, indent); } } } else if (tag == "KEYWORDS") { //Print all Keywords lines from all modules foreach (var module in modules.Values) { AddRangeWithIndent(newTemplateLines, module.Keywords, indent); } } else if (tag.StartsWith("FEATURES:")) { AddRangeWithIndent(newTemplateLines, modules[moduleName].Features, indent); } else if (tag.StartsWith("PROPERTIES_NEW:")) { AddRangeWithIndent(newTemplateLines, modules[moduleName].PropertiesNew, indent); } else if (tag.StartsWith("PROPERTIES_BLOCK:")) { AddRangeWithIndent(newTemplateLines, modules[moduleName].PropertiesBlock, indent); } else if (tag.StartsWith("SHADER_FEATURES_BLOCK")) { AddRangeWithIndent(newTemplateLines, modules[moduleName].ShaderFeaturesBlock, indent); } else if (tag.StartsWith("VERTEX:")) { //Get arguments if any var args = new List <string>(); int argStart = tag.IndexOf("(") + 1; int argEnd = tag.IndexOf(")"); if (argStart > 0 && argEnd > 0) { string arguments = tag.Substring(argStart, argEnd - argStart); var argumentsSplit = arguments.Split(','); foreach (var a in argumentsSplit) { args.Add(a.Trim()); } } AddRangeWithIndent(newTemplateLines, modules[moduleName].VertexLines(args, key), indent); } else if (tag.StartsWith("FRAGMENT:")) { //Get arguments if any var args = new List <string>(); int argStart = tag.IndexOf("(") + 1; int argEnd = tag.IndexOf(")"); if (argStart > 0 && argEnd > 0) { string arguments = tag.Substring(argStart, argEnd - argStart); var argumentsSplit = arguments.Split(','); foreach (var a in argumentsSplit) { args.Add(a.Trim()); } } AddRangeWithIndent(newTemplateLines, modules[moduleName].FragmentLines(args, key), indent); } } else { newTemplateLines.Add(line); } } //Apply to textLines this.textLines = newTemplateLines.ToArray(); //Second pass: parse other blocks for (int i = 0; i < textLines.Length; i++) { var line = textLines[i]; if (line.StartsWith("#INFO=")) { templateInfo = line.Substring("#INFO=".Length).TrimEnd().Replace(" ", "\n"); } else if (line.StartsWith("#WARNING=")) { templateWarning = line.Substring("#WARNING=".Length).TrimEnd().Replace(" ", "\n"); } else if (line.StartsWith("#CONFIG=")) { templateType = line.Substring("#CONFIG=".Length).TrimEnd().ToLower(); } else if (line.StartsWith("#TEMPLATE_KEYWORDS=")) { templateKeywords = line.Substring("#TEMPLATE_KEYWORDS=".Length).TrimEnd().Split(','); } else if (line.StartsWith("#ID=")) { id = line.Substring("#ID=".Length).TrimEnd(); } else if (line.StartsWith("#FEATURES")) { uiFeatures = UIFeature.GetUIFeatures(textLines, ref i, this); } else if (line.StartsWith("#PROPERTIES_NEW")) { shaderProperties = GetShaderProperties(textLines, i); return; } //Config meta should appear before the Shader name line else if (line.StartsWith("Shader")) { return; } } if (id == null) { Debug.LogWarning(ShaderGenerator2.ErrorMsg("Missing ID in template metadata!")); } } }
//Returns an array of parsed lines based on the current features enabled, with their corresponding original line number (for error reporting) //Only keeps the lines necessary to generate the shader source, e.g. #FEATURES will be skipped //Conditions are now only processed in this function, all the other code should ignore them internal ParsedLine[] GetParsedLinesFromConditions(Config config, List <string> flags) { var list = new List <ParsedLine>(); int depth = -1; var stack = new List <bool>(); var done = new List <bool>(); var features = new List <string>(config.Features); int passIndex = -1; //clear optional features from shader properties options config.ClearShaderPropertiesFeatures(); //make sure to use all needed features as config features for conditions var conditionFeatures = new List <string>(config.GetShaderPropertiesNeededFeaturesAll()); conditionFeatures.AddRange(config.Features); conditionFeatures.AddRange(config.ExtraTempFeatures); //make sure keywords have been processed var keywordsFeatures = new List <string>(); ProcessKeywordsBlock(config, conditionFeatures, keywordsFeatures, flags); features.AddRange(keywordsFeatures); //before first #PASS tag: use needed features from _all_ passes: //this is to make sure that the CGINCLUDE block with needed #VARIABLES:MODULES gets processed correctly features.AddRange(config.GetShaderPropertiesNeededFeaturesAll()); features.AddRange(config.GetHooksNeededFeatures()); //parse lines and strip based on conditions for (var i = 0; i < textLines.Length; i++) { var line = textLines[i]; if (line.Length > 0 && line[0] == '#') { if (line.StartsWith("#PASS")) { //new pass: get the specific features for this pass passIndex++; features = new List <string>(config.Features); features.AddRange(config.GetHooksNeededFeatures()); features.AddRange(config.GetShaderPropertiesNeededFeaturesForPass(passIndex)); var passKeywordsFeatures = new List <string>(); ProcessKeywordsBlock(config, features, passKeywordsFeatures, flags); features.AddRange(passKeywordsFeatures); } //Skip #FEATURES block if (line.StartsWith("#FEATURES")) { while (i < textLines.Length) { i++; line = textLines[i]; if (line == "#END") { break; } } } } //Conditions if (line.Contains("///")) { var error = ExpressionParser.ProcessCondition(line, features, ref depth, ref stack, ref done); if (!string.IsNullOrEmpty(error)) { Debug.LogError(ShaderGenerator2.ErrorMsg(error + "\n@ line " + i)); } } //Regular line else { //Append line if inside valid condition block if ((depth >= 0 && stack[depth]) || depth < 0) { list.Add(new ParsedLine { line = line, lineNumber = i + 1 }); } } } //error? if (depth >= 0) { //Analyze and try to find where the issue is var st = new Stack <ParsedLine>(); for (var i = 0; i < textLines.Length; i++) { var tline = textLines[i].TrimStart(); if (tline == "///") { st.Pop(); } else if (tline.StartsWith("/// IF")) { st.Push(new ParsedLine { line = textLines[i], lineNumber = i + 1 }); } } if (st.Count > 0) { var pl = st.Pop(); Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Missing {0} ending '///' tag{1} at line {2}:\n{3}", depth + 1, depth > 0 ? "s" : "", pl.lineNumber, pl.line))); } else { Debug.LogError(ShaderGenerator2.ErrorMsg(string.Format("Missing {0} ending '///' tag{1}", depth + 1, depth > 0 ? "s" : ""))); } } return(list.ToArray()); }
protected override void DrawGUI(Rect position, Config config) { // Fetch embedded Shader Property bool highlighted = Highlighted(config); if (shaderProperty == null && highlighted) // the SP only exists if the feature is enabled { var match = Array.Find(config.AllShaderProperties, sp => sp.Name == shaderPropertyName); if (match == null) { Debug.LogError(ShaderGenerator2.ErrorMsg("Can't find matching embedded Shader Property with name: '" + shaderPropertyName + "'")); } shaderProperty = match; } int feature = highlighted ? (shaderProperty.implementations[0] as ShaderProperty.Imp_Enum).ValueType + 1 : 0; if (feature < 0) { feature = 0; } EditorGUI.BeginChangeCheck(); feature = EditorGUI.Popup(position, feature, labels); if (EditorGUI.EndChangeCheck()) { config.ToggleFeature(keyword, feature > 0); // Update Fixed Function value type var ffv = fixedFunctionValues[feature]; if (feature > 0 && !string.IsNullOrEmpty(ffv) && shaderProperty != null) { (shaderProperty.implementations[0] as ShaderProperty.Imp_Enum).SetValueTypeFromString(ffv); shaderProperty.CheckHash(); shaderProperty.CheckErrors(); } } // Show embedded Shader Property UI if (highlighted && shaderProperty != null) { if (shaderProperty.Type != ShaderProperty.VariableType.fixed_function_enum) { EditorGUILayout.HelpBox("Embedded Shader Property should be a Fixed Function enum.", MessageType.Error); } else { var imp = shaderProperty.implementations[0] as ShaderProperty.Imp_Enum; if (imp == null) { EditorGUILayout.HelpBox("First implementation of enum Shader Property isn't an Imp_Enum.", MessageType.Error); } else { EditorGUI.BeginChangeCheck(); { imp.EmbeddedGUI(28, 170); } if (EditorGUI.EndChangeCheck()) { shaderProperty.CheckHash(); shaderProperty.CheckErrors(); } } } } }