暫無描述
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.

ReadonlyMaterialConverter.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEditor.SceneManagement;
  6. using UnityEditor.Search;
  7. using UnityEngine;
  8. using UnityEngine.SceneManagement;
  9. using Object = UnityEngine.Object;
  10. namespace UnityEditor.Rendering.Universal
  11. {
  12. enum IdentifierType { kNullIdentifier = 0, kImportedAsset = 1, kSceneObject = 2, kSourceAsset = 3, kBuiltInAsset = 4 };
  13. internal static class ReadonlyMaterialMap
  14. {
  15. public static readonly Dictionary<string, string> Map = new Dictionary<string, string>
  16. {
  17. {"Default-Diffuse", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Lit.mat"},
  18. {"Default-Material", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Lit.mat"},
  19. {"Default-ParticleSystem", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/ParticlesUnlit.mat"},
  20. {"Default-Particle", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/ParticlesUnlit.mat"},
  21. {"Default-Terrain-Diffuse", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/TerrainLit.mat"},
  22. {"Default-Terrain-Specular", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/TerrainLit.mat"},
  23. {"Default-Terrain-Standard", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/TerrainLit.mat"},
  24. {"Sprites-Default", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Sprite-Unlit-Default.mat"},
  25. {"Sprites-Mask", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Sprite-Unlit-Default.mat"},
  26. {"SpatialMappingOcclusion", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/SpatialMappingOcclusion.mat"},
  27. {"SpatialMappingWireframe", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/SpatialMappingWireframe.mat"},
  28. // TODO: These currently render in URP, but they are using BIRP shaders. Create a task to convert these.
  29. // {"Default UI Material", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Lit.mat"},
  30. // {"ETC1 Supported UI Material", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Lit.mat"},
  31. // {"Default-Line", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Lit.mat"},
  32. // {"Default-Skybox", "Packages/com.unity.render-pipelines.universal/Runtime/Materials/Lit.mat"},
  33. };
  34. }
  35. internal class ReadonlyMaterialConverter : RenderPipelineConverter
  36. {
  37. public override string name => "Readonly Material Converter";
  38. public override string info => "Converts references to Built-In readonly materials to URP readonly materials. This will create temporarily a .index file and that can take a long time.";
  39. public override Type container => typeof(BuiltInToURPConverterContainer);
  40. public override bool needsIndexing => true;
  41. List<string> guids = new List<string>();
  42. public override void OnInitialize(InitializeConverterContext ctx, Action callback)
  43. {
  44. Search.SearchService.Request
  45. (
  46. Search.SearchService.CreateContext("asset", "urp=convert-readonly a=URPConverterIndex"),
  47. (searchContext, items) =>
  48. {
  49. // we're going to do this step twice in order to get them ordered, but it should be fast
  50. var orderedRequest = items.OrderBy(req =>
  51. {
  52. GlobalObjectId.TryParse(req.id, out var gid);
  53. return gid.assetGUID;
  54. });
  55. foreach (var r in orderedRequest)
  56. {
  57. if (string.IsNullOrEmpty(r?.id) ||
  58. !GlobalObjectId.TryParse(r.id, out var gid))
  59. {
  60. continue;
  61. }
  62. var label = r.provider.fetchLabel(r, r.context);
  63. var description = r.provider.fetchDescription(r, r.context);
  64. var item = new ConverterItemDescriptor()
  65. {
  66. name = description.Split('/').Last().Split('.').First(),
  67. info = $"{label}",
  68. };
  69. guids.Add(gid.ToString());
  70. ctx.AddAssetToConvert(item);
  71. }
  72. callback.Invoke();
  73. searchContext?.Dispose();
  74. }
  75. );
  76. }
  77. public override void OnRun(ref RunItemContext ctx)
  78. {
  79. var obj = LoadObject(ref ctx);
  80. var result = true;
  81. var errorString = new StringBuilder();
  82. if (obj != null)
  83. {
  84. var materials = MaterialReferenceBuilder.GetMaterialsFromObject(obj);
  85. foreach (var material in materials)
  86. {
  87. if (material == null)
  88. {
  89. continue;
  90. }
  91. // there might be multiple materials on this object, we only care about the ones we explicitly try to remap that fail
  92. if (!MaterialReferenceBuilder.GetIsReadonlyMaterial(material)) continue;
  93. if (!ReadonlyMaterialMap.Map.ContainsKey(material.name)) continue;
  94. if (!ReassignMaterial(obj, material.name, ReadonlyMaterialMap.Map[material.name]))
  95. {
  96. result = false;
  97. errorString.AppendLine($"Material {material.name} failed to be reassigned");
  98. }
  99. }
  100. }
  101. else
  102. {
  103. result = false;
  104. errorString.AppendLine($"Object {ctx.item.descriptor.name} could not be loaded");
  105. }
  106. if (!result)
  107. {
  108. ctx.didFail = true;
  109. ctx.info = errorString.ToString();
  110. }
  111. else
  112. {
  113. // make sure the changes get saved
  114. EditorUtility.SetDirty(obj);
  115. var currentScene = SceneManager.GetActiveScene();
  116. EditorSceneManager.SaveScene(currentScene);
  117. }
  118. }
  119. public override void OnClicked(int index)
  120. {
  121. if (GlobalObjectId.TryParse(guids[index], out var gid))
  122. {
  123. var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID);
  124. EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(containerPath));
  125. }
  126. }
  127. private static bool ReassignMaterial(Object obj, string oldMaterialName, string newMaterialPath)
  128. {
  129. var result = true;
  130. // do the reflection to make sure we get the right material reference
  131. if (obj is GameObject go)
  132. {
  133. foreach (var key in MaterialReferenceBuilder.GetComponentTypes())
  134. {
  135. var components = go.GetComponentsInChildren(key);
  136. foreach (var component in components)
  137. {
  138. result &= ReassignMaterialOnComponentOrObject(component,
  139. oldMaterialName,
  140. newMaterialPath);
  141. }
  142. }
  143. }
  144. else
  145. {
  146. result &= ReassignMaterialOnComponentOrObject(obj,
  147. oldMaterialName,
  148. newMaterialPath);
  149. }
  150. return result;
  151. }
  152. private static bool ReassignMaterialOnComponentOrObject(Object obj,
  153. string oldMaterialName,
  154. string newMaterialPath)
  155. {
  156. var result = true;
  157. var materialProperties = obj.GetType().GetMaterialPropertiesWithoutLeaking();
  158. foreach (var property in materialProperties)
  159. {
  160. var materialValue = property.GetGetMethod().GetMaterialFromMethod(obj, (methodName, objectName) =>
  161. $"The method {methodName} was not found on {objectName}. Ignoring this property.");
  162. if (materialValue is Material material)
  163. {
  164. if (material.name.Equals(oldMaterialName, StringComparison.OrdinalIgnoreCase))
  165. {
  166. var newMaterial = AssetDatabase.LoadAssetAtPath<Material>(newMaterialPath);
  167. if (newMaterial != null)
  168. {
  169. var setMethod = property.GetSetMethod();
  170. if (setMethod != null)
  171. {
  172. setMethod.Invoke(obj, new object[] { newMaterial });
  173. }
  174. else
  175. {
  176. // failed to set the material from the SetMethod
  177. result = false;
  178. }
  179. }
  180. else
  181. {
  182. // a material we expected to exist does not
  183. result = false;
  184. }
  185. }
  186. }
  187. else if (materialValue is Material[] materialList)
  188. {
  189. for (int i = 0; i < materialList.Length; i++)
  190. {
  191. var mat = materialList[i];
  192. if (mat == null)
  193. {
  194. continue;
  195. }
  196. if (mat.name.Equals(oldMaterialName, StringComparison.OrdinalIgnoreCase))
  197. {
  198. var newMaterial = AssetDatabase.LoadAssetAtPath<Material>(newMaterialPath);
  199. if (newMaterial != null)
  200. {
  201. materialList[i] = newMaterial;
  202. }
  203. else
  204. {
  205. // a material we expected to exist does not
  206. result = false;
  207. }
  208. }
  209. }
  210. var setMethod = property.GetSetMethod();
  211. if (setMethod != null)
  212. {
  213. setMethod.Invoke(obj, new object[] { materialList });
  214. }
  215. else
  216. {
  217. // failed to set the material from the SetMethod
  218. result = false;
  219. }
  220. }
  221. }
  222. return result;
  223. }
  224. private Object LoadObject(ref RunItemContext ctx)
  225. {
  226. var item = ctx.item;
  227. var guid = guids[item.index];
  228. if (GlobalObjectId.TryParse(guid, out var gid))
  229. {
  230. var obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(gid);
  231. if (!obj)
  232. {
  233. // Open container scene
  234. if (gid.identifierType == (int)IdentifierType.kSceneObject)
  235. {
  236. var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID);
  237. var mainInstanceID = AssetDatabase.LoadAssetAtPath<Object>(containerPath);
  238. AssetDatabase.OpenAsset(mainInstanceID);
  239. // if we have a prefab open, then we already have the object we need to update
  240. var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
  241. if (prefabStage != null)
  242. {
  243. obj = mainInstanceID;
  244. }
  245. // Reload object if it is still null
  246. if (obj == null)
  247. {
  248. obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(gid);
  249. if (!obj)
  250. {
  251. ctx.didFail = true;
  252. ctx.info = $"Object {gid.assetGUID} failed to load...";
  253. }
  254. }
  255. }
  256. }
  257. return obj;
  258. }
  259. ctx.didFail = true;
  260. ctx.info = $"Failed to parse Global ID {item.descriptor.info}...";
  261. return null;
  262. }
  263. }
  264. }