using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using System.Linq; namespace UnityEngine.Rendering { /// /// This attribute allows you to add commands to the Add Override popup menu /// on Volumes. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public sealed 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; } } /// /// An attribute set on deprecated volume components. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = 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, VolumeComponentMenu("Custom/Example Component")] /// public class ExampleComponent : VolumeComponent /// { /// public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f); /// } /// /// [Serializable] public class VolumeComponent : ScriptableObject { /// /// 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; } = ""; /// /// A read-only collection of all the s defined in this class. /// public ReadOnlyCollection parameters { get; private set; } #pragma warning disable 414 [SerializeField] bool m_AdvancedMode = false; // Editor-only #pragma warning restore 414 /// /// 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 parameters = this.GetType() .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter))) .OrderBy(t => t.MetadataToken) // Guaranteed order .Select(t => (VolumeParameter)t.GetValue(this)) .ToList() .AsReadOnly(); foreach (var parameter in parameters) parameter.OnEnable(); } /// /// Unity calls this method when the object goes out of scope. /// protected virtual void OnDisable() { if (parameters == null) return; foreach (var parameter in parameters) 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]; /// /// // Keep track of the override state for debugging purpose /// stateParam.overrideState = toParam.overrideState; /// /// if (toParam.overrideState) /// stateParam.Interp(stateParam, toParam, interpFactor); /// } /// } /// /// 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]; // Keep track of the override state for debugging purpose stateParam.overrideState = toParam.overrideState; if (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) { SetAllOverridesTo(parameters, state); } void SetAllOverridesTo(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) SetAllOverridesTo(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 < parameters.Count; i++) hash = hash * 23 + parameters[i].GetHashCode(); return hash; } } } }