ObjectOutlineEditorUtils.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Reflection;
  4. using FlatKit;
  5. using JetBrains.Annotations;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using UnityEngine.Rendering;
  9. using UnityEngine.Rendering.Universal;
  10. public static class ObjectOutlineEditorUtils {
  11. private static readonly GUIStyle RichHelpBoxStyle = new(EditorStyles.helpBox) { richText = true };
  12. public static void SetActive(Material material, bool active) {
  13. var renderer = GetRenderer(Camera.current ?? Camera.main);
  14. if (renderer == null) {
  15. const string m = "<b>ScriptableRendererData</b> is required to enable per-object outlines.\n" +
  16. "Please assign a <b>URP Asset</b> in the Graphics settings.";
  17. EditorGUILayout.LabelField(m, RichHelpBoxStyle);
  18. return;
  19. }
  20. var features = GetRendererFeatures(renderer);
  21. if (features == null) {
  22. const string m = "<b>ScriptableRendererFeature</b> is required to enable per-object outlines.\n" +
  23. "Please assign a <b>URP Asset</b> in the Graphics settings.";
  24. EditorGUILayout.LabelField(m, RichHelpBoxStyle);
  25. return;
  26. }
  27. var feature = features.FirstOrDefault(f => f.GetType() == typeof(ObjectOutlineRendererFeature));
  28. if (feature == null) {
  29. if (active) {
  30. // Add the feature.
  31. var rendererData = GetRendererData();
  32. if (rendererData == null) return;
  33. feature = ScriptableObject.CreateInstance<ObjectOutlineRendererFeature>();
  34. feature.name = "Flat Kit Per Object Outline";
  35. AddRendererFeature(rendererData, feature);
  36. var m = $"<color=grey>[Flat Kit]</color> <b>Added</b> <color=green>{feature.name}</color> Renderer " +
  37. $"Feature to <b>{rendererData.name}</b>.";
  38. Debug.Log(m, rendererData);
  39. } else {
  40. // Feature not added and outline is disabled.
  41. return;
  42. }
  43. }
  44. // Register the material. This handles the feature activation.
  45. var outlineFeature = feature as ObjectOutlineRendererFeature;
  46. if (outlineFeature == null) {
  47. Debug.LogError("ObjectOutlineRendererFeature not found");
  48. return;
  49. }
  50. var featureIsUsed = outlineFeature.RegisterMaterial(material, active);
  51. // Remove the feature if no materials are using it.
  52. if (!featureIsUsed) {
  53. var rendererData = GetRendererData();
  54. if (rendererData == null) return;
  55. RemoveRendererFeature(rendererData, feature);
  56. var m = $"<color=grey>[Flat Kit]</color> <b>Removed</b> <color=green>{feature.name}</color> Renderer " +
  57. $"Feature from <b>{rendererData.name}</b> because no materials are using it.";
  58. Debug.Log(m, rendererData);
  59. }
  60. }
  61. private static void AddRendererFeature(ScriptableRendererData rendererData, ScriptableRendererFeature feature) {
  62. // Save the asset as a sub-asset.
  63. AssetDatabase.AddObjectToAsset(feature, rendererData);
  64. rendererData.rendererFeatures.Add(feature);
  65. rendererData.SetDirty();
  66. AssetDatabase.SaveAssets();
  67. AssetDatabase.Refresh();
  68. }
  69. private static void RemoveRendererFeature(ScriptableRendererData rendererData, ScriptableRendererFeature feature) {
  70. rendererData.rendererFeatures.Remove(feature);
  71. rendererData.SetDirty();
  72. // Remove the asset.
  73. AssetDatabase.RemoveObjectFromAsset(feature);
  74. AssetDatabase.SaveAssets();
  75. AssetDatabase.Refresh();
  76. }
  77. [CanBeNull]
  78. private static ScriptableRenderer GetRenderer(Camera camera) {
  79. if (!camera) {
  80. return null;
  81. }
  82. var additionalCameraData = camera.GetComponent<UniversalAdditionalCameraData>();
  83. if (!additionalCameraData) {
  84. return null;
  85. }
  86. var renderer = additionalCameraData.scriptableRenderer;
  87. return renderer;
  88. }
  89. private static List<ScriptableRendererFeature> GetRendererFeatures(ScriptableRenderer renderer) {
  90. var property =
  91. typeof(ScriptableRenderer).GetProperty("rendererFeatures", BindingFlags.NonPublic | BindingFlags.Instance);
  92. if (property == null) return null;
  93. var features = property.GetValue(renderer) as List<ScriptableRendererFeature>;
  94. return features;
  95. }
  96. internal static ScriptableRendererData GetRendererData() {
  97. #if UNITY_6000_0_OR_NEWER
  98. var srpAsset = GraphicsSettings.defaultRenderPipeline;
  99. #else
  100. var srpAsset = GraphicsSettings.renderPipelineAsset;
  101. #endif
  102. if (srpAsset == null) {
  103. const string m = "<b>Flat Kit</b> No SRP asset found. Please assign a URP Asset in the Graphics settings " +
  104. "to enable per-object outlines.";
  105. Debug.LogError(m);
  106. return null;
  107. }
  108. var field = typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList",
  109. BindingFlags.NonPublic | BindingFlags.Instance);
  110. var rendererDataList = (ScriptableRendererData[])field!.GetValue(srpAsset);
  111. var rendererData = rendererDataList.FirstOrDefault();
  112. if (rendererData == null) {
  113. Debug.LogError("No ScriptableRendererData found");
  114. return null;
  115. }
  116. return rendererData;
  117. }
  118. }