using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; using Games.GlobeDefine; using UnityEditor; using UnityEngine; public static class ShaderVariantTool { // public const string variantCollectionPath = "Assets/Project/Shader/ShaderVariants.shadervariants"; // public const string shadowDepthKeyword = "SHADOWS_DEPTH"; // public const string fogKeyword = "FOG_LINEAR"; // public const string lightmapKeyword = "LIGHTMAP_ON"; // public const string directionalKeyword = "DIRECTIONAL"; // // private const string _fogCompileWord = "fog"; // private const string _forwardBaseCompileWord = "fwdbase"; public static readonly string[] excludeKeywords = { "UNITY_HDR_ON", "DIRLIGHTMAP_COMBINED", "DIRLIGHTMAP_SEPARATE" }; // public static void BuildVariantForShaders(IList assetPaths) // { // var data = new List(); // for (var i = 0; i < assetPaths.Count; i++) // { // var path = assetPaths[i]; // var shader = AssetDatabase.LoadAssetAtPath(path); // var shaderSettings = CreateVariants(path, shader); // data.Add(shaderSettings); // } // // var collection = new ShaderVariantCollection(); // for (var i = 0; i < data.Count; i++) // { // var varients = data[i].ParseToVarients(); // for (var j = 0; j < varients.Length; j++) // collection.Add(varients[j]); // } // // AssetDatabase.CreateAsset(collection, variantCollectionPath); // AssetDatabase.SaveAssets(); // AssetDatabase.Refresh(); // Debug.LogWarning("Build Shader Variant Collection for " + assetPaths.Count); // } // public static ShaderVariantsSetting CreateVariants(string assetPath, Shader shader) // { // var filePath = Application.dataPath.Open(assetPath.Substring("Assets".Length)); // var keywordList = new List(); // var useForwardBase = false; // var useShadow = false; // var lines = File.ReadAllLines(filePath); // var regex = new Regex("(?<=#pragma multi_compile).+"); // var shadowRegex = new Regex("\"LightMode\"\\s*=\\s*\"ShadowCaster\""); // foreach (var line in lines) // { // var match = regex.Match(line); // if (match.Success) // { // var result = match.Value.Trim('_', ' '); // // Unity内置Keyword // if (match.Value.StartsWith("_")) // { // switch (result) // { // case _fogCompileWord: // if (!keywordList.Contains(fogKeyword)) // keywordList.Add(fogKeyword); // break; // case _forwardBaseCompileWord: // useForwardBase = true; // break; // } // } // else if (match.Value.Contains("__")) // { // if (!result.Contains(" ") && !excludeKeywords.Contain(result) && !keywordList.Contains(result)) // keywordList.Add(result); // } // else // { // Debug.LogWarning("无法处理 " + line.TrimStart()); // } // } // else // { // match = shadowRegex.Match(line); // if (match.Success) // useShadow = true; // } // } // // // 注:useForwardBase的物体,都是景物,仅仅需要烘培阴影 // if (useForwardBase) // useShadow = false; // var fixedKeywords = useForwardBase ? new[] {lightmapKeyword, directionalKeyword} : new string[0]; // var toggleKeywords = keywordList.ToArray(); // return new ShaderVariantsSetting(shader.name, useForwardBase ? PassType.ForwardBase : PassType.Normal, // fixedKeywords, toggleKeywords, useShadow); // } // public class ShaderVariantsSetting // { // public readonly string[] fixedKeywords; // public readonly PassType passType; // public readonly string shaderName; // public readonly bool shadow; // public readonly string[] toggleKeywords; // // public ShaderVariantsSetting(string name, PassType pass, string[] fixedMacro, string[] toggleMacro, // bool useShadow = false) // { // shaderName = name; // passType = pass; // fixedKeywords = fixedMacro; // toggleKeywords = toggleMacro; // shadow = useShadow; // } // // public ShaderVariantCollection.ShaderVariant[] ParseToVarients() // { // var shader = Shader.Find(shaderName); // if (shader == null) // throw new ArgumentException("无法获得Shader " + shaderName); // var end = 1 << toggleKeywords.Length; // var variants = new ShaderVariantCollection.ShaderVariant[end + (shadow ? 1 : 0)]; // for (var i = 0; i < end; i++) // { // var index = 0; // var count = EditorCommonUtility.GetMaskCount(i); // var keywords = new string[count + fixedKeywords.Length]; // for (var j = 0; j < toggleKeywords.Length; j++) // { // var num = j.ToFlag(); // if (i.ContainFlag(num)) // { // keywords[index] = toggleKeywords[j]; // index++; // } // } // // for (var j = 0; j < fixedKeywords.Length; j++) // keywords[count + j] = fixedKeywords[j]; // variants[i] = new ShaderVariantCollection.ShaderVariant // { // shader = shader, // passType = passType, // keywords = keywords // }; // } // // if (shadow) // variants[end] = new ShaderVariantCollection.ShaderVariant // { // shader = shader, // passType = PassType.ShadowCaster, // keywords = new[] {shadowDepthKeyword} // }; // // return variants; // } // } #region DummyMaterial public static void CreateAllDummyMaterials(List assetPaths, string bundlePath) { var recordPath = GlobeVar.testConfigPath.Open("DummyMatRecord.txt"); var create = true; assetPaths.Sort(); var builder = new StringBuilder(); foreach (var assetPath in assetPaths) builder.AppendLine(assetPath); var newHash = ClassifyBundles.GetStringHash(builder.ToString()); if (File.Exists(recordPath)) { var oldHash = File.ReadAllText(recordPath); if (oldHash.Equals(newHash, StringComparison.Ordinal)) create = false; } if (create) { var data = new List(); var dummyFilePath = EditorCommonUtility.AssetToFilePath(ClassifyBundles.dummyMaterialPath); if (Directory.Exists(dummyFilePath)) Directory.Delete(dummyFilePath, true); Directory.CreateDirectory(dummyFilePath); AssetDatabase.Refresh(); for (var i = 0; i < assetPaths.Count; i++) { var path = assetPaths[i]; var shader = AssetDatabase.LoadAssetAtPath(path); var shaderSettings = CreateDummyMaterials(path, shader); data.Add(shaderSettings); } for (var i = 0; i < data.Count; i++) { var variants = data[i]; variants.SaveToVariants(bundlePath); } File.WriteAllText(recordPath, newHash); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.LogWarning("Build Shader Variant Collection for " + assetPaths.Count); } else Debug.LogWarning("Skip Shader Variant as Hash matched!"); } public static DummyMaterialBuilder CreateDummyMaterials(string assetPath, Shader shader) { var filePath = Application.dataPath.Open(assetPath.Substring("Assets".Length)); var keywordList = new List(); var lines = File.ReadAllLines(filePath); var regex = new Regex("(?<=#pragma multi_compile).+"); foreach (var line in lines) { var match = regex.Match(line); if (match.Success) { var result = match.Value.Trim('_', ' '); // Unity内置Keyword if (match.Value.StartsWith("_")) { } else if (match.Value.Contains("__")) { if (!result.Contains(" ") && !excludeKeywords.Contain(result) && !keywordList.Contains(result)) keywordList.Add(result); } else { Debug.LogWarning("无法处理 " + line.TrimStart()); } } } // 注:useForwardBase的物体,都是景物,仅仅需要烘培阴影 return new DummyMaterialBuilder(shader, keywordList.ToArray()); } public class DummyMaterialBuilder { public readonly Shader shader; public readonly string[] toggleKeywords; public DummyMaterialBuilder(Shader name, string[] toggleMacro) { shader = name; toggleKeywords = toggleMacro; } public void SaveToVariants(string bundleName) { if (!shader) throw new ArgumentException("无法获得Shader!"); var end = 1 << toggleKeywords.Length; var variants = new Material[end]; for (var i = 0; i < end; i++) { var index = 0; var count = EditorCommonUtility.GetMaskCount(i); var keywords = new string[count]; for (var j = 0; j < toggleKeywords.Length; j++) { var num = j.ToFlag(); if (i.ContainFlag(num)) { keywords[index] = toggleKeywords[j]; index++; } } variants[i] = new Material(shader); for (var j = 0; j < keywords.Length; j++) variants[i].EnableKeyword(keywords[j]); variants[i].name = shader.name + "_" + i; } for (var i = 0; i < variants.Length; i++) { var assetPath = ClassifyBundles.dummyMaterialPath.Open(ClassifyBundles.GetStringHash(variants[i].name) + AssetConst.materialExtension); AssetDatabase.CreateAsset(variants[i], assetPath); var importer = AssetImporter.GetAtPath(assetPath); importer.assetBundleName = bundleName; importer.assetBundleVariant = LoadAssetBundle.bundleFileExtension; } } } #endregion }