EditorExtensions.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. #if UNITY_EDITOR
  2. using UnityEngine;
  3. using UnityEditor;
  4. namespace VLB
  5. {
  6. public static class EditorExtensions
  7. {
  8. public static GameObject NewBeam()
  9. {
  10. return new GameObject("Volumetric Light Beam", typeof(VolumetricLightBeam));
  11. }
  12. public static GameObject NewBeam2D()
  13. {
  14. var gao = new GameObject("Volumetric Light Beam (2D)", typeof(VolumetricLightBeam));
  15. gao.GetComponent<VolumetricLightBeam>().dimensions = Dimensions.Dim2D;
  16. return gao;
  17. }
  18. public static GameObject NewBeamAndDust()
  19. {
  20. return new GameObject("Volumetric Light Beam + Dust", typeof(VolumetricLightBeam), typeof(VolumetricDustParticles));
  21. }
  22. public static GameObject NewSpotLightAndBeam()
  23. {
  24. var light = Utils.NewWithComponent<Light>("Spotlight and Beam");
  25. light.type = LightType.Spot;
  26. var gao = light.gameObject;
  27. gao.AddComponent<VolumetricLightBeam>();
  28. return gao;
  29. }
  30. static void OnNewGameObjectCreated(GameObject gao)
  31. {
  32. if (Selection.activeGameObject)
  33. gao.transform.SetParent(Selection.activeGameObject.transform);
  34. Selection.activeGameObject = gao;
  35. }
  36. [MenuItem("GameObject/Light/Volumetric Beam (3D)", false, 100)]
  37. public static void Menu_CreateNewBeam()
  38. {
  39. OnNewGameObjectCreated(NewBeam());
  40. }
  41. [MenuItem("GameObject/Light/Volumetric Beam (3D) and Spotlight", false, 101)]
  42. public static void Menu_CreateSpotLightAndBeam()
  43. {
  44. OnNewGameObjectCreated(NewSpotLightAndBeam());
  45. }
  46. [MenuItem("GameObject/Light/Volumetric Beam (2D)", false, 102)]
  47. public static void Menu_CreateNewBeam2D()
  48. {
  49. OnNewGameObjectCreated(NewBeam2D());
  50. }
  51. [MenuItem("CONTEXT/Light/Attach a Volumetric Beam")]
  52. public static void Menu_AttachBeam(MenuCommand menuCommand)
  53. {
  54. var light = menuCommand.context as Light;
  55. if (light)
  56. {
  57. if (light.GetComponent<VolumetricLightBeam>() == null)
  58. Undo.AddComponent<VolumetricLightBeam>(light.gameObject);
  59. }
  60. }
  61. [MenuItem("CONTEXT/VolumetricLightBeam/Documentation")]
  62. public static void Menu_Beam_Doc(MenuCommand menuCommand) { Application.OpenURL(Consts.HelpUrlBeam); }
  63. [MenuItem("CONTEXT/VolumetricDustParticles/Documentation")]
  64. public static void Menu_DustParticles_Doc(MenuCommand menuCommand) { Application.OpenURL(Consts.HelpUrlDustParticles); }
  65. [MenuItem("CONTEXT/DynamicOcclusionRaycasting/Documentation")]
  66. public static void Menu_DynamicOcclusionRaycasting_Doc(MenuCommand menuCommand) { Application.OpenURL(Consts.HelpUrlDynamicOcclusionRaycasting); }
  67. [MenuItem("CONTEXT/DynamicOcclusionDepthBuffer/Documentation")]
  68. public static void Menu_DynamicOcclusionDepthBuffer_Doc(MenuCommand menuCommand) { Application.OpenURL(Consts.HelpUrlDynamicOcclusionDepthBuffer); }
  69. [MenuItem("CONTEXT/TriggerZone/Documentation")]
  70. public static void Menu_TriggerZone_Doc(MenuCommand menuCommand) { Application.OpenURL(Consts.HelpUrlTriggerZone); }
  71. [MenuItem("CONTEXT/Config/Documentation")]
  72. public static void Menu_Config_Doc(MenuCommand menuCommand) { Application.OpenURL(Consts.HelpUrlConfig); }
  73. [MenuItem("CONTEXT/VolumetricLightBeam/Open Global Config")]
  74. [MenuItem("CONTEXT/VolumetricDustParticles/Open Global Config")]
  75. [MenuItem("CONTEXT/DynamicOcclusionRaycasting/Open Global Config")]
  76. [MenuItem("CONTEXT/DynamicOcclusionDepthBuffer/Open Global Config")]
  77. [MenuItem("CONTEXT/TriggerZone/Open Global Config")]
  78. public static void Menu_Beam_Config(MenuCommand menuCommand) { Config.EditorSelectInstance(); }
  79. [MenuItem("CONTEXT/VolumetricLightBeam/Add Dust Particles")]
  80. public static void Menu_AddDustParticles(MenuCommand menuCommand) { AddComponentFromEditor<VolumetricDustParticles>(menuCommand.context as VolumetricLightBeam); }
  81. [MenuItem("CONTEXT/VolumetricLightBeam/Add Dynamic Occlusion (Raycasting)")]
  82. public static void Menu_AddDynamicOcclusion_Raycasting(MenuCommand menuCommand) { AddComponentFromEditor<DynamicOcclusionRaycasting>(menuCommand.context as VolumetricLightBeam); }
  83. [MenuItem("CONTEXT/VolumetricLightBeam/Add Dynamic Occlusion (Depth Buffer)")]
  84. public static void Menu_AddDynamicOcclusion_DepthBuffer(MenuCommand menuCommand) { AddComponentFromEditor<DynamicOcclusionDepthBuffer>(menuCommand.context as VolumetricLightBeam); }
  85. [MenuItem("CONTEXT/VolumetricLightBeam/Add Trigger Zone")]
  86. public static void Menu_AddTriggerZone(MenuCommand menuCommand) { AddComponentFromEditor<TriggerZone>(menuCommand.context as VolumetricLightBeam); }
  87. public static void AddComponentFromEditor<TComp>(VolumetricLightBeam self) where TComp : Component
  88. {
  89. if (self)
  90. {
  91. if (self.GetComponent<TComp>() == null)
  92. {
  93. var comp = Undo.AddComponent<TComp>(self.gameObject);
  94. if (comp is DynamicOcclusionRaycasting) (comp as DynamicOcclusionRaycasting).dimensions = self.dimensions;
  95. }
  96. }
  97. }
  98. [MenuItem("Edit/Volumetric Light Beam Config", false, 20001)]
  99. public static void Menu_EditOpenConfig()
  100. {
  101. Config.EditorSelectInstance();
  102. }
  103. public static void HorizontalLineSeparator()
  104. {
  105. EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
  106. }
  107. /// <summary>
  108. /// Add a EditorGUILayout.ToggleLeft which properly handles multi-object editing
  109. /// </summary>
  110. public static void ToggleLeft(this SerializedProperty prop, GUIContent label, params GUILayoutOption[] options)
  111. {
  112. ToggleLeft(prop, label, prop.boolValue, options);
  113. }
  114. public static void ToggleLeft(this SerializedProperty prop, GUIContent label, bool forcedValue, params GUILayoutOption[] options)
  115. {
  116. EditorGUI.BeginChangeCheck();
  117. EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
  118. var newValue = EditorGUILayout.ToggleLeft(label, forcedValue, options);
  119. EditorGUI.showMixedValue = false;
  120. if (EditorGUI.EndChangeCheck())
  121. prop.boolValue = newValue;
  122. }
  123. public static bool HasAtLeastOneValue(this SerializedProperty prop, bool value)
  124. {
  125. return (prop.boolValue == value) || prop.hasMultipleDifferentValues;
  126. }
  127. /// <summary>
  128. /// Create a EditorGUILayout.Slider which properly handles multi-object editing
  129. /// We apply the 'convIn' conversion to the SerializedProperty value before exposing it as a Slider.
  130. /// We apply the 'convOut' conversion to the Slider value to get the SerializedProperty back.
  131. /// </summary>
  132. /// <param name="prop">The value the slider shows.</param>
  133. /// <param name="label">Label in front of the slider.</param>
  134. /// <param name="leftValue">The value at the left end of the slider.</param>
  135. /// <param name="rightValue">The value at the right end of the slider.</param>
  136. /// <param name="convIn">Conversion applied on the SerializedProperty to get the Slider value</param>
  137. /// <param name="convOut">Conversion applied on the Slider value to get the SerializedProperty</param>
  138. public static bool FloatSlider(
  139. this SerializedProperty prop,
  140. GUIContent label,
  141. float leftValue, float rightValue,
  142. System.Func<float, float> convIn,
  143. System.Func<float, float> convOut,
  144. params GUILayoutOption[] options)
  145. {
  146. var floatValue = convIn(prop.floatValue);
  147. EditorGUI.BeginChangeCheck();
  148. {
  149. EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
  150. {
  151. floatValue = EditorGUILayout.Slider(label, floatValue, leftValue, rightValue, options);
  152. }
  153. EditorGUI.showMixedValue = false;
  154. }
  155. if (EditorGUI.EndChangeCheck())
  156. {
  157. prop.floatValue = convOut(floatValue);
  158. return true;
  159. }
  160. return false;
  161. }
  162. public static bool FloatSlider(
  163. this SerializedProperty prop,
  164. GUIContent label,
  165. float leftValue, float rightValue,
  166. params GUILayoutOption[] options)
  167. {
  168. var floatValue = prop.floatValue;
  169. EditorGUI.BeginChangeCheck();
  170. {
  171. EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
  172. {
  173. floatValue = EditorGUILayout.Slider(label, floatValue, leftValue, rightValue, options);
  174. }
  175. EditorGUI.showMixedValue = false;
  176. }
  177. if (EditorGUI.EndChangeCheck())
  178. {
  179. prop.floatValue = floatValue;
  180. return true;
  181. }
  182. return false;
  183. }
  184. /*
  185. public static void ToggleFromLight(this SerializedProperty prop)
  186. {
  187. ToggleLeft(
  188. prop,
  189. new GUIContent("From Spot", "Get the value from the Light Spot"),
  190. GUILayout.MaxWidth(80.0f));
  191. }
  192. */
  193. public static void ToggleUseGlobalNoise(this SerializedProperty prop)
  194. {
  195. ToggleLeft(
  196. prop,
  197. new GUIContent("Global", "Get the value from the Global 3D Noise"),
  198. GUILayout.MaxWidth(55.0f));
  199. }
  200. public static void CustomEnum<EnumType>(this SerializedProperty prop, GUIContent content, GUIContent[] descriptions)
  201. {
  202. Debug.Assert(System.Enum.GetNames(typeof(EnumType)).Length == descriptions.Length, string.Format("Enum '{0}' and the description array don't have the same size", typeof(EnumType)));
  203. int enumValueIndex = prop.enumValueIndex;
  204. EditorGUI.BeginChangeCheck();
  205. {
  206. EditorGUI.showMixedValue = prop.hasMultipleDifferentValues;
  207. {
  208. enumValueIndex = EditorGUILayout.Popup(content, enumValueIndex, descriptions);
  209. }
  210. EditorGUI.showMixedValue = false;
  211. }
  212. if (EditorGUI.EndChangeCheck())
  213. prop.enumValueIndex = enumValueIndex;
  214. }
  215. }
  216. }
  217. #endif