EPOSetuper.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Text;
  7. using UnityEditor;
  8. using UnityEditor.PackageManager;
  9. using UnityEditor.PackageManager.Requests;
  10. using UnityEngine;
  11. using UnityEngine.Rendering;
  12. using UnityEngine.WSA;
  13. #if HDRP_OUTLINE
  14. using UnityEngine.Rendering.HighDefinition;
  15. #endif
  16. namespace EPOOutline
  17. {
  18. #if UNITY_2019_1_OR_NEWER
  19. public class EPOSetuper : EditorWindow
  20. {
  21. private static readonly string URP_OUTLINE_NAME = "URP_OUTLINE";
  22. private static readonly string HDRP_OUTLINE_NAME = "HDRP_OUTLINE";
  23. private static readonly string EPODOTweenName = "EPO_DOTWEEN";
  24. private static readonly string SRPShownID = "EasyPerformantOutlineWasShownAndCanceled";
  25. private static bool UPRWasFound = false;
  26. private static bool HDRPWasFound = false;
  27. private static ListRequest request;
  28. private static AddRequest addRequest;
  29. private Texture2D logoImage;
  30. [SerializeField]
  31. private SetupType setupType;
  32. public enum SetupType
  33. {
  34. BuiltIn,
  35. URP,
  36. HDRP
  37. }
  38. [SerializeField]
  39. private Vector2 scroll;
  40. public static bool ShouldShow
  41. {
  42. get
  43. {
  44. return PlayerPrefs.GetInt(SRPShownID, 0) == 0;
  45. }
  46. set
  47. {
  48. PlayerPrefs.SetInt(SRPShownID, value ? 0 : 1);
  49. }
  50. }
  51. private static List<BuildTargetGroup> GetApplicableGroups()
  52. {
  53. var groups = new List<BuildTargetGroup>();
  54. var type = typeof(BuildTargetGroup);
  55. var values = type.GetFields(BindingFlags.Public | BindingFlags.Static);
  56. foreach (var value in values)
  57. {
  58. if (value.GetCustomAttribute<ObsoleteAttribute>() != null)
  59. continue;
  60. var targetValue = (BuildTargetGroup) value.GetValue(null);
  61. if (targetValue == BuildTargetGroup.Unknown)
  62. continue;
  63. groups.Add(targetValue);
  64. }
  65. return groups;
  66. }
  67. private static bool CheckHasDefinition(string definition)
  68. {
  69. var targets = GetApplicableGroups();
  70. foreach (var buildTargetGroup in targets)
  71. {
  72. var definitions = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup);
  73. var splited = definitions.Split(';');
  74. if (Array.Find(splited, x => x == definition) == null)
  75. return false;
  76. }
  77. return true;
  78. }
  79. private static bool CheckHasURPOutlineDefinition()
  80. {
  81. return CheckHasDefinition(URP_OUTLINE_NAME);
  82. }
  83. private static bool CheckHasHDRPOutlineDefinition()
  84. {
  85. return CheckHasDefinition(HDRP_OUTLINE_NAME);
  86. }
  87. private static bool CheckHasEPODotween()
  88. {
  89. return CheckHasDefinition(EPODOTweenName);
  90. }
  91. #if URP_OUTLINE
  92. private static bool CheckShouldFixFeature()
  93. {
  94. var activeAssets = PipelineAssetUtility.ActiveAssets;
  95. foreach (var asset in activeAssets)
  96. {
  97. if (!PipelineAssetUtility.IsURPOrLWRP(asset))
  98. continue;
  99. if (!PipelineAssetUtility.IsAssetContainsSRPOutlineFeature(asset))
  100. return true;
  101. }
  102. return false;
  103. }
  104. #endif
  105. #if URP_OUTLINE || HDRP_OUTLINE
  106. private static bool CheckHasActiveRenderers()
  107. {
  108. return PipelineAssetUtility.ActiveAssets.Count > 0;
  109. }
  110. #endif
  111. private void OnEnable()
  112. {
  113. EditorApplication.update += Check;
  114. }
  115. private void OnDisable()
  116. {
  117. EditorApplication.update -= Check;
  118. }
  119. private static void RemoveDefinition(string definition, Func<bool> check)
  120. {
  121. if (!check())
  122. return;
  123. var group = EditorUserBuildSettings.selectedBuildTargetGroup;
  124. var definitions = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
  125. var splited = definitions.Split(';');
  126. var builder = new StringBuilder();
  127. var addedCount = 0;
  128. foreach (var item in splited)
  129. {
  130. if (item == definition)
  131. continue;
  132. builder.Append(item);
  133. builder.Append(';');
  134. addedCount++;
  135. }
  136. if (addedCount != 0)
  137. builder.Remove(builder.Length - 1, 1);
  138. PlayerSettings.SetScriptingDefineSymbolsForGroup(group, builder.ToString());
  139. }
  140. private static void AddURPDefinition()
  141. {
  142. AddDefinition(URP_OUTLINE_NAME, CheckHasURPOutlineDefinition);
  143. }
  144. private static void AddHDRPDefinition()
  145. {
  146. AddDefinition(HDRP_OUTLINE_NAME, CheckHasHDRPOutlineDefinition);
  147. }
  148. private static void RemoveDOTweenDefinition()
  149. {
  150. RemoveDefinition(EPODOTweenName, CheckHasEPODotween);
  151. }
  152. private static void AddDOTweenDefinition()
  153. {
  154. AddDefinition(EPODOTweenName, CheckHasEPODotween);
  155. }
  156. private static void AddDefinition(string definition, Func<bool> check)
  157. {
  158. if (check())
  159. return;
  160. var groups = GetApplicableGroups();
  161. foreach (var group in groups)
  162. {
  163. var definitions = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
  164. PlayerSettings.SetScriptingDefineSymbolsForGroup(group, definitions + ";" + definition);
  165. }
  166. }
  167. private static void Check()
  168. {
  169. if (EditorApplication.isPlaying)
  170. return;
  171. if (request == null || request.Error != null)
  172. {
  173. request = Client.List();
  174. return;
  175. }
  176. if (!request.IsCompleted)
  177. return;
  178. UPRWasFound = HasURPOrLWRP(request.Result);
  179. HDRPWasFound = HasHDRP(request.Result);
  180. request = Client.List();
  181. }
  182. private static bool HasHDRP(PackageCollection result)
  183. {
  184. return HasPackage(result, "com.unity.render-pipelines.high-definition");
  185. }
  186. private static bool HasURPOrLWRP(PackageCollection result)
  187. {
  188. var name =
  189. #if UNITY_2019_3_OR_NEWER
  190. "com.unity.render-pipelines.universal";
  191. #else
  192. "com.unity.render-pipelines.lightweight";
  193. #endif
  194. return HasPackage(result, name);
  195. }
  196. private static bool HasPackage(PackageCollection result, string packageName)
  197. {
  198. if (result == null)
  199. return false;
  200. var found = false;
  201. var name = packageName;
  202. foreach (var item in result)
  203. {
  204. if (item.name == name)
  205. {
  206. found = true;
  207. break;
  208. }
  209. }
  210. return found;
  211. }
  212. [MenuItem("Tools/Easy performant outline/Setup")]
  213. private static void ForceShowWindow()
  214. {
  215. ShouldShow = true;
  216. ShowWindow();
  217. }
  218. [MenuItem("Tools/Easy performant outline/Online docs")]
  219. private static void ShowDocs()
  220. {
  221. UnityEngine.Application.OpenURL("https://docs.google.com/document/d/17GvzvXNEjpEQ8DShRrVHKQ4I6s2tTVtwX6NzCZZ28AQ");
  222. }
  223. private static void ShowWindow()
  224. {
  225. if (!ShouldShow)
  226. return;
  227. var window = EditorWindow.GetWindow<EPOSetuper>(true, "EPO Setuper", false);
  228. window.maxSize = new Vector2(500, 500);
  229. window.minSize = new Vector2(500, 500);
  230. }
  231. public void OnGUI()
  232. {
  233. if (logoImage == null)
  234. logoImage = Resources.Load<Texture2D>("Easy performant outline/EP Outline logo");
  235. var height = 180;
  236. GUILayout.Space(height);
  237. var imagePosition = new Rect(Vector2.zero, new Vector2(position.width, height));
  238. GUI.DrawTexture(imagePosition, logoImage, ScaleMode.ScaleAndCrop, true);
  239. GUILayout.Space(10);
  240. scroll = GUILayout.BeginScrollView(scroll);
  241. if (EditorApplication.isPlaying)
  242. {
  243. EditorGUILayout.HelpBox("Please stop running the app to start setup process", MessageType.Info);
  244. if (GUILayout.Button("Stop"))
  245. EditorApplication.isPlaying = false;
  246. GUILayout.EndScrollView();
  247. return;
  248. }
  249. if (EditorApplication.isCompiling)
  250. {
  251. EditorGUILayout.HelpBox(new GUIContent("Compiling... please wait."));
  252. return;
  253. }
  254. EditorGUILayout.HelpBox("Warning!\n Don't add integrations that is not available in your project. This will lead to compilation errors", MessageType.Warning);
  255. EditorGUILayout.LabelField("Integrations");
  256. EditorGUI.indentLevel = 1;
  257. var shouldAddDotween = EditorGUILayout.Toggle(new GUIContent("DOTween support"), CheckHasEPODotween());
  258. if (shouldAddDotween)
  259. AddDOTweenDefinition();
  260. else
  261. RemoveDOTweenDefinition();
  262. EditorGUILayout.Space();
  263. EditorGUI.indentLevel = 0;
  264. setupType = (SetupType)EditorGUILayout.EnumPopup("Setup type", setupType);
  265. switch (setupType)
  266. {
  267. case SetupType.BuiltIn:
  268. DrawBuiltInSetup();
  269. break;
  270. case SetupType.URP:
  271. DrawURPSetup();
  272. break;
  273. case SetupType.HDRP:
  274. DrawHDRPSetup();
  275. break;
  276. }
  277. GUILayout.EndScrollView();
  278. }
  279. private void DrawBuiltInSetup()
  280. {
  281. EditorGUILayout.HelpBox(new GUIContent("There is no need in making any changes to work with Built-In renderer. Just add Outliner to camera and Outlinable to the object you wish to highlight"));
  282. }
  283. private void DrawHDRPSetup()
  284. {
  285. if (addRequest != null && !addRequest.IsCompleted)
  286. {
  287. EditorGUILayout.HelpBox(new GUIContent("Adding package..."));
  288. return;
  289. }
  290. var packageName = "com.unity.render-pipelines.high-definition";
  291. if (!HDRPWasFound)
  292. {
  293. EditorGUILayout.HelpBox(new GUIContent("There is no package added. Chick 'Add' to add the pipeline package."));
  294. if (GUILayout.Button("Add"))
  295. addRequest = Client.Add(packageName);
  296. return;
  297. }
  298. else
  299. EditorGUILayout.HelpBox(new GUIContent("Pipeline asset has been found in packages"));
  300. if (!CheckHasHDRPOutlineDefinition())
  301. {
  302. EditorGUILayout.HelpBox(new GUIContent("There is no HDRP_OUTLINE feature added. Click 'Add' to fix it."));
  303. if (GUILayout.Button("Add"))
  304. AddHDRPDefinition();
  305. }
  306. else
  307. EditorGUILayout.HelpBox(new GUIContent("HDRP_OUTLINE definition is added"));
  308. #if HDRP_OUTLINE
  309. if (!CheckHasActiveRenderers())
  310. {
  311. EditorGUILayout.HelpBox(new GUIContent("There is not renderer asset set up. Create one?"));
  312. if (GUILayout.Button("Create"))
  313. {
  314. var path = EditorUtility.SaveFilePanelInProject("Asset location", "Rendering asset", "asset", "Select the folder to save rendering asset");
  315. if (string.IsNullOrEmpty(path))
  316. {
  317. GUILayout.EndScrollView();
  318. return;
  319. }
  320. var pathNoExt = Path.ChangeExtension(path, string.Empty);
  321. pathNoExt = pathNoExt.Substring(0, pathNoExt.Length - 1);
  322. var asset = PipelineAssetUtility.CreateHDRPAsset();
  323. GraphicsSettings.renderPipelineAsset = asset;
  324. AssetDatabase.CreateAsset(asset, path);
  325. }
  326. }
  327. else
  328. EditorGUILayout.HelpBox(new GUIContent("At least one renderer asset is set up"));
  329. var volume = FindObjectOfType<CustomPassVolume>();
  330. if (volume == null)
  331. {
  332. EditorGUILayout.HelpBox(new GUIContent("There is no custom pass volume in the scene. Click Add to fix it."));
  333. if (GUILayout.Button("Add"))
  334. {
  335. var go = new GameObject("Custom volume");
  336. go.AddComponent<CustomPassVolume>();
  337. EditorUtility.SetDirty(go);
  338. }
  339. }
  340. else
  341. {
  342. EditorGUILayout.HelpBox(new GUIContent("The scene has custom pass volume."));
  343. if (volume.customPasses.Find(x => x is OutlineCustomPass) == null)
  344. {
  345. EditorGUILayout.HelpBox(new GUIContent("The volume doesn't have custom pass. Click Add to fix it."));
  346. if (GUILayout.Button("Add"))
  347. {
  348. volume.AddPassOfType(typeof(OutlineCustomPass));
  349. EditorUtility.SetDirty(volume);
  350. }
  351. }
  352. else
  353. EditorGUILayout.HelpBox(new GUIContent("The custom volume is set up"));
  354. }
  355. #endif
  356. }
  357. private void DrawURPSetup()
  358. {
  359. if (addRequest != null && !addRequest.IsCompleted)
  360. {
  361. EditorGUILayout.HelpBox(new GUIContent("Adding package..."));
  362. return;
  363. }
  364. var packageName =
  365. #if UNITY_2019_3_OR_NEWER
  366. "com.unity.render-pipelines.universal";
  367. #else
  368. "com.unity.render-pipelines.lightweight";
  369. #endif
  370. if (!UPRWasFound)
  371. {
  372. EditorGUILayout.HelpBox(new GUIContent("There is no package added. Chick 'Add' to add the pipeline package."));
  373. if (GUILayout.Button("Add"))
  374. addRequest = Client.Add(packageName);
  375. return;
  376. }
  377. else
  378. EditorGUILayout.HelpBox(new GUIContent("Pipeline asset has been found in packages"));
  379. if (!CheckHasURPOutlineDefinition())
  380. {
  381. EditorGUILayout.HelpBox(new GUIContent("There is no URP_OUTLINE feature added. Click 'Add' to fix it."));
  382. if (GUILayout.Button("Add"))
  383. AddURPDefinition();
  384. }
  385. else
  386. EditorGUILayout.HelpBox(new GUIContent("URP_OUTLINE definition is added"));
  387. #if URP_OUTLINE
  388. if (!CheckHasActiveRenderers())
  389. {
  390. EditorGUILayout.HelpBox(new GUIContent("There is not renderer asset set up. Create one?"));
  391. if (GUILayout.Button("Create"))
  392. {
  393. var path = EditorUtility.SaveFilePanelInProject("Asset location", "Rendering asset", "asset", "Select the folder to save rendering asset");
  394. if (string.IsNullOrEmpty(path))
  395. {
  396. GUILayout.EndScrollView();
  397. return;
  398. }
  399. var pathNoExt = Path.ChangeExtension(path, string.Empty);
  400. pathNoExt = pathNoExt.Substring(0, pathNoExt.Length - 1);
  401. var asset = PipelineAssetUtility.CreateAsset(pathNoExt);
  402. GraphicsSettings.renderPipelineAsset = asset;
  403. }
  404. }
  405. else
  406. EditorGUILayout.HelpBox(new GUIContent("At least one renderer asset is set up"));
  407. if (CheckShouldFixFeature())
  408. {
  409. var assets = PipelineAssetUtility.ActiveAssets;
  410. foreach (var asset in assets)
  411. {
  412. if (PipelineAssetUtility.IsAssetContainsSRPOutlineFeature(asset))
  413. continue;
  414. EditorGUI.indentLevel = 0;
  415. var text = string.Format("There is no outline feature added to the pipeline asset called '{0}'. Please add the feature:", asset.name);
  416. EditorGUILayout.HelpBox(new GUIContent(text));
  417. Editor previous = null;
  418. Editor.CreateCachedEditor(PipelineAssetUtility.GetRenderer(asset), null, ref previous);
  419. previous.OnInspectorGUI();
  420. }
  421. for (var index = 0; index < 10; index++)
  422. EditorGUILayout.Space();
  423. return;
  424. }
  425. else
  426. EditorGUILayout.HelpBox(new GUIContent("Feature is added for all renderers in use"));
  427. #endif
  428. }
  429. public void OnDestroy()
  430. {
  431. ShouldShow = false;
  432. }
  433. }
  434. #endif
  435. }