using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
namespace UnityEngine.Rendering
{
///
/// This attribute allows you to add commands to the Add Override popup menu
/// on Volumes.
/// To filter VolumeComponentMenu based on current Render Pipeline, add SupportedOnRenderPipeline attribute to the class alongside with this attribute.
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class VolumeComponentMenu : Attribute
{
///
/// The name of the entry in the override list. You can use slashes to create sub-menus.
///
public readonly string menu;
// TODO: Add support for component icons
///
/// Creates a new instance.
///
/// The name of the entry in the override list. You can use slashes to
/// create sub-menus.
public VolumeComponentMenu(string menu)
{
this.menu = menu;
}
}
///
/// This attribute allows you to add commands to the Add Override popup menu
/// on Volumes and specify for which render pipelines will be supported
///
[Obsolete(@"VolumeComponentMenuForRenderPipelineAttribute is deprecated. Use VolumeComponentMenu with SupportedOnCurrentPipeline instead. #from(2023.1)", false)]
public class VolumeComponentMenuForRenderPipeline : VolumeComponentMenu
{
///
/// The list of pipeline types that the target class supports
///
public Type[] pipelineTypes { get; }
///
/// Creates a new instance.
///
/// The name of the entry in the override list. You can use slashes to
/// create sub-menus.
/// The list of pipeline types that the target class supports
public VolumeComponentMenuForRenderPipeline(string menu, params Type[] pipelineTypes)
: base(menu)
{
if (pipelineTypes == null)
throw new Exception("Specify a list of supported pipeline");
// Make sure that we only allow the class types that inherit from the render pipeline
foreach (var t in pipelineTypes)
{
if (!typeof(RenderPipeline).IsAssignableFrom(t))
throw new Exception(
$"You can only specify types that inherit from {typeof(RenderPipeline)}, please check {t}");
}
this.pipelineTypes = pipelineTypes;
}
}
///
/// An attribute to hide the volume component to be added through `Add Override` button on the volume component list
///
[AttributeUsage(AttributeTargets.Class)]
[Obsolete("VolumeComponentDeprecated has been deprecated (UnityUpgradable) -> [UnityEngine] UnityEngine.HideInInspector", false)]
public sealed class VolumeComponentDeprecated : Attribute
{
}
///
/// The base class for all the components that can be part of a .
/// The Volume framework automatically handles and interpolates any members found in this class.
///
///
///
/// using UnityEngine.Rendering;
///
/// [Serializable, VolumeComponentMenuForRenderPipeline("Custom/Example Component")]
/// public class ExampleComponent : VolumeComponent
/// {
/// public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
/// }
///
///
[Serializable]
public partial class VolumeComponent : ScriptableObject
{
///
/// Local attribute for VolumeComponent fields only.
/// It handles relative indentation of a property for inspector.
///
public sealed class Indent : PropertyAttribute
{
/// Relative indent amount registered in this attribute
public readonly int relativeAmount;
/// Constructor
/// Relative indent change to use
public Indent(int relativeAmount = 1)
=> this.relativeAmount = relativeAmount;
}
///
/// The active state of the set of parameters defined in this class. You can use this to
/// quickly turn on or off all the overrides at once.
///
public bool active = true;
///
/// The name displayed in the component header. If you do not set a name, Unity generates one from
/// the class name automatically.
///
public string displayName { get; protected set; } = "";
///
/// The backing storage of . Use this for performance-critical work.
///
internal readonly List parameterList = new();
ReadOnlyCollection m_ParameterReadOnlyCollection;
///
/// A read-only collection of all the s defined in this class.
///
public ReadOnlyCollection parameters
{
get
{
if (m_ParameterReadOnlyCollection == null)
m_ParameterReadOnlyCollection = parameterList.AsReadOnly();
return m_ParameterReadOnlyCollection;
}
}
///
/// Extracts all the s defined in this class and nested classes.
///
/// The object to find the parameters
/// The list filled with the parameters.
/// If you want to filter the parameters
internal static void FindParameters(object o, List parameters, Func filter = null)
{
if (o == null)
return;
var fields = o.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.OrderBy(t => t.MetadataToken); // Guaranteed order
foreach (var field in fields)
{
if (field.FieldType.IsSubclassOf(typeof(VolumeParameter)))
{
if (filter?.Invoke(field) ?? true)
{
VolumeParameter volumeParameter = (VolumeParameter)field.GetValue(o);
parameters.Add(volumeParameter);
}
}
else if (!field.FieldType.IsArray && field.FieldType.IsClass)
FindParameters(field.GetValue(o), parameters, filter);
}
}
///
/// Unity calls this method when it loads the class.
///
///
/// If you want to override this method, you must call base.OnEnable().
///
protected virtual void OnEnable()
{
// Automatically grab all fields of type VolumeParameter for this instance
parameterList.Clear();
FindParameters(this, parameterList);
foreach (var parameter in parameterList)
{
if (parameter != null)
parameter.OnEnable();
else
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.");
}
}
///
/// Unity calls this method when the object goes out of scope.
///
protected virtual void OnDisable()
{
foreach (var parameter in parameterList)
{
if (parameter != null)
parameter.OnDisable();
}
}
///
/// Interpolates a with this component by an interpolation
/// factor and puts the result back into the given .
///
///
/// You can override this method to do your own blending. Either loop through the
/// list or reference direct fields. You should only use
/// to set parameter values and not assign
/// directly to the state object. you should also manually check
/// before you set any values.
///
/// The internal component to interpolate from. You must store
/// the result of the interpolation in this same component.
/// The interpolation factor in range [0,1].
///
/// Below is the default implementation for blending:
///
/// public virtual void Override(VolumeComponent state, float interpFactor)
/// {
/// int count = parameters.Count;
///
/// for (int i = 0; i < count; i++)
/// {
/// var stateParam = state.parameters[i];
/// var toParam = parameters[i];
///
/// if (toParam.overrideState)
/// {
/// // Keep track of the override state to ensure that state will be reset on next frame (and for debugging purpose)
/// stateParam.overrideState = toParam.overrideState;
/// stateParam.Interp(stateParam, toParam, interpFactor);
/// }
/// }
/// }
///
///
public virtual void Override(VolumeComponent state, float interpFactor)
{
int count = parameterList.Count;
for (int i = 0; i < count; i++)
{
var stateParam = state.parameterList[i];
var toParam = parameterList[i];
if (toParam.overrideState)
{
// Keep track of the override state to ensure that state will be reset on next frame (and for debugging purpose)
stateParam.overrideState = toParam.overrideState;
stateParam.Interp(stateParam, toParam, interpFactor);
}
}
}
///
/// Sets the state of all the overrides on this component to a given value.
///
/// The value to set the state of the overrides to.
public void SetAllOverridesTo(bool state)
{
SetOverridesTo(parameterList, state);
}
///
/// Sets the override state of the given parameters on this component to a given value.
///
/// The value to set the state of the overrides to.
internal void SetOverridesTo(IEnumerable enumerable, bool state)
{
foreach (var prop in enumerable)
{
prop.overrideState = state;
var t = prop.GetType();
if (VolumeParameter.IsObjectParameter(t))
{
// This method won't be called a lot but this is sub-optimal, fix me
var innerParams = (ReadOnlyCollection)
t.GetProperty("parameters", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(prop, null);
if (innerParams != null)
SetOverridesTo(innerParams, state);
}
}
}
///
/// 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
{
//return parameters.Aggregate(17, (i, p) => i * 23 + p.GetHash());
int hash = 17;
for (int i = 0; i < parameterList.Count; i++)
hash = hash * 23 + parameterList[i].GetHashCode();
return hash;
}
}
///
/// Returns true if any of the volume properites has been overridden.
///
/// True if any of the volume properites has been overridden.
public bool AnyPropertiesIsOverridden()
{
for (int i = 0; i < parameterList.Count; ++i)
{
if (parameterList[i].overrideState) return true;
}
return false;
}
///
/// Unity calls this method before the object is destroyed.
///
protected virtual void OnDestroy() => Release();
///
/// Releases all the allocated resources.
///
public void Release()
{
if (parameterList == null)
return;
for (int i = 0; i < parameterList.Count; i++)
{
if (parameterList[i] != null)
parameterList[i].Release();
}
}
}
}