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