ShaderGenerator.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text.RegularExpressions;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using SoftMasking.Editor;
  9. using UnityEngine.Assertions;
  10. namespace SoftMasking.TextMeshPro.Editor {
  11. public static class ShaderGenerator {
  12. public class ShaderResource {
  13. public readonly Shader shader;
  14. public readonly string path;
  15. public readonly string text;
  16. public readonly string name;
  17. public ShaderResource(string path) {
  18. this.path = path;
  19. this.shader = AssetDatabase.LoadAssetAtPath<Shader>(path);
  20. this.text = ReadResource(path);
  21. this.name = Path.GetFileNameWithoutExtension(path);
  22. }
  23. }
  24. [MenuItem("Tools/Soft Mask/Update TestMesh Pro Integration")]
  25. public static void UpdateShaders() {
  26. var tmproShaders = CollectTMProShaders().ToList();
  27. if (tmproShaders.Count == 0) {
  28. Debug.LogError(
  29. "Could not update integration because TextMesh Pro shaders are not found. " +
  30. "Make sure that TextMesh Pro package is installed. If you're using " +
  31. "the Package Manager version of TextMesh Pro, import TextMesh Pro " +
  32. "essentials first.");
  33. return;
  34. }
  35. foreach (var shader in tmproShaders) {
  36. try {
  37. var newText = ShaderPatcher.Patch(shader.text);
  38. var replacementFileName = shader.name + " - SoftMask.shader";
  39. var generatedShadersPath = PackageResources.generatedShaderResourcesPath;
  40. if (!Directory.Exists(generatedShadersPath))
  41. Directory.CreateDirectory(generatedShadersPath);
  42. var outputFile = Path.Combine(generatedShadersPath, replacementFileName);
  43. File.WriteAllText(outputFile, UpdateIncludes(newText));
  44. AssetDatabase.ImportAsset(outputFile);
  45. } catch (Exception ex) {
  46. Debug.LogErrorFormat(
  47. "Unable to patch TextMesh Pro shader {0}: {1}",
  48. shader.name, ex);
  49. }
  50. }
  51. InvalidateSoftMasks();
  52. }
  53. public static IEnumerable<ShaderResource> CollectTMProShaders() {
  54. return
  55. TMProShaderGUIDs.Concat(TMProShaderPackageGUIDs)
  56. .Select(x => AssetDatabase.GUIDToAssetPath(x))
  57. .Where(x => !string.IsNullOrEmpty(x))
  58. .Select(x => new ShaderResource(x))
  59. .Where(x => CheckIsUIShader(x.shader));
  60. }
  61. static readonly List<string> TMProShaderGUIDs = new List<string> {
  62. "d1cf17907700cb647aa3ea423ba38f2e", // TMP_Bitmap-Mobile
  63. "edfcf888cd11d9245b91d2883049a57e", // TMP_Bitmap
  64. "afc255f7c2be52e41973a3d10a2e632d", // TMP_SDF-Mobile Masking
  65. "cafd18099dfc0114896e0a8b277b81b6", // TMP_SDF-Mobile
  66. "dca26082f9cb439469295791d9f76fe5", // TMP_SDF
  67. "3a1c68c8292caf046bd21158886c5e40" // TMP_Sprite
  68. };
  69. static readonly List<string> TMProShaderPackageGUIDs = new List<string> {
  70. "1e3b057af24249748ff873be7fafee47", // TMP_Bitmap-Mobile
  71. "128e987d567d4e2c824d754223b3f3b0", // TMP_Bitmap
  72. "bc1ede39bf3643ee8e493720e4259791", // TMP_SDF-Mobile Masking
  73. "fe393ace9b354375a9cb14cdbbc28be4", // TMP_SDF-Mobile
  74. "68e6db2ebdc24f95958faec2be5558d6", // TMP_SDF
  75. "cf81c85f95fe47e1a27f6ae460cf182c" // TMP_Sprite
  76. };
  77. static readonly Dictionary<string, List<string>> KnownIncludeGUIDs = new Dictionary<string, List<string>> {
  78. { "TMPro_Properties.cginc", new List<string> {
  79. "bc2d34f37efcbdf429ed46cb34aa2ad5",
  80. "3997e2241185407d80309a82f9148466"} },
  81. { "TMPro.cginc", new List<string> {
  82. "438defe6a2827704f90bdf852732bc11",
  83. "407bc68d299748449bbf7f48ee690f8d"} },
  84. // We do not have to use absolute path for SoftMask.cginc because patched shaders
  85. // reside in a subfolder but it's convenient to reuse mechanism made for TMPro includes.
  86. { "SoftMask.cginc", new List<string> {
  87. "2731380563bbb5a4aa4d3c9f57966cce" } }
  88. };
  89. static Dictionary<string, string> s_knownIncludes;
  90. static Dictionary<string, string> knownIncludes {
  91. get {
  92. if (s_knownIncludes == null)
  93. s_knownIncludes =
  94. KnownIncludeGUIDs
  95. .ToDictionary(
  96. kv => kv.Key,
  97. kv => kv.Value
  98. .Select(guid => AssetDatabase.GUIDToAssetPath(guid))
  99. .First(x => !string.IsNullOrEmpty(x)));
  100. return s_knownIncludes;
  101. }
  102. }
  103. static string UpdateInclude(string includePath) {
  104. string result;
  105. return
  106. knownIncludes.TryGetValue(Path.GetFileName(includePath), out result)
  107. ? result
  108. : includePath;
  109. }
  110. static string UpdateIncludes(string shaderCode) {
  111. return
  112. Regex.Replace(
  113. shaderCode,
  114. "#include \"(.+)\"",
  115. match => string.Format(
  116. "#include \"{0}\"",
  117. UpdateInclude(match.Groups[1].Value)));
  118. }
  119. static bool CheckIsUIShader(Shader shader) {
  120. var material = new Material(shader) {
  121. hideFlags = HideFlags.HideAndDontSave
  122. };
  123. var result = material.HasProperty(Ids.Stencil);
  124. UnityEngine.Object.DestroyImmediate(material);
  125. return result;
  126. }
  127. static string ReadResource(string path) { return File.ReadAllText(path); }
  128. static void InvalidateSoftMasks() {
  129. var softMaskPath = AssetDatabase.GUIDToAssetPath(PackageResources.SoftMaskCsGUID);
  130. Assert.IsFalse(string.IsNullOrEmpty(softMaskPath));
  131. AssetDatabase.ImportAsset(softMaskPath);
  132. }
  133. static class Ids {
  134. public static readonly int Stencil = Shader.PropertyToID("_Stencil");
  135. }
  136. }
  137. }