360 lines
14 KiB
C#
360 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEditor;
|
|
using UnityEngine.Assertions;
|
|
|
|
namespace UnityEngine.Rendering
|
|
{
|
|
/// <summary>
|
|
/// An Asset which holds a set of settings to use with a <see cref="Volume"/>.
|
|
/// </summary>
|
|
[CurrentPipelineHelpURL("Volume-Profile")]
|
|
[Icon("Packages/com.unity.render-pipelines.core/Editor/Icons/Processed/VolumeProfile Icon.asset")]
|
|
public sealed class VolumeProfile : ScriptableObject
|
|
{
|
|
/// <summary>
|
|
/// A list of every setting that this Volume Profile stores.
|
|
/// </summary>
|
|
public List<VolumeComponent> components = new List<VolumeComponent>();
|
|
|
|
/// <summary>
|
|
/// **Note**: For Internal Use Only<br/>
|
|
/// A dirty check used to redraw the profile inspector when something has changed. This is
|
|
/// currently only used in the editor.
|
|
/// </summary>
|
|
[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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
isDirty = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a <see cref="VolumeComponent"/> to this Volume Profile.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// You can only have a single component of the same type per Volume Profile.
|
|
/// </remarks>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
|
/// <param name="overrides">Specifies whether Unity should automatically override all the settings when
|
|
/// you add a <see cref="VolumeComponent"/> to the Volume Profile.</param>
|
|
/// <returns>The instance for the given type that you added to the Volume Profile</returns>
|
|
/// <seealso cref="Add"/>
|
|
public T Add<T>(bool overrides = false)
|
|
where T : VolumeComponent
|
|
{
|
|
return (T)Add(typeof(T), overrides);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a <see cref="VolumeComponent"/> to this Volume Profile.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// You can only have a single component of the same type per Volume Profile.
|
|
/// </remarks>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <param name="overrides">Specifies whether Unity should automatically override all the settings when
|
|
/// you add a <see cref="VolumeComponent"/> to the Volume Profile.</param>
|
|
/// <returns>The instance created for the given type that has been added to the profile</returns>
|
|
/// <seealso cref="Add{T}"/>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a <see cref="VolumeComponent"/> from this Volume Profile.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This method does nothing if the type does not exist in the Volume Profile.
|
|
/// </remarks>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
|
/// <seealso cref="Remove"/>
|
|
public void Remove<T>()
|
|
where T : VolumeComponent
|
|
{
|
|
Remove(typeof(T));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a <see cref="VolumeComponent"/> from this Volume Profile.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This method does nothing if the type does not exist in the Volume Profile.
|
|
/// </remarks>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <seealso cref="Remove{T}"/>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if this Volume Profile contains the <see cref="VolumeComponent"/> you pass in.
|
|
/// </summary>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
|
/// <returns><c>true</c> if the <see cref="VolumeComponent"/> exists in the Volume Profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="Has"/>
|
|
/// <seealso cref="HasSubclassOf"/>
|
|
public bool Has<T>()
|
|
where T : VolumeComponent
|
|
{
|
|
return Has(typeof(T));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if this Volume Profile contains the <see cref="VolumeComponent"/> you pass in.
|
|
/// </summary>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <returns><c>true</c> if the <see cref="VolumeComponent"/> exists in the Volume Profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="Has{T}"/>
|
|
/// <seealso cref="HasSubclassOf"/>
|
|
public bool Has(Type type)
|
|
{
|
|
foreach (var component in components)
|
|
{
|
|
if (component.GetType() == type)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if this Volume Profile contains the <see cref="VolumeComponent"/>, which is a subclass of <paramref name="type"/>,
|
|
/// that you pass in.
|
|
/// </summary>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <returns><c>true</c> if the <see cref="VolumeComponent"/> exists in the Volume Profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="Has"/>
|
|
/// <seealso cref="Has{T}"/>
|
|
public bool HasSubclassOf(Type type)
|
|
{
|
|
foreach (var component in components)
|
|
{
|
|
if (component.GetType().IsSubclassOf(type))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="VolumeComponent"/> of the specified type, if it exists.
|
|
/// </summary>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
|
/// <param name="component">The output argument that contains the <see cref="VolumeComponent"/>
|
|
/// or <c>null</c>.</param>
|
|
/// <returns><c>true</c> if the <see cref="VolumeComponent"/> is in the Volume Profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="TryGet{T}(Type, out T)"/>
|
|
/// <seealso cref="TryGetSubclassOf{T}"/>
|
|
/// <seealso cref="TryGetAllSubclassOf{T}"/>
|
|
public bool TryGet<T>(out T component)
|
|
where T : VolumeComponent
|
|
{
|
|
return TryGet(typeof(T), out component);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="VolumeComponent"/> of the specified type, if it exists.
|
|
/// </summary>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/></typeparam>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <param name="component">The output argument that contains the <see cref="VolumeComponent"/>
|
|
/// or <c>null</c>.</param>
|
|
/// <returns><c>true</c> if the <see cref="VolumeComponent"/> is in the Volume Profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="TryGet{T}(out T)"/>
|
|
/// <seealso cref="TryGetSubclassOf{T}"/>
|
|
/// <seealso cref="TryGetAllSubclassOf{T}"/>
|
|
public bool TryGet<T>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="VolumeComponent"/>, which is a subclass of <paramref name="type"/>, if
|
|
/// it exists.
|
|
/// </summary>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <param name="component">The output argument that contains the <see cref="VolumeComponent"/>
|
|
/// or <c>null</c>.</param>
|
|
/// <returns><c>true</c> if the <see cref="VolumeComponent"/> is in the Volume Profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="TryGet{T}(Type, out T)"/>
|
|
/// <seealso cref="TryGet{T}(out T)"/>
|
|
/// <seealso cref="TryGetAllSubclassOf{T}"/>
|
|
public bool TryGetSubclassOf<T>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all the <see cref="VolumeComponent"/> that are subclasses of the specified type,
|
|
/// if there are any.
|
|
/// </summary>
|
|
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
|
/// <param name="type">A type that inherits from <see cref="VolumeComponent"/>.</param>
|
|
/// <param name="result">The output list that contains all the <see cref="VolumeComponent"/>
|
|
/// if any. Note that Unity does not clear this list.</param>
|
|
/// <returns><c>true</c> if any <see cref="VolumeComponent"/> have been found in the profile,
|
|
/// <c>false</c> otherwise.</returns>
|
|
/// <seealso cref="TryGet{T}(Type, out T)"/>
|
|
/// <seealso cref="TryGet{T}(out T)"/>
|
|
/// <seealso cref="TryGetSubclassOf{T}"/>
|
|
public bool TryGetAllSubclassOf<T>(Type type, List<T> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A custom hashing function that Unity uses to compare the state of parameters.
|
|
/// </summary>
|
|
/// <returns>A computed hash code for the current instance.</returns>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes any components that were destroyed externally from the iternal list of components
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|