using System; using System.Collections.Generic; using UnityEditor; using UnityEngine.Assertions; namespace UnityEngine.Rendering { /// /// An Asset which holds a set of settings to use with a . /// [CurrentPipelineHelpURL("Volume-Profile")] [Icon("Packages/com.unity.render-pipelines.core/Editor/Icons/Processed/VolumeProfile Icon.asset")] public sealed class VolumeProfile : ScriptableObject { /// /// A list of every setting that this Volume Profile stores. /// public List components = new List(); /// /// **Note**: For Internal Use Only
/// A dirty check used to redraw the profile inspector when something has changed. This is /// currently only used in the editor. ///
[NonSerialized] public bool isDirty = true; // Editor only, doesn't have any use outside of it void OnEnable() { // Make sure every setting is valid. If a profile holds a script that doesn't exist // anymore, nuke it to keep the volume clean. Note that if you delete a script that is // currently in use in a volume you'll still get a one-time error in the console, it's // harmless and happens because Unity does a redraw of the editor (and thus the current // frame) before the recompilation step. components.RemoveAll(x => x == null); } // The lifetime of ScriptableObjects is different from MonoBehaviours. When the last reference to a // VolumeProfile goes out of scope (e.g. when a scene containing Volume components is unloaded), Unity will call // OnDisable() on the VolumeProfile. We need to release the internal resources in this case to avoid leaks. internal void OnDisable() { if (components == null) return; for (int i = 0; i < components.Count; i++) { if (components[i] != null) components[i].Release(); } } /// /// Resets the dirty state of the Volume Profile. Unity uses this to force-refresh and redraw the /// Volume Profile editor when you modify the Asset via script instead of the Inspector. /// public void Reset() { isDirty = true; } /// /// Adds a to this Volume Profile. /// /// /// You can only have a single component of the same type per Volume Profile. /// /// A type of . /// Specifies whether Unity should automatically override all the settings when /// you add a to the Volume Profile. /// The instance for the given type that you added to the Volume Profile /// public T Add(bool overrides = false) where T : VolumeComponent { return (T)Add(typeof(T), overrides); } /// /// Adds a to this Volume Profile. /// /// /// You can only have a single component of the same type per Volume Profile. /// /// A type that inherits from . /// Specifies whether Unity should automatically override all the settings when /// you add a to the Volume Profile. /// The instance created for the given type that has been added to the profile /// public VolumeComponent Add(Type type, bool overrides = false) { if (Has(type)) throw new InvalidOperationException("Component already exists in the volume"); var component = (VolumeComponent)CreateInstance(type); #if UNITY_EDITOR component.hideFlags = HideFlags.HideInInspector | HideFlags.HideInHierarchy; component.name = type.Name; #endif component.SetAllOverridesTo(overrides); components.Add(component); isDirty = true; return component; } /// /// Removes a from this Volume Profile. /// /// /// This method does nothing if the type does not exist in the Volume Profile. /// /// A type of . /// public void Remove() where T : VolumeComponent { Remove(typeof(T)); } /// /// Removes a from this Volume Profile. /// /// /// This method does nothing if the type does not exist in the Volume Profile. /// /// A type that inherits from . /// public void Remove(Type type) { int toRemove = -1; for (int i = 0; i < components.Count; i++) { if (components[i].GetType() == type) { toRemove = i; break; } } if (toRemove >= 0) { components.RemoveAt(toRemove); isDirty = true; } } /// /// Checks if this Volume Profile contains the you pass in. /// /// A type of . /// true if the exists in the Volume Profile, /// false otherwise. /// /// public bool Has() where T : VolumeComponent { return Has(typeof(T)); } /// /// Checks if this Volume Profile contains the you pass in. /// /// A type that inherits from . /// true if the exists in the Volume Profile, /// false otherwise. /// /// public bool Has(Type type) { foreach (var component in components) { if (component.GetType() == type) return true; } return false; } /// /// Checks if this Volume Profile contains the , which is a subclass of , /// that you pass in. /// /// A type that inherits from . /// true if the exists in the Volume Profile, /// false otherwise. /// /// public bool HasSubclassOf(Type type) { foreach (var component in components) { if (component.GetType().IsSubclassOf(type)) return true; } return false; } /// /// Gets the of the specified type, if it exists. /// /// A type of . /// The output argument that contains the /// or null. /// true if the is in the Volume Profile, /// false otherwise. /// /// /// public bool TryGet(out T component) where T : VolumeComponent { return TryGet(typeof(T), out component); } /// /// Gets the of the specified type, if it exists. /// /// A type of /// A type that inherits from . /// The output argument that contains the /// or null. /// true if the is in the Volume Profile, /// false otherwise. /// /// /// public bool TryGet(Type type, out T component) where T : VolumeComponent { component = null; foreach (var comp in components) { if (comp.GetType() == type) { component = (T)comp; return true; } } return false; } /// /// Gets the , which is a subclass of , if /// it exists. /// /// A type of . /// A type that inherits from . /// The output argument that contains the /// or null. /// true if the is in the Volume Profile, /// false otherwise. /// /// /// public bool TryGetSubclassOf(Type type, out T component) where T : VolumeComponent { component = null; foreach (var comp in components) { if (comp.GetType().IsSubclassOf(type)) { component = (T)comp; return true; } } return false; } /// /// Gets all the that are subclasses of the specified type, /// if there are any. /// /// A type of . /// A type that inherits from . /// The output list that contains all the /// if any. Note that Unity does not clear this list. /// true if any have been found in the profile, /// false otherwise. /// /// /// public bool TryGetAllSubclassOf(Type type, List result) where T : VolumeComponent { Assert.IsNotNull(components); int count = result.Count; foreach (var comp in components) { if (comp.GetType().IsSubclassOf(type)) result.Add((T)comp); } return count != result.Count; } /// /// A custom hashing function that Unity uses to compare the state of parameters. /// /// A computed hash code for the current instance. public override int GetHashCode() { unchecked { int hash = 17; for (int i = 0; i < components.Count; i++) hash = hash * 23 + components[i].GetHashCode(); return hash; } } internal int GetComponentListHashCode() { unchecked { int hash = 17; for (int i = 0; i < components.Count; i++) hash = hash * 23 + components[i].GetType().GetHashCode(); return hash; } } /// /// Removes any components that were destroyed externally from the iternal list of components /// internal void Sanitize() { for (int i = components.Count - 1; i >= 0; i--) if (components[i] == null) components.RemoveAt(i); } #if UNITY_EDITOR void OnValidate() { // Delay the callback because when undoing the deletion of a VolumeComponent from a profile, // it's possible VolumeComponent.OnEnable() has not yet been called, resulting in a crash when trying to // update the default state. EditorApplication.delayCall += () => { if (VolumeManager.instance.isInitialized) VolumeManager.instance.OnVolumeProfileChanged(this); }; } #endif } }