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}