MaterialUpgrader.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using System;
  4. namespace UnityEditor.Rendering
  5. {
  6. /// <summary>
  7. /// Material Upgrader dialog text.
  8. /// </summary>
  9. public static class DialogText
  10. {
  11. /// <summary>Material Upgrader title.</summary>
  12. public static readonly string title = "Material Upgrader";
  13. /// <summary>Material Upgrader proceed.</summary>
  14. public static readonly string proceed = "Proceed";
  15. /// <summary>Material Upgrader Ok.</summary>
  16. public static readonly string ok = "Ok";
  17. /// <summary>Material Upgrader cancel.</summary>
  18. public static readonly string cancel = "Cancel";
  19. /// <summary>Material Upgrader no selection message.</summary>
  20. public static readonly string noSelectionMessage = "You must select at least one material.";
  21. /// <summary>Material Upgrader project backup message.</summary>
  22. public static readonly string projectBackMessage = "Make sure to have a project backup before proceeding.";
  23. }
  24. /// <summary>
  25. /// Material Upgrader class.
  26. /// </summary>
  27. public class MaterialUpgrader
  28. {
  29. /// <summary>
  30. /// Material Upgrader finalizer delegate.
  31. /// </summary>
  32. /// <param name="mat">Material</param>
  33. public delegate void MaterialFinalizer(Material mat);
  34. string m_OldShader;
  35. string m_NewShader;
  36. MaterialFinalizer m_Finalizer;
  37. Dictionary<string, string> m_TextureRename = new Dictionary<string, string>();
  38. Dictionary<string, string> m_FloatRename = new Dictionary<string, string>();
  39. Dictionary<string, string> m_ColorRename = new Dictionary<string, string>();
  40. Dictionary<string, float> m_FloatPropertiesToSet = new Dictionary<string, float>();
  41. Dictionary<string, Color> m_ColorPropertiesToSet = new Dictionary<string, Color>();
  42. List<string> m_TexturesToRemove = new List<string>();
  43. Dictionary<string, Texture> m_TexturesToSet = new Dictionary<string, Texture>();
  44. class KeywordFloatRename
  45. {
  46. public string keyword;
  47. public string property;
  48. public float setVal, unsetVal;
  49. }
  50. List<KeywordFloatRename> m_KeywordFloatRename = new List<KeywordFloatRename>();
  51. /// <summary>
  52. /// Upgrade Flags
  53. /// </summary>
  54. [Flags]
  55. public enum UpgradeFlags
  56. {
  57. /// <summary>None.</summary>
  58. None = 0,
  59. /// <summary>LogErrorOnNonExistingProperty.</summary>
  60. LogErrorOnNonExistingProperty = 1,
  61. /// <summary>CleanupNonUpgradedProperties.</summary>
  62. CleanupNonUpgradedProperties = 2,
  63. /// <summary>LogMessageWhenNoUpgraderFound.</summary>
  64. LogMessageWhenNoUpgraderFound = 4
  65. }
  66. /// <summary>
  67. /// Upgrade method.
  68. /// </summary>
  69. /// <param name="material">Material to upgrade.</param>
  70. /// <param name="flags">Upgrade flag</param>
  71. public void Upgrade(Material material, UpgradeFlags flags)
  72. {
  73. Material newMaterial;
  74. if ((flags & UpgradeFlags.CleanupNonUpgradedProperties) != 0)
  75. {
  76. newMaterial = new Material(Shader.Find(m_NewShader));
  77. }
  78. else
  79. {
  80. newMaterial = UnityEngine.Object.Instantiate(material) as Material;
  81. newMaterial.shader = Shader.Find(m_NewShader);
  82. }
  83. Convert(material, newMaterial);
  84. material.shader = Shader.Find(m_NewShader);
  85. material.CopyPropertiesFromMaterial(newMaterial);
  86. UnityEngine.Object.DestroyImmediate(newMaterial);
  87. if (m_Finalizer != null)
  88. m_Finalizer(material);
  89. }
  90. // Overridable function to implement custom material upgrading functionality
  91. /// <summary>
  92. /// Custom material conversion method.
  93. /// </summary>
  94. /// <param name="srcMaterial">Source material.</param>
  95. /// <param name="dstMaterial">Destination material.</param>
  96. public virtual void Convert(Material srcMaterial, Material dstMaterial)
  97. {
  98. foreach (var t in m_TextureRename)
  99. {
  100. if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value))
  101. continue;
  102. dstMaterial.SetTextureScale(t.Value, srcMaterial.GetTextureScale(t.Key));
  103. dstMaterial.SetTextureOffset(t.Value, srcMaterial.GetTextureOffset(t.Key));
  104. dstMaterial.SetTexture(t.Value, srcMaterial.GetTexture(t.Key));
  105. }
  106. foreach (var t in m_FloatRename)
  107. {
  108. if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value))
  109. continue;
  110. dstMaterial.SetFloat(t.Value, srcMaterial.GetFloat(t.Key));
  111. }
  112. foreach (var t in m_ColorRename)
  113. {
  114. if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value))
  115. continue;
  116. dstMaterial.SetColor(t.Value, srcMaterial.GetColor(t.Key));
  117. }
  118. foreach (var prop in m_TexturesToRemove)
  119. {
  120. if (!dstMaterial.HasProperty(prop))
  121. continue;
  122. dstMaterial.SetTexture(prop, null);
  123. }
  124. foreach (var prop in m_TexturesToSet)
  125. {
  126. if (!dstMaterial.HasProperty(prop.Key))
  127. continue;
  128. dstMaterial.SetTexture(prop.Key, prop.Value);
  129. }
  130. foreach (var prop in m_FloatPropertiesToSet)
  131. {
  132. if (!dstMaterial.HasProperty(prop.Key))
  133. continue;
  134. dstMaterial.SetFloat(prop.Key, prop.Value);
  135. }
  136. foreach (var prop in m_ColorPropertiesToSet)
  137. {
  138. if (!dstMaterial.HasProperty(prop.Key))
  139. continue;
  140. dstMaterial.SetColor(prop.Key, prop.Value);
  141. }
  142. foreach (var t in m_KeywordFloatRename)
  143. {
  144. if (!dstMaterial.HasProperty(t.property))
  145. continue;
  146. dstMaterial.SetFloat(t.property, srcMaterial.IsKeywordEnabled(t.keyword) ? t.setVal : t.unsetVal);
  147. }
  148. }
  149. /// <summary>
  150. /// Rename shader.
  151. /// </summary>
  152. /// <param name="oldName">Old name.</param>
  153. /// <param name="newName">New name.</param>
  154. /// <param name="finalizer">Finalizer delegate.</param>
  155. public void RenameShader(string oldName, string newName, MaterialFinalizer finalizer = null)
  156. {
  157. m_OldShader = oldName;
  158. m_NewShader = newName;
  159. m_Finalizer = finalizer;
  160. }
  161. /// <summary>
  162. /// Rename Texture Parameter.
  163. /// </summary>
  164. /// <param name="oldName">Old name.</param>
  165. /// <param name="newName">New name.</param>
  166. public void RenameTexture(string oldName, string newName)
  167. {
  168. m_TextureRename[oldName] = newName;
  169. }
  170. /// <summary>
  171. /// Rename Float Parameter.
  172. /// </summary>
  173. /// <param name="oldName">Old name.</param>
  174. /// <param name="newName">New name.</param>
  175. public void RenameFloat(string oldName, string newName)
  176. {
  177. m_FloatRename[oldName] = newName;
  178. }
  179. /// <summary>
  180. /// Rename Color Parameter.
  181. /// </summary>
  182. /// <param name="oldName">Old name.</param>
  183. /// <param name="newName">New name.</param>
  184. public void RenameColor(string oldName, string newName)
  185. {
  186. m_ColorRename[oldName] = newName;
  187. }
  188. /// <summary>
  189. /// Remove Texture Parameter.
  190. /// </summary>
  191. /// <param name="name">Parameter name.</param>
  192. public void RemoveTexture(string name)
  193. {
  194. m_TexturesToRemove.Add(name);
  195. }
  196. /// <summary>
  197. /// Set float property.
  198. /// </summary>
  199. /// <param name="propertyName">Property name.</param>
  200. /// <param name="value">Property value.</param>
  201. public void SetFloat(string propertyName, float value)
  202. {
  203. m_FloatPropertiesToSet[propertyName] = value;
  204. }
  205. /// <summary>
  206. /// Set color property.
  207. /// </summary>
  208. /// <param name="propertyName">Property name.</param>
  209. /// <param name="value">Property value.</param>
  210. public void SetColor(string propertyName, Color value)
  211. {
  212. m_ColorPropertiesToSet[propertyName] = value;
  213. }
  214. /// <summary>
  215. /// Set texture property.
  216. /// </summary>
  217. /// <param name="propertyName">Property name.</param>
  218. /// <param name="value">Property value.</param>
  219. public void SetTexture(string propertyName, Texture value)
  220. {
  221. m_TexturesToSet[propertyName] = value;
  222. }
  223. /// <summary>
  224. /// Rename a keyword to float.
  225. /// </summary>
  226. /// <param name="oldName">Old name.</param>
  227. /// <param name="newName">New name.</param>
  228. /// <param name="setVal">Value when set.</param>
  229. /// <param name="unsetVal">Value when unset.</param>
  230. public void RenameKeywordToFloat(string oldName, string newName, float setVal, float unsetVal)
  231. {
  232. m_KeywordFloatRename.Add(new KeywordFloatRename { keyword = oldName, property = newName, setVal = setVal, unsetVal = unsetVal });
  233. }
  234. static bool IsMaterialPath(string path)
  235. {
  236. return path.EndsWith(".mat", StringComparison.OrdinalIgnoreCase);
  237. }
  238. static MaterialUpgrader GetUpgrader(List<MaterialUpgrader> upgraders, Material material)
  239. {
  240. if (material == null || material.shader == null)
  241. return null;
  242. string shaderName = material.shader.name;
  243. for (int i = 0; i != upgraders.Count; i++)
  244. {
  245. if (upgraders[i].m_OldShader == shaderName)
  246. return upgraders[i];
  247. }
  248. return null;
  249. }
  250. //@TODO: Only do this when it exceeds memory consumption...
  251. static void SaveAssetsAndFreeMemory()
  252. {
  253. AssetDatabase.SaveAssets();
  254. GC.Collect();
  255. EditorUtility.UnloadUnusedAssetsImmediate();
  256. AssetDatabase.Refresh();
  257. }
  258. private static bool ShouldUpgradeShader(Material material, HashSet<string> shaderNamesToIgnore)
  259. {
  260. if (material == null)
  261. return false;
  262. if (material.shader == null)
  263. return false;
  264. return !shaderNamesToIgnore.Contains(material.shader.name);
  265. }
  266. /// <summary>
  267. /// Upgrade the project folder.
  268. /// </summary>
  269. /// <param name="upgraders">List of upgraders.</param>
  270. /// <param name="progressBarName">Name of the progress bar.</param>
  271. /// <param name="flags">Material Upgrader flags.</param>
  272. public static void UpgradeProjectFolder(List<MaterialUpgrader> upgraders, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  273. {
  274. HashSet<string> shaderNamesToIgnore = new HashSet<string>();
  275. UpgradeProjectFolder(upgraders, shaderNamesToIgnore, progressBarName, flags);
  276. }
  277. /// <summary>
  278. /// Upgrade the project folder.
  279. /// </summary>
  280. /// <param name="upgraders">List of upgraders.</param>
  281. /// <param name="shaderNamesToIgnore">Set of shader names to ignore.</param>
  282. /// <param name="progressBarName">Name of the progress bar.</param>
  283. /// <param name="flags">Material Upgrader flags.</param>
  284. public static void UpgradeProjectFolder(List<MaterialUpgrader> upgraders, HashSet<string> shaderNamesToIgnore, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  285. {
  286. if (!EditorUtility.DisplayDialog(DialogText.title, "The upgrade will overwrite materials in your project. " + DialogText.projectBackMessage, DialogText.proceed, DialogText.cancel))
  287. return;
  288. int totalMaterialCount = 0;
  289. foreach (string s in UnityEditor.AssetDatabase.GetAllAssetPaths())
  290. {
  291. if (IsMaterialPath(s))
  292. totalMaterialCount++;
  293. }
  294. int materialIndex = 0;
  295. foreach (string path in UnityEditor.AssetDatabase.GetAllAssetPaths())
  296. {
  297. if (IsMaterialPath(path))
  298. {
  299. materialIndex++;
  300. if (UnityEditor.EditorUtility.DisplayCancelableProgressBar(progressBarName, string.Format("({0} of {1}) {2}", materialIndex, totalMaterialCount, path), (float)materialIndex / (float)totalMaterialCount))
  301. break;
  302. Material m = UnityEditor.AssetDatabase.LoadMainAssetAtPath(path) as Material;
  303. if (!ShouldUpgradeShader(m, shaderNamesToIgnore))
  304. continue;
  305. Upgrade(m, upgraders, flags);
  306. //SaveAssetsAndFreeMemory();
  307. }
  308. }
  309. UnityEditor.EditorUtility.ClearProgressBar();
  310. }
  311. /// <summary>
  312. /// Upgrade a material.
  313. /// </summary>
  314. /// <param name="material">Material to upgrade.</param>
  315. /// <param name="upgrader">Material upgrader.</param>
  316. /// <param name="flags">Material Upgrader flags.</param>
  317. public static void Upgrade(Material material, MaterialUpgrader upgrader, UpgradeFlags flags)
  318. {
  319. var upgraders = new List<MaterialUpgrader>();
  320. upgraders.Add(upgrader);
  321. Upgrade(material, upgraders, flags);
  322. }
  323. /// <summary>
  324. /// Upgrade a material.
  325. /// </summary>
  326. /// <param name="material">Material to upgrade.</param>
  327. /// <param name="upgraders">List of Material upgraders.</param>
  328. /// <param name="flags">Material Upgrader flags.</param>
  329. public static void Upgrade(Material material, List<MaterialUpgrader> upgraders, UpgradeFlags flags)
  330. {
  331. if (material == null)
  332. return;
  333. var upgrader = GetUpgrader(upgraders, material);
  334. if (upgrader != null)
  335. upgrader.Upgrade(material, flags);
  336. else if ((flags & UpgradeFlags.LogMessageWhenNoUpgraderFound) == UpgradeFlags.LogMessageWhenNoUpgraderFound)
  337. Debug.Log(string.Format("{0} material was not upgraded. There's no upgrader to convert {1} shader to selected pipeline", material.name, material.shader.name));
  338. }
  339. /// <summary>
  340. /// Upgrade the selection.
  341. /// </summary>
  342. /// <param name="upgraders">List of upgraders.</param>
  343. /// <param name="progressBarName">Name of the progress bar.</param>
  344. /// <param name="flags">Material Upgrader flags.</param>
  345. public static void UpgradeSelection(List<MaterialUpgrader> upgraders, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  346. {
  347. HashSet<string> shaderNamesToIgnore = new HashSet<string>();
  348. UpgradeSelection(upgraders, shaderNamesToIgnore, progressBarName, flags);
  349. }
  350. /// <summary>
  351. /// Upgrade the selection.
  352. /// </summary>
  353. /// <param name="upgraders">List of upgraders.</param>
  354. /// <param name="shaderNamesToIgnore">Set of shader names to ignore.</param>
  355. /// <param name="progressBarName">Name of the progress bar.</param>
  356. /// <param name="flags">Material Upgrader flags.</param>
  357. public static void UpgradeSelection(List<MaterialUpgrader> upgraders, HashSet<string> shaderNamesToIgnore, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  358. {
  359. var selection = Selection.objects;
  360. if (selection == null)
  361. {
  362. EditorUtility.DisplayDialog(DialogText.title, DialogText.noSelectionMessage, DialogText.ok);
  363. return;
  364. }
  365. List<Material> selectedMaterials = new List<Material>(selection.Length);
  366. for (int i = 0; i < selection.Length; ++i)
  367. {
  368. Material mat = selection[i] as Material;
  369. if (mat != null)
  370. selectedMaterials.Add(mat);
  371. }
  372. int selectedMaterialsCount = selectedMaterials.Count;
  373. if (selectedMaterialsCount == 0)
  374. {
  375. EditorUtility.DisplayDialog(DialogText.title, DialogText.noSelectionMessage, DialogText.ok);
  376. return;
  377. }
  378. if (!EditorUtility.DisplayDialog(DialogText.title, string.Format("The upgrade will overwrite {0} selected material{1}. ", selectedMaterialsCount, selectedMaterialsCount > 1 ? "s" : "") +
  379. DialogText.projectBackMessage, DialogText.proceed, DialogText.cancel))
  380. return;
  381. string lastMaterialName = "";
  382. for (int i = 0; i < selectedMaterialsCount; i++)
  383. {
  384. if (UnityEditor.EditorUtility.DisplayCancelableProgressBar(progressBarName, string.Format("({0} of {1}) {2}", i, selectedMaterialsCount, lastMaterialName), (float)i / (float)selectedMaterialsCount))
  385. break;
  386. var material = selectedMaterials[i];
  387. if (!ShouldUpgradeShader(material, shaderNamesToIgnore))
  388. continue;
  389. Upgrade(material, upgraders, flags);
  390. if (material != null)
  391. lastMaterialName = material.name;
  392. }
  393. UnityEditor.EditorUtility.ClearProgressBar();
  394. }
  395. }
  396. }