Ei kuvausta
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.

VolumeComponent.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Linq;
  5. using System.Reflection;
  6. namespace UnityEngine.Rendering
  7. {
  8. /// <summary>
  9. /// This attribute allows you to add commands to the <b>Add Override</b> popup menu
  10. /// on Volumes.
  11. /// To filter VolumeComponentMenu based on current Render Pipeline, add SupportedOnRenderPipeline attribute to the class alongside with this attribute.
  12. /// </summary>
  13. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
  14. public class VolumeComponentMenu : Attribute
  15. {
  16. /// <summary>
  17. /// The name of the entry in the override list. You can use slashes to create sub-menus.
  18. /// </summary>
  19. public readonly string menu;
  20. // TODO: Add support for component icons
  21. /// <summary>
  22. /// Creates a new <see cref="VolumeComponentMenu"/> instance.
  23. /// </summary>
  24. /// <param name="menu">The name of the entry in the override list. You can use slashes to
  25. /// create sub-menus.</param>
  26. public VolumeComponentMenu(string menu)
  27. {
  28. this.menu = menu;
  29. }
  30. }
  31. /// <summary>
  32. /// This attribute allows you to add commands to the <b>Add Override</b> popup menu
  33. /// on Volumes and specify for which render pipelines will be supported
  34. /// </summary>
  35. [Obsolete(@"VolumeComponentMenuForRenderPipelineAttribute is deprecated. Use VolumeComponentMenu with SupportedOnCurrentPipeline instead. #from(2023.1)", false)]
  36. public class VolumeComponentMenuForRenderPipeline : VolumeComponentMenu
  37. {
  38. /// <summary>
  39. /// The list of pipeline types that the target class supports
  40. /// </summary>
  41. public Type[] pipelineTypes { get; }
  42. /// <summary>
  43. /// Creates a new <see cref="VolumeComponentMenuForRenderPipeline"/> instance.
  44. /// </summary>
  45. /// <param name="menu">The name of the entry in the override list. You can use slashes to
  46. /// create sub-menus.</param>
  47. /// <param name="pipelineTypes">The list of pipeline types that the target class supports</param>
  48. public VolumeComponentMenuForRenderPipeline(string menu, params Type[] pipelineTypes)
  49. : base(menu)
  50. {
  51. if (pipelineTypes == null)
  52. throw new Exception("Specify a list of supported pipeline");
  53. // Make sure that we only allow the class types that inherit from the render pipeline
  54. foreach (var t in pipelineTypes)
  55. {
  56. if (!typeof(RenderPipeline).IsAssignableFrom(t))
  57. throw new Exception(
  58. $"You can only specify types that inherit from {typeof(RenderPipeline)}, please check {t}");
  59. }
  60. this.pipelineTypes = pipelineTypes;
  61. }
  62. }
  63. /// <summary>
  64. /// An attribute to hide the volume component to be added through `Add Override` button on the volume component list
  65. /// </summary>
  66. [AttributeUsage(AttributeTargets.Class)]
  67. [Obsolete("VolumeComponentDeprecated has been deprecated (UnityUpgradable) -> [UnityEngine] UnityEngine.HideInInspector", false)]
  68. public sealed class VolumeComponentDeprecated : Attribute
  69. {
  70. }
  71. /// <summary>
  72. /// The base class for all the components that can be part of a <see cref="VolumeProfile"/>.
  73. /// The Volume framework automatically handles and interpolates any <see cref="VolumeParameter"/> members found in this class.
  74. /// </summary>
  75. /// <example>
  76. /// <code>
  77. /// using UnityEngine.Rendering;
  78. ///
  79. /// [Serializable, VolumeComponentMenuForRenderPipeline("Custom/Example Component")]
  80. /// public class ExampleComponent : VolumeComponent
  81. /// {
  82. /// public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
  83. /// }
  84. /// </code>
  85. /// </example>
  86. [Serializable]
  87. public partial class VolumeComponent : ScriptableObject
  88. {
  89. /// <summary>
  90. /// Local attribute for VolumeComponent fields only.
  91. /// It handles relative indentation of a property for inspector.
  92. /// </summary>
  93. public sealed class Indent : PropertyAttribute
  94. {
  95. /// <summary> Relative indent amount registered in this attribute </summary>
  96. public readonly int relativeAmount;
  97. /// <summary> Constructor </summary>
  98. /// <param name="relativeAmount">Relative indent change to use</param>
  99. public Indent(int relativeAmount = 1)
  100. => this.relativeAmount = relativeAmount;
  101. }
  102. /// <summary>
  103. /// The active state of the set of parameters defined in this class. You can use this to
  104. /// quickly turn on or off all the overrides at once.
  105. /// </summary>
  106. public bool active = true;
  107. /// <summary>
  108. /// The name displayed in the component header. If you do not set a name, Unity generates one from
  109. /// the class name automatically.
  110. /// </summary>
  111. public string displayName { get; protected set; } = "";
  112. /// <summary>
  113. /// The backing storage of <see cref="parameters"/>. Use this for performance-critical work.
  114. /// </summary>
  115. internal readonly List<VolumeParameter> parameterList = new();
  116. ReadOnlyCollection<VolumeParameter> m_ParameterReadOnlyCollection;
  117. /// <summary>
  118. /// A read-only collection of all the <see cref="VolumeParameter"/>s defined in this class.
  119. /// </summary>
  120. public ReadOnlyCollection<VolumeParameter> parameters
  121. {
  122. get
  123. {
  124. if (m_ParameterReadOnlyCollection == null)
  125. m_ParameterReadOnlyCollection = parameterList.AsReadOnly();
  126. return m_ParameterReadOnlyCollection;
  127. }
  128. }
  129. /// <summary>
  130. /// Extracts all the <see cref="VolumeParameter"/>s defined in this class and nested classes.
  131. /// </summary>
  132. /// <param name="o">The object to find the parameters</param>
  133. /// <param name="parameters">The list filled with the parameters.</param>
  134. /// <param name="filter">If you want to filter the parameters</param>
  135. internal static void FindParameters(object o, List<VolumeParameter> parameters, Func<FieldInfo, bool> filter = null)
  136. {
  137. if (o == null)
  138. return;
  139. var fields = o.GetType()
  140. .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  141. .OrderBy(t => t.MetadataToken); // Guaranteed order
  142. foreach (var field in fields)
  143. {
  144. if (field.FieldType.IsSubclassOf(typeof(VolumeParameter)))
  145. {
  146. if (filter?.Invoke(field) ?? true)
  147. {
  148. VolumeParameter volumeParameter = (VolumeParameter)field.GetValue(o);
  149. parameters.Add(volumeParameter);
  150. }
  151. }
  152. else if (!field.FieldType.IsArray && field.FieldType.IsClass)
  153. FindParameters(field.GetValue(o), parameters, filter);
  154. }
  155. }
  156. /// <summary>
  157. /// Unity calls this method when it loads the class.
  158. /// </summary>
  159. /// <remarks>
  160. /// If you want to override this method, you must call <c>base.OnEnable()</c>.
  161. /// </remarks>
  162. protected virtual void OnEnable()
  163. {
  164. // Automatically grab all fields of type VolumeParameter for this instance
  165. parameterList.Clear();
  166. FindParameters(this, parameterList);
  167. foreach (var parameter in parameterList)
  168. {
  169. if (parameter != null)
  170. parameter.OnEnable();
  171. else
  172. Debug.LogWarning("Volume Component " + GetType().Name + " contains a null parameter; please make sure all parameters are initialized to a default value. Until this is fixed the null parameters will not be considered by the system.");
  173. }
  174. }
  175. /// <summary>
  176. /// Unity calls this method when the object goes out of scope.
  177. /// </summary>
  178. protected virtual void OnDisable()
  179. {
  180. foreach (var parameter in parameterList)
  181. {
  182. if (parameter != null)
  183. parameter.OnDisable();
  184. }
  185. }
  186. /// <summary>
  187. /// Interpolates a <see cref="VolumeComponent"/> with this component by an interpolation
  188. /// factor and puts the result back into the given <see cref="VolumeComponent"/>.
  189. /// </summary>
  190. /// <remarks>
  191. /// You can override this method to do your own blending. Either loop through the
  192. /// <see cref="parameters"/> list or reference direct fields. You should only use
  193. /// <see cref="VolumeParameter.SetValue"/> to set parameter values and not assign
  194. /// directly to the state object. you should also manually check
  195. /// <see cref="VolumeParameter.overrideState"/> before you set any values.
  196. /// </remarks>
  197. /// <param name="state">The internal component to interpolate from. You must store
  198. /// the result of the interpolation in this same component.</param>
  199. /// <param name="interpFactor">The interpolation factor in range [0,1].</param>
  200. /// <example>
  201. /// <para> Below is the default implementation for blending:</para>
  202. /// <code>
  203. /// public virtual void Override(VolumeComponent state, float interpFactor)
  204. /// {
  205. /// int count = parameters.Count;
  206. ///
  207. /// for (int i = 0; i &lt; count; i++)
  208. /// {
  209. /// var stateParam = state.parameters[i];
  210. /// var toParam = parameters[i];
  211. ///
  212. /// if (toParam.overrideState)
  213. /// {
  214. /// // Keep track of the override state to ensure that state will be reset on next frame (and for debugging purpose)
  215. /// stateParam.overrideState = toParam.overrideState;
  216. /// stateParam.Interp(stateParam, toParam, interpFactor);
  217. /// }
  218. /// }
  219. /// }
  220. /// </code>
  221. /// </example>
  222. public virtual void Override(VolumeComponent state, float interpFactor)
  223. {
  224. int count = parameterList.Count;
  225. for (int i = 0; i < count; i++)
  226. {
  227. var stateParam = state.parameterList[i];
  228. var toParam = parameterList[i];
  229. if (toParam.overrideState)
  230. {
  231. // Keep track of the override state to ensure that state will be reset on next frame (and for debugging purpose)
  232. stateParam.overrideState = toParam.overrideState;
  233. stateParam.Interp(stateParam, toParam, interpFactor);
  234. }
  235. }
  236. }
  237. /// <summary>
  238. /// Sets the state of all the overrides on this component to a given value.
  239. /// </summary>
  240. /// <param name="state">The value to set the state of the overrides to.</param>
  241. public void SetAllOverridesTo(bool state)
  242. {
  243. SetOverridesTo(parameterList, state);
  244. }
  245. /// <summary>
  246. /// Sets the override state of the given parameters on this component to a given value.
  247. /// </summary>
  248. /// <param name="state">The value to set the state of the overrides to.</param>
  249. internal void SetOverridesTo(IEnumerable<VolumeParameter> enumerable, bool state)
  250. {
  251. foreach (var prop in enumerable)
  252. {
  253. prop.overrideState = state;
  254. var t = prop.GetType();
  255. if (VolumeParameter.IsObjectParameter(t))
  256. {
  257. // This method won't be called a lot but this is sub-optimal, fix me
  258. var innerParams = (ReadOnlyCollection<VolumeParameter>)
  259. t.GetProperty("parameters", BindingFlags.NonPublic | BindingFlags.Instance)
  260. .GetValue(prop, null);
  261. if (innerParams != null)
  262. SetOverridesTo(innerParams, state);
  263. }
  264. }
  265. }
  266. /// <summary>
  267. /// A custom hashing function that Unity uses to compare the state of parameters.
  268. /// </summary>
  269. /// <returns>A computed hash code for the current instance.</returns>
  270. public override int GetHashCode()
  271. {
  272. unchecked
  273. {
  274. //return parameters.Aggregate(17, (i, p) => i * 23 + p.GetHash());
  275. int hash = 17;
  276. for (int i = 0; i < parameterList.Count; i++)
  277. hash = hash * 23 + parameterList[i].GetHashCode();
  278. return hash;
  279. }
  280. }
  281. /// <summary>
  282. /// Returns true if any of the volume properites has been overridden.
  283. /// </summary>
  284. /// <returns>True if any of the volume properites has been overridden.</returns>
  285. public bool AnyPropertiesIsOverridden()
  286. {
  287. for (int i = 0; i < parameterList.Count; ++i)
  288. {
  289. if (parameterList[i].overrideState) return true;
  290. }
  291. return false;
  292. }
  293. /// <summary>
  294. /// Unity calls this method before the object is destroyed.
  295. /// </summary>
  296. protected virtual void OnDestroy() => Release();
  297. /// <summary>
  298. /// Releases all the allocated resources.
  299. /// </summary>
  300. public void Release()
  301. {
  302. if (parameterList == null)
  303. return;
  304. for (int i = 0; i < parameterList.Count; i++)
  305. {
  306. if (parameterList[i] != null)
  307. parameterList[i].Release();
  308. }
  309. }
  310. }
  311. }