private static string[] LoadFractalSource(IFractalType[] fractalTypes) { // load fractal templates List <string> fractalSource = new List <string>(); foreach (IFractalType fractal in fractalTypes) { FractalTypeDescriptor desc = fractal.GetDescription(); string path = desc.templatePath; string source = null; if (!File.Exists(path)) { Debug.LogError("NoiseLib: Fractal Template File at \"" + path + "\" could not be found! Skipping generation of: " + desc.name); } else { using (StreamReader sr = new StreamReader(path)) { source = sr.ReadToEnd(); } } fractalSource.Add(source); } return(fractalSource.ToArray()); }
private static string GetShaderName(ShaderGeneratorDescriptor generatorDesc, FractalTypeDescriptor fractalDesc) { string shaderStr = string.Format("{0}/{1}{2}", generatorDesc.shaderCategory, generatorDesc.name, fractalDesc.name); return(shaderStr); }
private static void GatherFractalTypes() { List <IFractalType> instances = new List <IFractalType>(); List <string> names = new List <string>(); List <Type> types = new List <Type>(); foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { Type[] assemblyTypes = null; try { assemblyTypes = asm.GetTypes(); } catch (Exception) { Debug.Log("NoiseLib::GatherFractalTypes: Failed to get types from assembly: " + asm); assemblyTypes = null; } if (assemblyTypes != null) { types.AddRange(GetSubclassesOfGenericType(assemblyTypes, typeof(FractalType <>))); } } foreach (Type t in types) { PropertyInfo propertyInfo = t.GetProperty("instance", s_bindingFlags); MethodInfo methodInfo = propertyInfo.GetGetMethod(); IFractalType instance = (IFractalType)methodInfo.Invoke(null, null); FractalTypeDescriptor desc = instance.GetDescription(); if (string.IsNullOrEmpty(desc.name)) { Debug.LogError("FractalType name cannot be null or empty! Skipping fractal type: " + desc.name); continue; } instances.Add(instance); names.Add(desc.name); } s_fractalTypes = instances.ToArray(); s_fractalNames = names.ToArray(); }
public GeneratedShaderInfo(IFractalType fractalType, INoiseType noiseType) { this.fractalDesc = fractalType.GetDescription(); this.noiseDesc = noiseType.GetDescription(); this.noiseIncludeStr = string.Format("#include \"{0}\"", noiseDesc.sourcePath); if (!string.IsNullOrEmpty(fractalDesc.name)) { this.variantName = string.Format("{0}{1}", fractalDesc.name, noiseDesc.name); } else { this.variantName = noiseDesc.name; } // set the path of the generated file. this will be used when writing the file // to disk and when adding the include in any generated shaders that use this // fractal and noise type variant this.generatedIncludePath = string.Format("{0}/{1}/{2}.hlsl", noiseDesc.outputDir, fractalDesc.name, noiseDesc.name); this.outputDir = string.Format("{0}/{1}", noiseDesc.outputDir, fractalDesc.name); fractalStructName = string.Format("{0}FractalInput", fractalDesc.name); noiseStructName = string.Format("{0}NoiseInput", noiseDesc.name); numFractalInputs = fractalDesc.inputStructDefinition == null ? 0 : fractalDesc.inputStructDefinition.Count; numNoiseInputs = noiseDesc.inputStructDefinition == null ? 0 : noiseDesc.inputStructDefinition.Count; fractalParamStr = null; noiseParamStr = null; functionInputStr = ""; // construct include paths string additionalIncludePaths = "\n"; for (int i = 0; i < fractalDesc.additionalIncludePaths.Count; ++i) { additionalIncludePaths += $"#include \"{ fractalDesc.additionalIncludePaths[ i ] }\"\n"; } additionalIncludePaths += "\n"; // generate the string for the fractal type structure as it would appear as a parameter // in an HLSL function declaration if (numFractalInputs > 0) { fractalParamStr = string.Format("{0} {1}", fractalStructName, "fractalInput"); } // generate the string for the noise type structure as it would appear as a parameter // in an HLSL function declaration if (numNoiseInputs > 0) { noiseParamStr = string.Format("{0} {1}", noiseStructName, "noiseInput"); } // generate the argument string for an HLSL function declaration that would be // using this combination of noise and fractal type structure definitions functionParamStr = ""; if (fractalParamStr != null) { functionParamStr += fractalParamStr; functionInputStr += "fractalInput"; } if (fractalParamStr != null && noiseParamStr != null) { functionParamStr += ", "; functionInputStr += ", "; } if (noiseParamStr != null) { functionParamStr += noiseParamStr; functionInputStr += "noiseInput"; } fractalStructDef = ""; if (numFractalInputs > 0) { fractalStructDef = NoiseLib.BuildStructString(fractalStructName, fractalDesc.inputStructDefinition); string getDefaultFuncStr = NoiseLib.GetDefaultFunctionString(fractalStructName, fractalDesc.inputStructDefinition); fractalStructDef += $"\n\n{ getDefaultFuncStr }\n\n"; } noiseStructDef = ""; if (numNoiseInputs > 0) { noiseStructDef = NoiseLib.BuildStructString(noiseStructName, noiseDesc.inputStructDefinition); } // get input str construction getInputsStr = ""; getFractalInputStr = NoiseLib.GetInputFunctionCallString(fractalStructName); getNoiseInputStr = NoiseLib.GetInputFunctionCallString(fractalStructName); if (numFractalInputs > 0) { getInputsStr += getFractalInputStr; } if (numFractalInputs > 0 && numNoiseInputs > 0) { getInputsStr += ", "; } if (numNoiseInputs > 0) { getInputsStr += getNoiseInputStr; } // get default input str construction getDefaultInputsStr = ""; getDefaultFractalInputStr = NoiseLib.GetDefaultInputFunctionCallString(fractalStructName); getDefaultNoiseInputStr = NoiseLib.GetDefaultInputFunctionCallString(noiseStructName); if (numFractalInputs > 0) { getDefaultInputsStr += getDefaultFractalInputStr; } if (numFractalInputs > 0 && numNoiseInputs > 0) { getDefaultInputsStr += ", "; } if (numNoiseInputs > 0) { getDefaultInputsStr += getDefaultNoiseInputStr; } fractalPropertyDefStr = ""; if (fractalDesc.inputStructDefinition != null && fractalDesc.inputStructDefinition.Count > 0) { fractalPropertyDefStr = NoiseLib.GetPropertyDefinitionStr(fractalDesc.name, fractalDesc.inputStructDefinition); fractalPropertyDefStr += "\n" + NoiseLib.GetPropertyFunctionStr(fractalStructName, fractalDesc.name, fractalDesc.inputStructDefinition); } }
/// <summary> /// Forces the generation of any shaders that make use of generated noise header files. Gathers all /// the NoiseShaderGenerators and generates shaders based on the ".noisehlsltemplate" file /// provided by that particular NoiseShaderGenerator implementation /// </summary> public static void GenerateShaders() { System.Globalization.CultureInfo prevCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture; System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; GatherGenerators(); IFractalType[] fractalTypes = s_fractalTypes; INoiseType[] noiseTypes = s_noiseTypes; Dictionary <Type, INoiseShaderGenerator> generators = s_generators; StringBuilder shaderSB = new StringBuilder(); StringBuilder passesSB = new StringBuilder(); foreach (KeyValuePair <Type, INoiseShaderGenerator> pair in generators) { shaderSB.Clear(); passesSB.Clear(); string shaderTemplateStr = null; INoiseShaderGenerator generator = pair.Value; ShaderGeneratorDescriptor generatorDesc = generator.GetDescription(); if (!File.Exists(generatorDesc.templatePath)) { Debug.LogError("Could not find specified template file for noise shader generator: " + generator); continue; } // load contents of shader template using (StreamReader sr = new StreamReader(generatorDesc.templatePath)) { shaderTemplateStr = sr.ReadToEnd(); } // find the pass template using regex matching Match passTemplateMatch = Regex.Match(shaderTemplateStr, Strings.k_regexPassTemplate1); if (!passTemplateMatch.Success) { Debug.LogError($"Could not find pass template in {generatorDesc.templatePath}. Skipping noise shader generation for this generator type!!"); continue; } string passTemplateStr = passTemplateMatch.Value; // generate shaders for each fractal type foreach (IFractalType fractal in fractalTypes) { FractalTypeDescriptor fractalDesc = fractal.GetDescription(); string fullShaderCategory = GetShaderName(generatorDesc, fractalDesc); shaderSB.Append(NoiseLib.Strings.k_warningHeader); shaderSB.Append(shaderTemplateStr); shaderSB.Replace(NoiseLib.Strings.k_tagShaderCategory, $"\"{fullShaderCategory}\""); // add passes for each noise type foreach (INoiseType noise in noiseTypes) { GeneratedShaderInfo info = new GeneratedShaderInfo(fractal, noise); // add to passes string builer passesSB.Append(passTemplateStr); passesSB.AppendLine(); passesSB.Replace(NoiseLib.Strings.k_tagIncludes, string.Format("#include \"{0}\"", info.generatedIncludePath)); info.ReplaceTags(passesSB); } // replace template with generated passes string newContents = Regex.Replace(shaderSB.ToString(), Strings.k_regexPassTemplate2, passesSB.ToString()); newContents = newContents.Replace(NoiseLib.Strings.k_tagFractalName, fractalDesc.name); // load shader contents from disk if it exists string fileName = string.Format("{0}{1}.shader", generatorDesc.name, fractalDesc.name); string filePath = string.Format("{0}/{1}", generatorDesc.outputDir, fileName); if (!Directory.Exists(generatorDesc.outputDir)) { Directory.CreateDirectory(generatorDesc.outputDir); } string currentContents = null; FileInfo fi = new FileInfo(filePath); if (File.Exists(filePath)) { using (StreamReader sr = new StreamReader(filePath)) { currentContents = sr.ReadToEnd(); currentContents = NormalizeLineEndings(currentContents); } } // do some code cleanup newContents = Regex.Replace(newContents, NoiseLib.Strings.k_regexDupCommas, ", "); newContents = Regex.Replace(newContents, NoiseLib.Strings.k_emptyArgsRight, " )"); newContents = Regex.Replace(newContents, NoiseLib.Strings.k_emptyArgsLeft, "( "); newContents = NormalizeLineEndings(newContents); // only write to file if it is not read-only, ie. if it is one of the generated // shader files that we ship with the TerrainTools package if (!fi.IsReadOnly) { if (currentContents == null || currentContents.CompareTo(newContents) != 0) { try { using (StreamWriter sw = new StreamWriter(filePath)) { sw.Write(newContents); } } catch (Exception) { // restore previous cultureinfo System.Threading.Thread.CurrentThread.CurrentCulture = prevCultureInfo; } } } shaderSB.Clear(); passesSB.Clear(); } } // restore previous cultureinfo System.Threading.Thread.CurrentThread.CurrentCulture = prevCultureInfo; // UnityEditor.AssetDatabase.Refresh(); }