A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityEditor;
4using UnityEngine.Assertions;
5
6namespace UnityEngine.Rendering
7{
8 /// <summary>
9 /// An Asset which holds a set of settings to use with a <see cref="Volume"/>.
10 /// </summary>
11 [CurrentPipelineHelpURL("Volume-Profile")]
12 [Icon("Packages/com.unity.render-pipelines.core/Editor/Icons/Processed/VolumeProfile Icon.asset")]
13 public sealed class VolumeProfile : ScriptableObject
14 {
15 /// <summary>
16 /// A list of every setting that this Volume Profile stores.
17 /// </summary>
18 public List<VolumeComponent> components = new List<VolumeComponent>();
19
20 /// <summary>
21 /// **Note**: For Internal Use Only<br/>
22 /// A dirty check used to redraw the profile inspector when something has changed. This is
23 /// currently only used in the editor.
24 /// </summary>
25 [NonSerialized]
26 public bool isDirty = true; // Editor only, doesn't have any use outside of it
27
28 void OnEnable()
29 {
30 // Make sure every setting is valid. If a profile holds a script that doesn't exist
31 // anymore, nuke it to keep the volume clean. Note that if you delete a script that is
32 // currently in use in a volume you'll still get a one-time error in the console, it's
33 // harmless and happens because Unity does a redraw of the editor (and thus the current
34 // frame) before the recompilation step.
35 components.RemoveAll(x => x == null);
36 }
37
38 // The lifetime of ScriptableObjects is different from MonoBehaviours. When the last reference to a
39 // VolumeProfile goes out of scope (e.g. when a scene containing Volume components is unloaded), Unity will call
40 // OnDisable() on the VolumeProfile. We need to release the internal resources in this case to avoid leaks.
41 internal void OnDisable()
42 {
43 if (components == null)
44 return;
45
46 for (int i = 0; i < components.Count; i++)
47 {
48 if (components[i] != null)
49 components[i].Release();
50 }
51 }
52
53 /// <summary>
54 /// Resets the dirty state of the Volume Profile. Unity uses this to force-refresh and redraw the
55 /// Volume Profile editor when you modify the Asset via script instead of the Inspector.
56 /// </summary>
57 public void Reset()
58 {
59 isDirty = true;
60 }
61
62 /// <summary>
63 /// Adds a <see cref="VolumeComponent"/> to this Volume Profile.
64 /// </summary>
65 /// <remarks>
66 /// You can only have a single component of the same type per Volume Profile.
67 /// </remarks>
68 /// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
69 /// <param name="overrides">Specifies whether Unity should automatically override all the settings when
70 /// you add a <see cref="VolumeComponent"/> to the Volume Profile.</param>
71 /// <returns>The instance for the given type that you added to the Volume Profile</returns>
72 /// <seealso cref="Add"/>
73 public T Add<T>(bool overrides = false)
74 where T : VolumeComponent
75 {
76 return (T)Add(typeof(T), overrides);
77 }
78
79 /// <summary>
80 /// Adds a <see cref="VolumeComponent"/> to this Volume Profile.
81 /// </summary>
82 /// <remarks>
83 /// You can only have a single component of the same type per Volume Profile.
84 /// </remarks>
85 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
86 /// <param name="overrides">Specifies whether Unity should automatically override all the settings when
87 /// you add a <see cref="VolumeComponent"/> to the Volume Profile.</param>
88 /// <returns>The instance created for the given type that has been added to the profile</returns>
89 /// <seealso cref="Add{T}"/>
90 public VolumeComponent Add(Type type, bool overrides = false)
91 {
92 if (Has(type))
93 throw new InvalidOperationException("Component already exists in the volume");
94
95 var component = (VolumeComponent)CreateInstance(type);
96#if UNITY_EDITOR
97 component.hideFlags = HideFlags.HideInInspector | HideFlags.HideInHierarchy;
98 component.name = type.Name;
99#endif
100 component.SetAllOverridesTo(overrides);
101 components.Add(component);
102 isDirty = true;
103 return component;
104 }
105
106 /// <summary>
107 /// Removes a <see cref="VolumeComponent"/> from this Volume Profile.
108 /// </summary>
109 /// <remarks>
110 /// This method does nothing if the type does not exist in the Volume Profile.
111 /// </remarks>
112 /// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
113 /// <seealso cref="Remove"/>
114 public void Remove<T>()
115 where T : VolumeComponent
116 {
117 Remove(typeof(T));
118 }
119
120 /// <summary>
121 /// Removes a <see cref="VolumeComponent"/> from this Volume Profile.
122 /// </summary>
123 /// <remarks>
124 /// This method does nothing if the type does not exist in the Volume Profile.
125 /// </remarks>
126 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
127 /// <seealso cref="Remove{T}"/>
128 public void Remove(Type type)
129 {
130 int toRemove = -1;
131
132 for (int i = 0; i < components.Count; i++)
133 {
134 if (components[i].GetType() == type)
135 {
136 toRemove = i;
137 break;
138 }
139 }
140
141 if (toRemove >= 0)
142 {
143 components.RemoveAt(toRemove);
144 isDirty = true;
145 }
146 }
147
148 /// <summary>
149 /// Checks if this Volume Profile contains the <see cref="VolumeComponent"/> you pass in.
150 /// </summary>
151 /// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
152 /// <returns><c>true</c> if the <see cref="VolumeComponent"/> exists in the Volume Profile,
153 /// <c>false</c> otherwise.</returns>
154 /// <seealso cref="Has"/>
155 /// <seealso cref="HasSubclassOf"/>
156 public bool Has<T>()
157 where T : VolumeComponent
158 {
159 return Has(typeof(T));
160 }
161
162 /// <summary>
163 /// Checks if this Volume Profile contains the <see cref="VolumeComponent"/> you pass in.
164 /// </summary>
165 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
166 /// <returns><c>true</c> if the <see cref="VolumeComponent"/> exists in the Volume Profile,
167 /// <c>false</c> otherwise.</returns>
168 /// <seealso cref="Has{T}"/>
169 /// <seealso cref="HasSubclassOf"/>
170 public bool Has(Type type)
171 {
172 foreach (var component in components)
173 {
174 if (component.GetType() == type)
175 return true;
176 }
177
178 return false;
179 }
180
181 /// <summary>
182 /// Checks if this Volume Profile contains the <see cref="VolumeComponent"/>, which is a subclass of <paramref name="type"/>,
183 /// that you pass in.
184 /// </summary>
185 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
186 /// <returns><c>true</c> if the <see cref="VolumeComponent"/> exists in the Volume Profile,
187 /// <c>false</c> otherwise.</returns>
188 /// <seealso cref="Has"/>
189 /// <seealso cref="Has{T}"/>
190 public bool HasSubclassOf(Type type)
191 {
192 foreach (var component in components)
193 {
194 if (component.GetType().IsSubclassOf(type))
195 return true;
196 }
197
198 return false;
199 }
200
201 /// <summary>
202 /// Gets the <see cref="VolumeComponent"/> of the specified type, if it exists.
203 /// </summary>
204 /// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
205 /// <param name="component">The output argument that contains the <see cref="VolumeComponent"/>
206 /// or <c>null</c>.</param>
207 /// <returns><c>true</c> if the <see cref="VolumeComponent"/> is in the Volume Profile,
208 /// <c>false</c> otherwise.</returns>
209 /// <seealso cref="TryGet{T}(Type, out T)"/>
210 /// <seealso cref="TryGetSubclassOf{T}"/>
211 /// <seealso cref="TryGetAllSubclassOf{T}"/>
212 public bool TryGet<T>(out T component)
213 where T : VolumeComponent
214 {
215 return TryGet(typeof(T), out component);
216 }
217
218 /// <summary>
219 /// Gets the <see cref="VolumeComponent"/> of the specified type, if it exists.
220 /// </summary>
221 /// <typeparam name="T">A type of <see cref="VolumeComponent"/></typeparam>
222 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
223 /// <param name="component">The output argument that contains the <see cref="VolumeComponent"/>
224 /// or <c>null</c>.</param>
225 /// <returns><c>true</c> if the <see cref="VolumeComponent"/> is in the Volume Profile,
226 /// <c>false</c> otherwise.</returns>
227 /// <seealso cref="TryGet{T}(out T)"/>
228 /// <seealso cref="TryGetSubclassOf{T}"/>
229 /// <seealso cref="TryGetAllSubclassOf{T}"/>
230 public bool TryGet<T>(Type type, out T component)
231 where T : VolumeComponent
232 {
233 component = null;
234
235 foreach (var comp in components)
236 {
237 if (comp.GetType() == type)
238 {
239 component = (T)comp;
240 return true;
241 }
242 }
243
244 return false;
245 }
246
247 /// <summary>
248 /// Gets the <see cref="VolumeComponent"/>, which is a subclass of <paramref name="type"/>, if
249 /// it exists.
250 /// </summary>
251 /// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
252 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
253 /// <param name="component">The output argument that contains the <see cref="VolumeComponent"/>
254 /// or <c>null</c>.</param>
255 /// <returns><c>true</c> if the <see cref="VolumeComponent"/> is in the Volume Profile,
256 /// <c>false</c> otherwise.</returns>
257 /// <seealso cref="TryGet{T}(Type, out T)"/>
258 /// <seealso cref="TryGet{T}(out T)"/>
259 /// <seealso cref="TryGetAllSubclassOf{T}"/>
260 public bool TryGetSubclassOf<T>(Type type, out T component)
261 where T : VolumeComponent
262 {
263 component = null;
264
265 foreach (var comp in components)
266 {
267 if (comp.GetType().IsSubclassOf(type))
268 {
269 component = (T)comp;
270 return true;
271 }
272 }
273
274 return false;
275 }
276
277 /// <summary>
278 /// Gets all the <see cref="VolumeComponent"/> that are subclasses of the specified type,
279 /// if there are any.
280 /// </summary>
281 /// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
282 /// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
283 /// <param name="result">The output list that contains all the <see cref="VolumeComponent"/>
284 /// if any. Note that Unity does not clear this list.</param>
285 /// <returns><c>true</c> if any <see cref="VolumeComponent"/> have been found in the profile,
286 /// <c>false</c> otherwise.</returns>
287 /// <seealso cref="TryGet{T}(Type, out T)"/>
288 /// <seealso cref="TryGet{T}(out T)"/>
289 /// <seealso cref="TryGetSubclassOf{T}"/>
290 public bool TryGetAllSubclassOf<T>(Type type, List<T> result)
291 where T : VolumeComponent
292 {
293 Assert.IsNotNull(components);
294 int count = result.Count;
295
296 foreach (var comp in components)
297 {
298 if (comp.GetType().IsSubclassOf(type))
299 result.Add((T)comp);
300 }
301
302 return count != result.Count;
303 }
304
305 /// <summary>
306 /// A custom hashing function that Unity uses to compare the state of parameters.
307 /// </summary>
308 /// <returns>A computed hash code for the current instance.</returns>
309 public override int GetHashCode()
310 {
311 unchecked
312 {
313 int hash = 17;
314
315 for (int i = 0; i < components.Count; i++)
316 hash = hash * 23 + components[i].GetHashCode();
317
318 return hash;
319 }
320 }
321
322 internal int GetComponentListHashCode()
323 {
324 unchecked
325 {
326 int hash = 17;
327
328 for (int i = 0; i < components.Count; i++)
329 hash = hash * 23 + components[i].GetType().GetHashCode();
330
331 return hash;
332 }
333 }
334
335 /// <summary>
336 /// Removes any components that were destroyed externally from the iternal list of components
337 /// </summary>
338 internal void Sanitize()
339 {
340 for (int i = components.Count - 1; i >= 0; i--)
341 if (components[i] == null)
342 components.RemoveAt(i);
343 }
344
345#if UNITY_EDITOR
346 void OnValidate()
347 {
348 // Delay the callback because when undoing the deletion of a VolumeComponent from a profile,
349 // it's possible VolumeComponent.OnEnable() has not yet been called, resulting in a crash when trying to
350 // update the default state.
351 EditorApplication.delayCall += () =>
352 {
353 if (VolumeManager.instance.isInitialized)
354 VolumeManager.instance.OnVolumeProfileChanged(this);
355 };
356 }
357#endif
358 }
359}