using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using System.Linq; namespace UnityEngine.Rendering { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public sealed class VolumeComponentMenu : Attribute { public readonly string menu; // TODO: Add support for component icons public VolumeComponentMenu(string menu) { this.menu = menu; } } [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public sealed class VolumeComponentDeprecated : Attribute { } [Serializable] public class VolumeComponent : ScriptableObject { // Used to control the state of this override - handy to quickly turn a volume override // on & off in the editor public bool active = true; public string displayName { get; protected set; } = ""; public ReadOnlyCollection parameters { get; private set; } #pragma warning disable 414 [SerializeField] bool m_AdvancedMode = false; // Editor-only #pragma warning restore 414 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(); } protected virtual void OnDisable() { if (parameters == null) return; foreach (var parameter in parameters) parameter.OnDisable(); } // You can override this to do your own blending. Either loop through the `parameters` list // or reference direct fields (you'll need to cast `state` to your custom type and don't // forget to use `SetValue` on parameters, do not assign directly to the state object - and // of course you'll need to check for the `overrideState` manually). 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 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); } } } // Custom hashing function used to compare the state of settings (it's not meant to be // unique but to be a quick way to check if two setting sets have the same state or not). // Hash collision rate should be pretty low. 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; } } } }