No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MaterialUpgrader.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using System;
  4. using UnityEngine.Rendering;
  5. namespace UnityEditor.Rendering
  6. {
  7. /// <summary>
  8. /// Material Upgrader dialog text.
  9. /// </summary>
  10. public static class DialogText
  11. {
  12. /// <summary>Material Upgrader title.</summary>
  13. public static readonly string title = "Material Upgrader";
  14. /// <summary>Material Upgrader proceed.</summary>
  15. public static readonly string proceed = "Proceed";
  16. /// <summary>Material Upgrader Ok.</summary>
  17. public static readonly string ok = "Ok";
  18. /// <summary>Material Upgrader cancel.</summary>
  19. public static readonly string cancel = "Cancel";
  20. /// <summary>Material Upgrader no selection message.</summary>
  21. public static readonly string noSelectionMessage = "You must select at least one material.";
  22. /// <summary>Material Upgrader project backup message.</summary>
  23. public static readonly string projectBackMessage = "Make sure to have a project backup before proceeding.";
  24. }
  25. /// <summary>
  26. /// Material Upgrader class.
  27. /// </summary>
  28. public class MaterialUpgrader
  29. {
  30. /// <summary>
  31. /// Material Upgrader finalizer delegate.
  32. /// </summary>
  33. /// <param name="mat">Material</param>
  34. public delegate void MaterialFinalizer(Material mat);
  35. string m_OldShader;
  36. string m_NewShader;
  37. private static string[] s_PathsWhiteList = new[]
  38. {
  39. "Hidden/",
  40. "HDRP/",
  41. "Shader Graphs/"
  42. };
  43. /// <summary>
  44. /// Retrieves path to new shader.
  45. /// </summary>
  46. public string NewShaderPath
  47. {
  48. get => m_NewShader;
  49. }
  50. MaterialFinalizer m_Finalizer;
  51. Dictionary<string, string> m_TextureRename = new Dictionary<string, string>();
  52. Dictionary<string, string> m_FloatRename = new Dictionary<string, string>();
  53. Dictionary<string, string> m_ColorRename = new Dictionary<string, string>();
  54. Dictionary<string, float> m_FloatPropertiesToSet = new Dictionary<string, float>();
  55. Dictionary<string, Color> m_ColorPropertiesToSet = new Dictionary<string, Color>();
  56. List<string> m_TexturesToRemove = new List<string>();
  57. Dictionary<string, Texture> m_TexturesToSet = new Dictionary<string, Texture>();
  58. class KeywordFloatRename
  59. {
  60. public string keyword;
  61. public string property;
  62. public float setVal, unsetVal;
  63. }
  64. List<KeywordFloatRename> m_KeywordFloatRename = new List<KeywordFloatRename>();
  65. /// <summary>
  66. /// Type of property to rename.
  67. /// </summary>
  68. public enum MaterialPropertyType
  69. {
  70. /// <summary>Texture reference property.</summary>
  71. Texture,
  72. /// <summary>Float property.</summary>
  73. Float,
  74. /// <summary>Color property.</summary>
  75. Color
  76. }
  77. /// <summary>
  78. /// Retrieves a collection of renamed parameters of a specific MaterialPropertyType.
  79. /// </summary>
  80. /// <param name="type">Material Property Type</param>
  81. /// <returns>Dictionary of property names to their renamed values.</returns>
  82. /// <exception cref="ArgumentException">type is not valid.</exception>
  83. public IReadOnlyDictionary<string, string> GetPropertyRenameMap(MaterialPropertyType type)
  84. {
  85. switch (type)
  86. {
  87. case MaterialPropertyType.Texture: return m_TextureRename;
  88. case MaterialPropertyType.Float: return m_FloatRename;
  89. case MaterialPropertyType.Color: return m_ColorRename;
  90. default: throw new ArgumentException(nameof(type));
  91. }
  92. }
  93. /// <summary>
  94. /// Upgrade Flags
  95. /// </summary>
  96. [Flags]
  97. public enum UpgradeFlags
  98. {
  99. /// <summary>None.</summary>
  100. None = 0,
  101. /// <summary>LogErrorOnNonExistingProperty.</summary>
  102. LogErrorOnNonExistingProperty = 1,
  103. /// <summary>CleanupNonUpgradedProperties.</summary>
  104. CleanupNonUpgradedProperties = 2,
  105. /// <summary>LogMessageWhenNoUpgraderFound.</summary>
  106. LogMessageWhenNoUpgraderFound = 4
  107. }
  108. /// <summary>
  109. /// Upgrade method.
  110. /// </summary>
  111. /// <param name="material">Material to upgrade.</param>
  112. /// <param name="flags">Upgrade flag</param>
  113. public void Upgrade(Material material, UpgradeFlags flags)
  114. {
  115. Material newMaterial;
  116. if ((flags & UpgradeFlags.CleanupNonUpgradedProperties) != 0)
  117. {
  118. newMaterial = new Material(Shader.Find(m_NewShader));
  119. }
  120. else
  121. {
  122. newMaterial = UnityEngine.Object.Instantiate(material) as Material;
  123. newMaterial.shader = Shader.Find(m_NewShader);
  124. }
  125. Convert(material, newMaterial);
  126. material.shader = Shader.Find(m_NewShader);
  127. material.CopyPropertiesFromMaterial(newMaterial);
  128. UnityEngine.Object.DestroyImmediate(newMaterial);
  129. if (m_Finalizer != null)
  130. m_Finalizer(material);
  131. }
  132. // Overridable function to implement custom material upgrading functionality
  133. /// <summary>
  134. /// Custom material conversion method.
  135. /// </summary>
  136. /// <param name="srcMaterial">Source material.</param>
  137. /// <param name="dstMaterial">Destination material.</param>
  138. public virtual void Convert(Material srcMaterial, Material dstMaterial)
  139. {
  140. foreach (var t in m_TextureRename)
  141. {
  142. if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value))
  143. continue;
  144. dstMaterial.SetTextureScale(t.Value, srcMaterial.GetTextureScale(t.Key));
  145. dstMaterial.SetTextureOffset(t.Value, srcMaterial.GetTextureOffset(t.Key));
  146. dstMaterial.SetTexture(t.Value, srcMaterial.GetTexture(t.Key));
  147. }
  148. foreach (var t in m_FloatRename)
  149. {
  150. if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value))
  151. continue;
  152. dstMaterial.SetFloat(t.Value, srcMaterial.GetFloat(t.Key));
  153. }
  154. foreach (var t in m_ColorRename)
  155. {
  156. if (!srcMaterial.HasProperty(t.Key) || !dstMaterial.HasProperty(t.Value))
  157. continue;
  158. dstMaterial.SetColor(t.Value, srcMaterial.GetColor(t.Key));
  159. }
  160. foreach (var prop in m_TexturesToRemove)
  161. {
  162. if (!dstMaterial.HasProperty(prop))
  163. continue;
  164. dstMaterial.SetTexture(prop, null);
  165. }
  166. foreach (var prop in m_TexturesToSet)
  167. {
  168. if (!dstMaterial.HasProperty(prop.Key))
  169. continue;
  170. dstMaterial.SetTexture(prop.Key, prop.Value);
  171. }
  172. foreach (var prop in m_FloatPropertiesToSet)
  173. {
  174. if (!dstMaterial.HasProperty(prop.Key))
  175. continue;
  176. dstMaterial.SetFloat(prop.Key, prop.Value);
  177. }
  178. foreach (var prop in m_ColorPropertiesToSet)
  179. {
  180. if (!dstMaterial.HasProperty(prop.Key))
  181. continue;
  182. dstMaterial.SetColor(prop.Key, prop.Value);
  183. }
  184. foreach (var t in m_KeywordFloatRename)
  185. {
  186. if (!dstMaterial.HasProperty(t.property))
  187. continue;
  188. dstMaterial.SetFloat(t.property, srcMaterial.IsKeywordEnabled(t.keyword) ? t.setVal : t.unsetVal);
  189. }
  190. }
  191. /// <summary>
  192. /// Rename shader.
  193. /// </summary>
  194. /// <param name="oldName">Old name.</param>
  195. /// <param name="newName">New name.</param>
  196. /// <param name="finalizer">Finalizer delegate.</param>
  197. public void RenameShader(string oldName, string newName, MaterialFinalizer finalizer = null)
  198. {
  199. m_OldShader = oldName;
  200. m_NewShader = newName;
  201. m_Finalizer = finalizer;
  202. }
  203. /// <summary>
  204. /// Rename Texture Parameter.
  205. /// </summary>
  206. /// <param name="oldName">Old name.</param>
  207. /// <param name="newName">New name.</param>
  208. public void RenameTexture(string oldName, string newName)
  209. {
  210. m_TextureRename[oldName] = newName;
  211. }
  212. /// <summary>
  213. /// Rename Float Parameter.
  214. /// </summary>
  215. /// <param name="oldName">Old name.</param>
  216. /// <param name="newName">New name.</param>
  217. public void RenameFloat(string oldName, string newName)
  218. {
  219. m_FloatRename[oldName] = newName;
  220. }
  221. /// <summary>
  222. /// Rename Color Parameter.
  223. /// </summary>
  224. /// <param name="oldName">Old name.</param>
  225. /// <param name="newName">New name.</param>
  226. public void RenameColor(string oldName, string newName)
  227. {
  228. m_ColorRename[oldName] = newName;
  229. }
  230. /// <summary>
  231. /// Remove Texture Parameter.
  232. /// </summary>
  233. /// <param name="name">Parameter name.</param>
  234. public void RemoveTexture(string name)
  235. {
  236. m_TexturesToRemove.Add(name);
  237. }
  238. /// <summary>
  239. /// Set float property.
  240. /// </summary>
  241. /// <param name="propertyName">Property name.</param>
  242. /// <param name="value">Property value.</param>
  243. public void SetFloat(string propertyName, float value)
  244. {
  245. m_FloatPropertiesToSet[propertyName] = value;
  246. }
  247. /// <summary>
  248. /// Set color property.
  249. /// </summary>
  250. /// <param name="propertyName">Property name.</param>
  251. /// <param name="value">Property value.</param>
  252. public void SetColor(string propertyName, Color value)
  253. {
  254. m_ColorPropertiesToSet[propertyName] = value;
  255. }
  256. /// <summary>
  257. /// Set texture property.
  258. /// </summary>
  259. /// <param name="propertyName">Property name.</param>
  260. /// <param name="value">Property value.</param>
  261. public void SetTexture(string propertyName, Texture value)
  262. {
  263. m_TexturesToSet[propertyName] = value;
  264. }
  265. /// <summary>
  266. /// Rename a keyword to float.
  267. /// </summary>
  268. /// <param name="oldName">Old name.</param>
  269. /// <param name="newName">New name.</param>
  270. /// <param name="setVal">Value when set.</param>
  271. /// <param name="unsetVal">Value when unset.</param>
  272. public void RenameKeywordToFloat(string oldName, string newName, float setVal, float unsetVal)
  273. {
  274. m_KeywordFloatRename.Add(new KeywordFloatRename { keyword = oldName, property = newName, setVal = setVal, unsetVal = unsetVal });
  275. }
  276. static MaterialUpgrader GetUpgrader(List<MaterialUpgrader> upgraders, Material material)
  277. {
  278. if (material == null || material.shader == null)
  279. return null;
  280. string shaderName = material.shader.name;
  281. for (int i = 0; i != upgraders.Count; i++)
  282. {
  283. if (upgraders[i].m_OldShader == shaderName)
  284. return upgraders[i];
  285. }
  286. return null;
  287. }
  288. //@TODO: Only do this when it exceeds memory consumption...
  289. static void SaveAssetsAndFreeMemory()
  290. {
  291. AssetDatabase.SaveAssets();
  292. GC.Collect();
  293. EditorUtility.UnloadUnusedAssetsImmediate();
  294. AssetDatabase.Refresh();
  295. }
  296. /// <summary>
  297. /// Checking if the passed in value is a path to a Material.
  298. /// </summary>
  299. /// <param name="material">Material to check.</param>
  300. /// <param name="shaderNamesToIgnore">HashSet of strings to ignore.</param>
  301. /// <returns>Returns true if the passed in material's shader is not in the passed in ignore list.</returns>
  302. static bool ShouldUpgradeShader(Material material, HashSet<string> shaderNamesToIgnore)
  303. {
  304. if (material == null)
  305. return false;
  306. if (material.shader == null)
  307. return false;
  308. return !shaderNamesToIgnore.Contains(material.shader.name);
  309. }
  310. private static bool IsNotAutomaticallyUpgradable(List<MaterialUpgrader> upgraders, Material material)
  311. {
  312. return GetUpgrader(upgraders, material) == null && !material.shader.name.ContainsAny(s_PathsWhiteList);
  313. }
  314. /// <summary>
  315. /// Checking if project folder contains any materials that are not using built-in shaders.
  316. /// </summary>
  317. /// <param name="upgraders">List if MaterialUpgraders</param>
  318. /// <returns>Returns true if at least one material uses a non-built-in shader (ignores Hidden, HDRP and Shader Graph Shaders)</returns>
  319. public static bool ProjectFolderContainsNonBuiltinMaterials(List<MaterialUpgrader> upgraders)
  320. {
  321. foreach (var material in AssetDatabaseHelper.FindAssets<Material>(".mat"))
  322. {
  323. if(IsNotAutomaticallyUpgradable(upgraders, material))
  324. return true;
  325. }
  326. return false;
  327. }
  328. /// <summary>
  329. /// Upgrade the project folder.
  330. /// </summary>
  331. /// <param name="upgraders">List of upgraders.</param>
  332. /// <param name="progressBarName">Name of the progress bar.</param>
  333. /// <param name="flags">Material Upgrader flags.</param>
  334. public static void UpgradeProjectFolder(List<MaterialUpgrader> upgraders, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  335. {
  336. HashSet<string> shaderNamesToIgnore = new HashSet<string>();
  337. UpgradeProjectFolder(upgraders, shaderNamesToIgnore, progressBarName, flags);
  338. }
  339. /// <summary>
  340. /// Upgrade the project folder.
  341. /// </summary>
  342. /// <param name="upgraders">List of upgraders.</param>
  343. /// <param name="shaderNamesToIgnore">Set of shader names to ignore.</param>
  344. /// <param name="progressBarName">Name of the progress bar.</param>
  345. /// <param name="flags">Material Upgrader flags.</param>
  346. public static void UpgradeProjectFolder(List<MaterialUpgrader> upgraders, HashSet<string> shaderNamesToIgnore, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  347. {
  348. if ((!Application.isBatchMode) && (!EditorUtility.DisplayDialog(DialogText.title, "The upgrade will overwrite materials in your project. " + DialogText.projectBackMessage, DialogText.proceed, DialogText.cancel)))
  349. return;
  350. var materialAssets = AssetDatabase.FindAssets($"t:{nameof(Material)} glob:\"**/*.mat\"");
  351. int materialIndex = 0;
  352. foreach (var guid in materialAssets)
  353. {
  354. Material material = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid));
  355. materialIndex++;
  356. if (UnityEditor.EditorUtility.DisplayCancelableProgressBar(progressBarName, string.Format("({0} of {1}) {2}", materialIndex, materialAssets.Length, material), (float)materialIndex / (float)materialAssets.Length))
  357. break;
  358. if (!ShouldUpgradeShader(material, shaderNamesToIgnore))
  359. continue;
  360. Upgrade(material, upgraders, flags);
  361. }
  362. // Upgrade terrain specifically since it is a builtin material
  363. if (Terrain.activeTerrains.Length > 0)
  364. {
  365. Material terrainMat = Terrain.activeTerrain.materialTemplate;
  366. Upgrade(terrainMat, upgraders, flags);
  367. }
  368. UnityEditor.EditorUtility.ClearProgressBar();
  369. }
  370. /// <summary>
  371. /// Upgrade a material.
  372. /// </summary>
  373. /// <param name="material">Material to upgrade.</param>
  374. /// <param name="upgrader">Material upgrader.</param>
  375. /// <param name="flags">Material Upgrader flags.</param>
  376. public static void Upgrade(Material material, MaterialUpgrader upgrader, UpgradeFlags flags)
  377. {
  378. using (ListPool<MaterialUpgrader>.Get(out List<MaterialUpgrader> upgraders))
  379. {
  380. upgraders.Add(upgrader);
  381. Upgrade(material, upgraders, flags);
  382. }
  383. }
  384. /// <summary>
  385. /// Upgrade a material.
  386. /// </summary>
  387. /// <param name="material">Material to upgrade.</param>
  388. /// <param name="upgraders">List of Material upgraders.</param>
  389. /// <param name="flags">Material Upgrader flags.</param>
  390. public static void Upgrade(Material material, List<MaterialUpgrader> upgraders, UpgradeFlags flags)
  391. {
  392. string message = string.Empty;
  393. if (Upgrade(material, upgraders, flags, ref message))
  394. return;
  395. if (!string.IsNullOrEmpty(message))
  396. {
  397. Debug.Log(message);
  398. }
  399. }
  400. /// <summary>
  401. /// Upgrade a material.
  402. /// </summary>
  403. /// <param name="material">Material to upgrade.</param>
  404. /// <param name="upgraders">List of Material upgraders.</param>
  405. /// <param name="flags">Material upgrader flags.</param>
  406. /// <param name="message">Error message to be outputted when no material upgraders are suitable for given material if the flags <see cref="UpgradeFlags.LogMessageWhenNoUpgraderFound"/> is used.</param>
  407. /// <returns>Returns true if the upgrader was found for the passed in material.</returns>
  408. public static bool Upgrade(Material material, List<MaterialUpgrader> upgraders, UpgradeFlags flags, ref string message)
  409. {
  410. if (material == null)
  411. return false;
  412. var upgrader = GetUpgrader(upgraders, material);
  413. if (upgrader != null)
  414. {
  415. upgrader.Upgrade(material, flags);
  416. return true;
  417. }
  418. if ((flags & UpgradeFlags.LogMessageWhenNoUpgraderFound) == UpgradeFlags.LogMessageWhenNoUpgraderFound)
  419. {
  420. message =
  421. $"{material.name} material was not upgraded. There's no upgrader to convert {material.shader.name} shader to selected pipeline";
  422. return false;
  423. }
  424. return true;
  425. }
  426. /// <summary>
  427. /// Upgrade the selection.
  428. /// </summary>
  429. /// <param name="upgraders">List of upgraders.</param>
  430. /// <param name="progressBarName">Name of the progress bar.</param>
  431. /// <param name="flags">Material Upgrader flags.</param>
  432. public static void UpgradeSelection(List<MaterialUpgrader> upgraders, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  433. {
  434. HashSet<string> shaderNamesToIgnore = new HashSet<string>();
  435. UpgradeSelection(upgraders, shaderNamesToIgnore, progressBarName, flags);
  436. }
  437. /// <summary>
  438. /// Upgrade the selection.
  439. /// </summary>
  440. /// <param name="upgraders">List of upgraders.</param>
  441. /// <param name="shaderNamesToIgnore">Set of shader names to ignore.</param>
  442. /// <param name="progressBarName">Name of the progress bar.</param>
  443. /// <param name="flags">Material Upgrader flags.</param>
  444. public static void UpgradeSelection(List<MaterialUpgrader> upgraders, HashSet<string> shaderNamesToIgnore, string progressBarName, UpgradeFlags flags = UpgradeFlags.None)
  445. {
  446. var selection = Selection.objects;
  447. if (selection == null)
  448. {
  449. EditorUtility.DisplayDialog(DialogText.title, DialogText.noSelectionMessage, DialogText.ok);
  450. return;
  451. }
  452. List<Material> selectedMaterials = new List<Material>(selection.Length);
  453. for (int i = 0; i < selection.Length; ++i)
  454. {
  455. Material mat = selection[i] as Material;
  456. if (mat != null)
  457. selectedMaterials.Add(mat);
  458. }
  459. int selectedMaterialsCount = selectedMaterials.Count;
  460. if (selectedMaterialsCount == 0)
  461. {
  462. EditorUtility.DisplayDialog(DialogText.title, DialogText.noSelectionMessage, DialogText.ok);
  463. return;
  464. }
  465. if (!EditorUtility.DisplayDialog(DialogText.title, string.Format("The upgrade will overwrite {0} selected material{1}. ", selectedMaterialsCount, selectedMaterialsCount > 1 ? "s" : "") +
  466. DialogText.projectBackMessage, DialogText.proceed, DialogText.cancel))
  467. return;
  468. string lastMaterialName = "";
  469. for (int i = 0; i < selectedMaterialsCount; i++)
  470. {
  471. if (UnityEditor.EditorUtility.DisplayCancelableProgressBar(progressBarName, string.Format("({0} of {1}) {2}", i, selectedMaterialsCount, lastMaterialName), (float)i / (float)selectedMaterialsCount))
  472. break;
  473. var material = selectedMaterials[i];
  474. if (!ShouldUpgradeShader(material, shaderNamesToIgnore))
  475. continue;
  476. Upgrade(material, upgraders, flags);
  477. if (material != null)
  478. lastMaterialName = material.name;
  479. }
  480. UnityEditor.EditorUtility.ClearProgressBar();
  481. }
  482. }
  483. }