Files
2026-02-23 22:01:07 +07:00

265 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace AllIn13DShader
{
public static class ShaderVariantCreator
{
private const string INCLUDE_LINE_FORMAT = @"#include ""{0}""";
private const string TAG_VARIANT_NAME = @"<VARIANT_NAME>";
private const string TAG_SHADER_PROPERTIES = @"<SHADER_PROPERTIES>";
private const string TAG_SHADER_PASSES_URP = @"<SHADER_PASSES_URP>";
private const string TAG_SHADER_PASSES_BIRP = @"<SHADER_PASSES_BIRP>";
//
private const string TAG_PASS_NAME = @"<PASS_NAME>";
private const string TAG_LIGHT_MODE = @"<LIGHT_MODE>";
private const string TAG_BLEND_COMMAND = @"<BLEND_COMMAND>";
private const string TAG_CULL_COMMAND = @"<CULL_COMMAND>";
private const string TAG_Z_WRITE_COMMAND = @"<Z_WRITE_COMMAND>";
private const string TAG_Z_TEST_COMMAND = @"<Z_TEST_COMMAND>";
private const string TAG_COLOR_MASK_COMMAND = @"<COLOR_MASK_COMMAND>";
private const string TAG_STENCIL_BLOCK = @"<STENCIL_BLOCK>";
private const string TAG_FEATURES_URP_DEFINES = @"<FEATURES_URP_DEFINES>";
private const string TAG_FEATURES_URP_LIBRARY = @"<FEATURES_URP_LIBRARY>";
private const string TAG_VERTEX_PROGRAM_NAME = @"<VERTEX_PROGRAM_NAME>";
private const string TAG_FRAGMENT_PROGRAM_NAME = @"<FRAGMENT_PROGRAM_NAME>";
private const string TAG_ALL_IN_1_FEATURES = @"<ALLIN1_FEATURES>";
private const string TAG_PIPELINE_PASS_SYMBOL = @"<PIPELINE_PASS_SYMBOL>";
private const string TAG_THIS_PASS_SYMBOL = @"<THIS_PASS_SYMBOL>";
private const string TAG_INCLUDE_PIPELINE_HELPER = @"<INCLUDE_PIPELINE_HELPER>";
private const string TAG_INCLUDE_EFFECT_LIBRARIES = @"<INCLUDE_EFFECT_LIBRARIES>";
private const string TAG_INCLUDE_PASS = @"<INCLUDE_PASS>";
private const string TAG_EXTRA_PRAGMA_LINES = @"<EXTRA_PRAGMA_LINES>";
private const string TAG_INCLUDE_ALL_IN_SHADER_FEATURES = @"<INCLUDE_ALL_IN_1_SHADER_FEATURES>";
private const string TAG_INCLUDE_ALL_IN_1_SHADER_LIGHT = @"<INCLUDE_ALL_IN_1_SHADER_LIGHT>";
private const string TAG_INCLUDE_CORE_LIBRARY = @"<INCLUDE_CORE_LIBRARY>";
private const string TAG_INCLUDE_COMMON_FUNCTIONS = @"<INCLUDE_COMMON_FUNCTIONS>";
private const string TAG_INCLUDE_COMMON_STRUCTS = @"<INCLUDE_COMMON_STRUCTS>";
//
private static string TEMPLATE_PATH = GlobalConfiguration.GetRootPluginFolderPath() + "/Editor/Effects Profiles/Templates/ShaderTemplate.allin1template";
private static string SHADER_PROPERTIES_PATH = GlobalConfiguration.GetRootPluginFolderPath() + "/Editor/Templates/AllIn13DShader_ShaderProperties.allIn13DData";
private static string SHADER_PASS_URP_TAMPLATE_PATH = GlobalConfiguration.GetRootPluginFolderPath() + "/Editor/Effects Profiles/Templates/PassTemplate_URP.allin1template";
private static string SHADER_PASS_BIRP_TAMPLATE_PATH = GlobalConfiguration.GetRootPluginFolderPath() + "/Editor/Effects Profiles/Templates/PassTemplate_BIRP.allin1template";
public static string SHADER_VARIANTS_FOLDER_NAME = "Baked Variants";
private static string SHADER_VARIANT_FILE_NAME = "AllIn13D_BakedEffects_{0}.shader";
public static Shader CreateVariantByMaterialInfo(PropertiesConfig propertiesConfig, AbstractMaterialInfo matInfo, string profileName)
{
EffectsProfileCollection effectsProfileCollection = GlobalConfiguration.instance.effectsProfileCollection;
EffectsProfile effectsProfile = effectsProfileCollection.CreateNewProfile(profileName);
effectsProfile.InitFromOtherProfile(effectsProfileCollection.generalProfile);
effectsProfileCollection.ConfigureEffectProfileByMaterialInfo(
target: effectsProfile,
activeEffectsList: effectsProfileCollection.generalProfile,
matInfo: matInfo,
propertiesConfig: propertiesConfig);
string folderPath = GlobalConfiguration.instance.BakedShaderSavePath;
Shader res = CreateVariant(effectsProfile, effectsProfileCollection, folderPath, false);
return res;
}
public static Shader CreateVariant(EffectsProfileCollection effectsProfileCollection, string profileName)
{
EffectsProfile effectProfile = effectsProfileCollection.CreateNewProfile(profileName);
effectProfile.InitFromOtherProfile(effectsProfileCollection.generalProfile);
string folderPath = GlobalConfiguration.instance.BakedShaderSavePath;
Shader res = CreateVariant(effectProfile, effectsProfileCollection, folderPath, false);
return res;
}
public static Shader CreateVariant(EffectsProfile effectsProfile, EffectsProfileCollection effectsProfileCollection, string folderPath, bool overwrite)
{
string txtShaderVariant = File.ReadAllText(TEMPLATE_PATH);
txtShaderVariant = ConfigureVariantName(txtShaderVariant, effectsProfile.profileName);
txtShaderVariant = ConfigureShaderProperties(txtShaderVariant);
ShaderPassCollection shaderPassCollection = GlobalConfiguration.instance.shaderPassCollection;
string urpPassesEntry = GetShaderPassesEntry(effectsProfile, shaderPassCollection, RenderPipelineEnum.URP, folderPath);
txtShaderVariant = txtShaderVariant.Replace(TAG_SHADER_PASSES_URP, urpPassesEntry);
string birpPassesEntry = GetShaderPassesEntry(effectsProfile, shaderPassCollection, RenderPipelineEnum.BIRP, folderPath);
txtShaderVariant = txtShaderVariant.Replace(TAG_SHADER_PASSES_BIRP, birpPassesEntry);
string filePath;
if (overwrite)
{
Shader shader = effectsProfile.FindShader();
filePath = AssetDatabase.GetAssetPath(shader);
}
else
{
string fileName = string.Format(SHADER_VARIANT_FILE_NAME, effectsProfile.profileName);
filePath = Path.Combine(folderPath, fileName);
filePath = AssetDatabase.GenerateUniqueAssetPath(filePath);
}
effectsProfile.shaderGUID = AssetDatabase.AssetPathToGUID(filePath);
txtShaderVariant = EditorUtils.UnifyEOL(txtShaderVariant);
SaveFile(filePath, txtShaderVariant);
AssetDatabase.Refresh();
Shader res = AssetDatabase.LoadAssetAtPath(filePath, typeof(Shader)) as Shader;
EditorUtility.SetDirty(effectsProfileCollection);
return res;
}
private static void SaveFile(string filePath, string txtFile)
{
File.WriteAllText(filePath, txtFile);
}
private static string ConfigureVariantName(string input, string variantName)
{
string res = input.Replace(TAG_VARIANT_NAME, variantName);
return res;
}
private static string ConfigureShaderProperties(string input)
{
string res = input;
string shaderPropertiesTxt = EditorUtils.ReadFileTextWithTabs(SHADER_PROPERTIES_PATH, 2);
res = res.Replace(TAG_SHADER_PROPERTIES, shaderPropertiesTxt);
return res;
}
private static string GetShaderPassesEntry(EffectsProfile effectsProfile, ShaderPassCollection shaderPassCollection, RenderPipelineEnum renderPipeline, string shaderFolderPath)
{
string shaderPassTemplateTxt = EditorUtils.ReadFileTextWithTabs(GetTemplatePathByRenderPipeline(renderPipeline), 2);
List<ShaderPassConfig> shaderPasses = effectsProfile.GetShaderPasses(shaderPassCollection, renderPipeline);
bool hasStencilBlock = false;
for(int i = 0; i < shaderPasses.Count; i++)
{
if (shaderPasses[i].passType == AllIn13DPassType.OUTLINE)
{
hasStencilBlock = true;
break;
}
}
string res = string.Empty;
for (int i = 0; i < shaderPasses.Count; i++)
{
string currentPass = shaderPassTemplateTxt;
currentPass = ConfigureShaderPass(effectsProfile, currentPass, shaderPasses[i], renderPipeline, hasStencilBlock, shaderFolderPath);
res += currentPass;
res += "\n";
}
return res;
}
private static string ConfigureShaderPass(EffectsProfile effectsProfile,
string templatePass, ShaderPassConfig shaderPass, RenderPipelineEnum renderPipeline, bool hasStencilBlock,
string shaderFolderPath)
{
string res = templatePass;
res = res.Replace(TAG_PASS_NAME, shaderPass.GetPassName(renderPipeline));
res = res.Replace(TAG_LIGHT_MODE, shaderPass.GetLightModeShaderEntry(renderPipeline));
res = res.Replace(TAG_BLEND_COMMAND, shaderPass.blendCommand.GetShaderEntry());
res = res.Replace(TAG_CULL_COMMAND, shaderPass.cullCommand.GetShaderEntry());
res = res.Replace(TAG_Z_WRITE_COMMAND, shaderPass.zWriteCommand.GetShaderEntry());
res = res.Replace(TAG_Z_TEST_COMMAND, shaderPass.zTestCommand.GetShaderEntry());
res = res.Replace(TAG_COLOR_MASK_COMMAND, shaderPass.colorMaskCommand.GetShaderEntry());
if (hasStencilBlock)
{
res = res.Replace(TAG_STENCIL_BLOCK, shaderPass.GetStencilBlockShaderEntry(effectsProfile));
}
else
{
res = res.Replace(TAG_STENCIL_BLOCK, string.Empty);
}
res = res.Replace(TAG_FEATURES_URP_DEFINES, shaderPass.GetPipelineFeaturesDefinesShaderEntry(renderPipeline, shaderFolderPath));
res = res.Replace(TAG_FEATURES_URP_LIBRARY, shaderPass.GetPipelineFeaturesLibraryShaderEntry(renderPipeline, shaderFolderPath));
res = res.Replace(TAG_VERTEX_PROGRAM_NAME, shaderPass.vertexProgramName);
res = res.Replace(TAG_FRAGMENT_PROGRAM_NAME, shaderPass.fragmentProgramName);
res = res.Replace(TAG_INCLUDE_PIPELINE_HELPER, string.Format(INCLUDE_LINE_FORMAT, shaderPass.GetHelperLibraryPath(renderPipeline, shaderFolderPath)));
string effectsLibraries = shaderPass.GetEffectsLibrariesShaderEntry(effectsProfile, shaderFolderPath);
res = res.Replace(TAG_INCLUDE_EFFECT_LIBRARIES, effectsLibraries);
res = res.Replace(TAG_INCLUDE_PASS, string.Format(INCLUDE_LINE_FORMAT, shaderPass.GetPassFilePath(shaderFolderPath)));
List<EffectsProfileEntry> enabledEntries = effectsProfile.GetEnabledEntriesFlatList();
string entryFeaturesTxt = ShaderPassConfig.GetAllIn1FeaturesEntries(enabledEntries);
res = res.Replace(TAG_ALL_IN_1_FEATURES, entryFeaturesTxt);
res = res.Replace(TAG_PIPELINE_PASS_SYMBOL, shaderPass.GetPipelinePassSymbolShaderEntry(renderPipeline));
res = res.Replace(TAG_THIS_PASS_SYMBOL, shaderPass.GetPassSymbolShaderEntry());
res = res.Replace(TAG_EXTRA_PRAGMA_LINES, shaderPass.GetExtraPragmaLines(renderPipeline));
res = res.Replace(TAG_INCLUDE_ALL_IN_SHADER_FEATURES, shaderPass.GetShaderFeaturesLibraryShaderEntry(shaderFolderPath));
res = res.Replace(TAG_INCLUDE_ALL_IN_1_SHADER_LIGHT, shaderPass.GetLightLibraryShaderEntry(shaderFolderPath));
res = res.Replace(TAG_INCLUDE_CORE_LIBRARY, shaderPass.GetCoreLibraryShaderEntry(shaderFolderPath));
res = res.Replace(TAG_INCLUDE_COMMON_STRUCTS, shaderPass.GetCommonStructsShaderEntry(shaderFolderPath));
res = res.Replace(TAG_INCLUDE_COMMON_FUNCTIONS, shaderPass.GetCommonFunctionsShaderEntry(shaderFolderPath));
return res;
}
private static string GetTemplatePathByRenderPipeline(RenderPipelineEnum renderPipeline)
{
string res = string.Empty;
switch (renderPipeline)
{
case RenderPipelineEnum.BIRP:
res = SHADER_PASS_BIRP_TAMPLATE_PATH;
break;
case RenderPipelineEnum.URP:
res = SHADER_PASS_URP_TAMPLATE_PATH;
break;
}
return res;
}
public static string GetShaderVariantsFolderPath()
{
string res = GlobalConfiguration.instance.BakedShaderSavePath;
return res;
}
}
}