testss
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic Volume component holding a <see cref="VolumeProfile"/>.
|
||||
/// </summary>
|
||||
[HelpURL(Documentation.baseURLHDRP + Documentation.version + Documentation.subURL + "Volumes" + Documentation.endURL)]
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Miscellaneous/Volume")]
|
||||
public class Volume : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies whether to apply the Volume to the entire Scene or not.
|
||||
/// </summary>
|
||||
[Tooltip("When enabled, HDRP applies this Volume to the entire Scene.")]
|
||||
public bool isGlobal = true;
|
||||
|
||||
/// <summary>
|
||||
/// The Volume priority in the stack. A higher value means higher priority. This supports negative values.
|
||||
/// </summary>
|
||||
[Tooltip("Sets the Volume priority in the stack. A higher value means higher priority. You can use negative values.")]
|
||||
public float priority = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// The outer distance to start blending from. A value of 0 means no blending and Unity applies
|
||||
/// the Volume overrides immediately upon entry.
|
||||
/// </summary>
|
||||
[Tooltip("Sets the outer distance to start blending from. A value of 0 means no blending and Unity applies the Volume overrides immediately upon entry.")]
|
||||
public float blendDistance = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// The total weight of this volume in the Scene. 0 means no effect and 1 means full effect.
|
||||
/// </summary>
|
||||
[Range(0f, 1f), Tooltip("Sets the total weight of this Volume in the Scene. 0 means no effect and 1 means full effect.")]
|
||||
public float weight = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The shared Profile that this Volume uses.
|
||||
/// Modifying <c>sharedProfile</c> changes every Volumes that uses this Profile and also changes
|
||||
/// the Profile settings stored in the Project.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should not modify Profiles that <c>sharedProfile</c> returns. If you want
|
||||
/// to modify the Profile of a Volume, use <see cref="profile"/> instead.
|
||||
/// </remarks>
|
||||
/// <seealso cref="profile"/>
|
||||
public VolumeProfile sharedProfile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first instantiated <see cref="VolumeProfile"/> assigned to the Volume.
|
||||
/// Modifying <c>profile</c> changes the Profile for this Volume only. If another Volume
|
||||
/// uses the same Profile, this clones the shared Profile and starts using it from now on.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property automatically instantiates the Profile and make it unique to this Volume
|
||||
/// so you can safely edit it via scripting at runtime without changing the original Asset
|
||||
/// in the Project.
|
||||
/// Note that if you pass your own Profile, you must destroy it when you finish using it.
|
||||
/// </remarks>
|
||||
/// <seealso cref="sharedProfile"/>
|
||||
public VolumeProfile profile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_InternalProfile == null)
|
||||
{
|
||||
m_InternalProfile = ScriptableObject.CreateInstance<VolumeProfile>();
|
||||
|
||||
if (sharedProfile != null)
|
||||
{
|
||||
foreach (var item in sharedProfile.components)
|
||||
{
|
||||
var itemCopy = Instantiate(item);
|
||||
m_InternalProfile.components.Add(itemCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_InternalProfile;
|
||||
}
|
||||
set => m_InternalProfile = value;
|
||||
}
|
||||
|
||||
internal VolumeProfile profileRef => m_InternalProfile == null ? sharedProfile : m_InternalProfile;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Volume has an instantiated Profile or if it uses a shared Profile.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if the profile has been instantiated.</returns>
|
||||
/// <seealso cref="profile"/>
|
||||
/// <seealso cref="sharedProfile"/>
|
||||
public bool HasInstantiatedProfile() => m_InternalProfile != null;
|
||||
|
||||
// Needed for state tracking (see the comments in Update)
|
||||
int m_PreviousLayer;
|
||||
float m_PreviousPriority;
|
||||
VolumeProfile m_InternalProfile;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_PreviousLayer = gameObject.layer;
|
||||
VolumeManager.instance.Register(this, m_PreviousLayer);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
VolumeManager.instance.Unregister(this, gameObject.layer);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Unfortunately we need to track the current layer to update the volume manager in
|
||||
// real-time as the user could change it at any time in the editor or at runtime.
|
||||
// Because no event is raised when the layer changes, we have to track it on every
|
||||
// frame :/
|
||||
UpdateLayer();
|
||||
|
||||
// Same for priority. We could use a property instead, but it doesn't play nice with the
|
||||
// serialization system. Using a custom Attribute/PropertyDrawer for a property is
|
||||
// possible but it doesn't work with Undo/Redo in the editor, which makes it useless for
|
||||
// our case.
|
||||
if (priority != m_PreviousPriority)
|
||||
{
|
||||
VolumeManager.instance.SetLayerDirty(gameObject.layer);
|
||||
m_PreviousPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateLayer()
|
||||
{
|
||||
int layer = gameObject.layer;
|
||||
if (layer != m_PreviousLayer)
|
||||
{
|
||||
VolumeManager.instance.UpdateVolumeLayer(this, m_PreviousLayer, layer);
|
||||
m_PreviousLayer = layer;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// TODO: Look into a better volume previsualization system
|
||||
List<Collider> m_TempColliders;
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (m_TempColliders == null)
|
||||
m_TempColliders = new List<Collider>();
|
||||
|
||||
var colliders = m_TempColliders;
|
||||
GetComponents(colliders);
|
||||
|
||||
if (isGlobal || colliders == null)
|
||||
return;
|
||||
|
||||
var scale = transform.localScale;
|
||||
var invScale = new Vector3(1f / scale.x, 1f / scale.y, 1f / scale.z);
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, scale);
|
||||
Gizmos.color = CoreRenderPipelinePreferences.volumeGizmoColor;
|
||||
|
||||
// Draw a separate gizmo for each collider
|
||||
foreach (var collider in colliders)
|
||||
{
|
||||
if (!collider.enabled)
|
||||
continue;
|
||||
|
||||
// We'll just use scaling as an approximation for volume skin. It's far from being
|
||||
// correct (and is completely wrong in some cases). Ultimately we'd use a distance
|
||||
// field or at least a tesselate + push modifier on the collider's mesh to get a
|
||||
// better approximation, but the current Gizmo system is a bit limited and because
|
||||
// everything is dynamic in Unity and can be changed at anytime, it's hard to keep
|
||||
// track of changes in an elegant way (which we'd need to implement a nice cache
|
||||
// system for generated volume meshes).
|
||||
switch (collider)
|
||||
{
|
||||
case BoxCollider c:
|
||||
Gizmos.DrawCube(c.center, c.size);
|
||||
break;
|
||||
case SphereCollider c:
|
||||
// For sphere the only scale that is used is the transform.x
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one * scale.x);
|
||||
Gizmos.DrawSphere(c.center, c.radius);
|
||||
break;
|
||||
case MeshCollider c:
|
||||
// Only convex mesh m_Colliders are allowed
|
||||
if (!c.convex)
|
||||
c.convex = true;
|
||||
|
||||
// Mesh pivot should be centered or this won't work
|
||||
Gizmos.DrawMesh(c.sharedMesh);
|
||||
break;
|
||||
default:
|
||||
// Nothing for capsule (DrawCapsule isn't exposed in Gizmo), terrain, wheel and
|
||||
// other m_Colliders...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
colliders.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,267 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute allows you to add commands to the <strong>Add Override</strong> popup menu
|
||||
/// on Volumes.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public sealed class VolumeComponentMenu : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the entry in the override list. You can use slashes to create sub-menus.
|
||||
/// </summary>
|
||||
public readonly string menu;
|
||||
// TODO: Add support for component icons
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <seealso cref="VolumeComponentMenu"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="menu">The name of the entry in the override list. You can use slashes to
|
||||
/// create sub-menus.</param>
|
||||
public VolumeComponentMenu(string menu)
|
||||
{
|
||||
this.menu = menu;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An attribute set on deprecated volume components.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public sealed class VolumeComponentDeprecated : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base class for all the components that can be part of a <see cref="VolumeProfile"/>.
|
||||
/// The Volume framework automatically handles and interpolates any <see cref="VolumeParameter"/> members found in this class.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using UnityEngine.Rendering;
|
||||
///
|
||||
/// [Serializable, VolumeComponentMenu("Custom/Example Component")]
|
||||
/// public class ExampleComponent : VolumeComponent
|
||||
/// {
|
||||
/// public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[Serializable]
|
||||
public class VolumeComponent : ScriptableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool active = true;
|
||||
|
||||
/// <summary>
|
||||
/// The name displayed in the component header. If you do not set a name, Unity generates one from
|
||||
/// the class name automatically.
|
||||
/// </summary>
|
||||
public string displayName { get; protected set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// A read-only collection of all the <see cref="VolumeParameter"/>s defined in this class.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<VolumeParameter> parameters { get; private set; }
|
||||
|
||||
#pragma warning disable 414
|
||||
[SerializeField]
|
||||
bool m_AdvancedMode = false; // Editor-only
|
||||
#pragma warning restore 414
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all the <see cref="VolumeParameter"/>s defined in this class and nested classes.
|
||||
/// </summary>
|
||||
/// <param name="o">The object to find the parameters</param>
|
||||
/// <param name="parameters">The list filled with the parameters.</param>
|
||||
/// <param name="filter">If you want to filter the parameters</param>
|
||||
internal static void FindParameters(object o, List<VolumeParameter> parameters, Func<FieldInfo, bool> 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)
|
||||
parameters.Add((VolumeParameter)field.GetValue(o));
|
||||
}
|
||||
else if (!field.FieldType.IsArray && field.FieldType.IsClass)
|
||||
FindParameters(field.GetValue(o), parameters, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity calls this method when it loads the class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you want to override this method, you must call <c>base.OnEnable()</c>.
|
||||
/// </remarks>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// Automatically grab all fields of type VolumeParameter for this instance
|
||||
var fields = new List<VolumeParameter>();
|
||||
FindParameters(this, fields);
|
||||
parameters = fields.AsReadOnly();
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity calls this method when the object goes out of scope.
|
||||
/// </summary>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (parameters == null)
|
||||
return;
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (parameter != null)
|
||||
parameter.OnDisable();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates a <see cref="VolumeComponent"/> with this component by an interpolation
|
||||
/// factor and puts the result back into the given <see cref="VolumeComponent"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can override this method to do your own blending. Either loop through the
|
||||
/// <see cref="parameters"/> list or reference direct fields. You should only use
|
||||
/// <see cref="VolumeParameter.SetValue"/> to set parameter values and not assign
|
||||
/// directly to the state object. you should also manually check
|
||||
/// <see cref="VolumeParameter.overrideState"/> before you set any values.
|
||||
/// </remarks>
|
||||
/// <param name="state">The internal component to interpolate from. You must store
|
||||
/// the result of the interpolation in this same component.</param>
|
||||
/// <param name="interpFactor">The interpolation factor in range [0,1].</param>
|
||||
/// <example>
|
||||
/// Below is the default implementation for blending:
|
||||
/// <code>
|
||||
/// 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);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
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 for debugging purpose
|
||||
stateParam.overrideState = toParam.overrideState;
|
||||
stateParam.Interp(stateParam, toParam, interpFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the state of all the overrides on this component to a given value.
|
||||
/// </summary>
|
||||
/// <param name="state">The value to set the state of the overrides to.</param>
|
||||
public void SetAllOverridesTo(bool state)
|
||||
{
|
||||
SetOverridesTo(parameters, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the override state of the given parameters on this component to a given value.
|
||||
/// </summary>
|
||||
/// <param name="state">The value to set the state of the overrides to.</param>
|
||||
internal void SetOverridesTo(IEnumerable<VolumeParameter> 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<VolumeParameter>)
|
||||
t.GetProperty("parameters", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetValue(prop, null);
|
||||
|
||||
if (innerParams != null)
|
||||
SetOverridesTo(innerParams, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity calls this method before the object is destroyed.
|
||||
/// </summary>
|
||||
protected virtual void OnDestroy() => Release();
|
||||
|
||||
/// <summary>
|
||||
/// Releases all the allocated resources.
|
||||
/// </summary>
|
||||
public void Release()
|
||||
{
|
||||
for (int i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
if (parameters[i] != null)
|
||||
parameters[i].Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,493 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
/// <summary>
|
||||
/// A global manager that tracks all the Volumes in the currently loaded Scenes and does all the
|
||||
/// interpolation work.
|
||||
/// </summary>
|
||||
public sealed class VolumeManager
|
||||
{
|
||||
static readonly Lazy<VolumeManager> s_Instance = new Lazy<VolumeManager>(() => new VolumeManager());
|
||||
|
||||
/// <summary>
|
||||
/// The current singleton instance of <see cref="VolumeManager"/>.
|
||||
/// </summary>
|
||||
public static VolumeManager instance => s_Instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the main <see cref="VolumeStack"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="VolumeStack"/>
|
||||
public VolumeStack stack { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current list of all available types that derive from <see cref="VolumeComponent"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Please use baseComponentTypeArray instead.")]
|
||||
public IEnumerable<Type> baseComponentTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseComponentTypeArray;
|
||||
}
|
||||
private set
|
||||
{
|
||||
baseComponentTypeArray = value.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] baseComponentTypeArray { get; private set; }
|
||||
|
||||
// Max amount of layers available in Unity
|
||||
const int k_MaxLayerCount = 32;
|
||||
|
||||
// Cached lists of all volumes (sorted by priority) by layer mask
|
||||
readonly Dictionary<int, List<Volume>> m_SortedVolumes;
|
||||
|
||||
// Holds all the registered volumes
|
||||
readonly List<Volume> m_Volumes;
|
||||
|
||||
// Keep track of sorting states for layer masks
|
||||
readonly Dictionary<int, bool> m_SortNeeded;
|
||||
|
||||
// Internal list of default state for each component type - this is used to reset component
|
||||
// states on update instead of having to implement a Reset method on all components (which
|
||||
// would be error-prone)
|
||||
readonly List<VolumeComponent> m_ComponentsDefaultState;
|
||||
|
||||
// Recycled list used for volume traversal
|
||||
readonly List<Collider> m_TempColliders;
|
||||
|
||||
VolumeManager()
|
||||
{
|
||||
m_SortedVolumes = new Dictionary<int, List<Volume>>();
|
||||
m_Volumes = new List<Volume>();
|
||||
m_SortNeeded = new Dictionary<int, bool>();
|
||||
m_TempColliders = new List<Collider>(8);
|
||||
m_ComponentsDefaultState = new List<VolumeComponent>();
|
||||
|
||||
ReloadBaseTypes();
|
||||
|
||||
stack = CreateStack();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a new <see cref="VolumeStack"/> to use when you need to store
|
||||
/// the result of the Volume blending pass in a separate stack.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <seealso cref="VolumeStack"/>
|
||||
/// <seealso cref="Update(VolumeStack,Transform,LayerMask)"/>
|
||||
public VolumeStack CreateStack()
|
||||
{
|
||||
var stack = new VolumeStack();
|
||||
stack.Reload(baseComponentTypeArray);
|
||||
return stack;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroy a Volume Stack
|
||||
/// </summary>
|
||||
/// <param name="stack">Volume Stack that needs to be destroyed.</param>
|
||||
public void DestroyStack(VolumeStack stack)
|
||||
{
|
||||
stack.Dispose();
|
||||
}
|
||||
|
||||
// This will be called only once at runtime and everytime script reload kicks-in in the
|
||||
// editor as we need to keep track of any compatible component in the project
|
||||
void ReloadBaseTypes()
|
||||
{
|
||||
m_ComponentsDefaultState.Clear();
|
||||
|
||||
// Grab all the component types we can find
|
||||
baseComponentTypeArray = CoreUtils.GetAllTypesDerivedFrom<VolumeComponent>()
|
||||
.Where(t => !t.IsAbstract).ToArray();
|
||||
|
||||
var flags = System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
|
||||
// Keep an instance of each type to be used in a virtual lowest priority global volume
|
||||
// so that we have a default state to fallback to when exiting volumes
|
||||
foreach (var type in baseComponentTypeArray)
|
||||
{
|
||||
type.GetMethod("Init", flags)?.Invoke(null, null);
|
||||
var inst = (VolumeComponent)ScriptableObject.CreateInstance(type);
|
||||
m_ComponentsDefaultState.Add(inst);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new Volume in the manager. Unity does this automatically when a new Volume is
|
||||
/// enabled, or its layer changes, but you can use this function to force-register a Volume
|
||||
/// that is currently disabled.
|
||||
/// </summary>
|
||||
/// <param name="volume">The volume to register.</param>
|
||||
/// <param name="layer">The LayerMask that this volume is in.</param>
|
||||
/// <seealso cref="Unregister"/>
|
||||
public void Register(Volume volume, int layer)
|
||||
{
|
||||
m_Volumes.Add(volume);
|
||||
|
||||
// Look for existing cached layer masks and add it there if needed
|
||||
foreach (var kvp in m_SortedVolumes)
|
||||
{
|
||||
// We add the volume to sorted lists only if the layer match and if it doesn't contain the volume already.
|
||||
if ((kvp.Key & (1 << layer)) != 0 && !kvp.Value.Contains(volume))
|
||||
kvp.Value.Add(volume);
|
||||
}
|
||||
|
||||
SetLayerDirty(layer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a Volume from the manager. Unity does this automatically when a Volume is
|
||||
/// disabled or goes out of scope, but you can use this function to force-unregister a Volume
|
||||
/// that you added manually while it was disabled.
|
||||
/// </summary>
|
||||
/// <param name="volume">The Volume to unregister.</param>
|
||||
/// <param name="layer">The LayerMask that this Volume is in.</param>
|
||||
/// <seealso cref="Register"/>
|
||||
public void Unregister(Volume volume, int layer)
|
||||
{
|
||||
m_Volumes.Remove(volume);
|
||||
|
||||
foreach (var kvp in m_SortedVolumes)
|
||||
{
|
||||
// Skip layer masks this volume doesn't belong to
|
||||
if ((kvp.Key & (1 << layer)) == 0)
|
||||
continue;
|
||||
|
||||
kvp.Value.Remove(volume);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a <see cref="VolumeComponent"/> is active in a given LayerMask.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A type derived from <see cref="VolumeComponent"/></typeparam>
|
||||
/// <param name="layerMask">The LayerMask to check against</param>
|
||||
/// <returns><c>true</c> if the component is active in the LayerMask, <c>false</c>
|
||||
/// otherwise.</returns>
|
||||
public bool IsComponentActiveInMask<T>(LayerMask layerMask)
|
||||
where T : VolumeComponent
|
||||
{
|
||||
int mask = layerMask.value;
|
||||
|
||||
foreach (var kvp in m_SortedVolumes)
|
||||
{
|
||||
if (kvp.Key != mask)
|
||||
continue;
|
||||
|
||||
foreach (var volume in kvp.Value)
|
||||
{
|
||||
if (!volume.enabled || volume.profileRef == null)
|
||||
continue;
|
||||
|
||||
if (volume.profileRef.TryGet(out T component) && component.active)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void SetLayerDirty(int layer)
|
||||
{
|
||||
Assert.IsTrue(layer >= 0 && layer <= k_MaxLayerCount, "Invalid layer bit");
|
||||
|
||||
foreach (var kvp in m_SortedVolumes)
|
||||
{
|
||||
var mask = kvp.Key;
|
||||
|
||||
if ((mask & (1 << layer)) != 0)
|
||||
m_SortNeeded[mask] = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateVolumeLayer(Volume volume, int prevLayer, int newLayer)
|
||||
{
|
||||
Assert.IsTrue(prevLayer >= 0 && prevLayer <= k_MaxLayerCount, "Invalid layer bit");
|
||||
Unregister(volume, prevLayer);
|
||||
Register(volume, newLayer);
|
||||
}
|
||||
|
||||
// Go through all listed components and lerp overridden values in the global state
|
||||
void OverrideData(VolumeStack stack, List<VolumeComponent> components, float interpFactor)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (!component.active)
|
||||
continue;
|
||||
|
||||
var state = stack.GetComponent(component.GetType());
|
||||
component.Override(state, interpFactor);
|
||||
}
|
||||
}
|
||||
|
||||
// Faster version of OverrideData to force replace values in the global state
|
||||
void ReplaceData(VolumeStack stack, List<VolumeComponent> components)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
var target = stack.GetComponent(component.GetType());
|
||||
int count = component.parameters.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (target.parameters[i] != null)
|
||||
{
|
||||
target.parameters[i].overrideState = false;
|
||||
target.parameters[i].SetValue(component.parameters[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the state of the base type library. This is only used in the editor to handle
|
||||
/// entering and exiting of play mode and domain reload.
|
||||
/// </summary>
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public void CheckBaseTypes()
|
||||
{
|
||||
// Editor specific hack to work around serialization doing funky things when exiting
|
||||
if (m_ComponentsDefaultState == null || (m_ComponentsDefaultState.Count > 0 && m_ComponentsDefaultState[0] == null))
|
||||
ReloadBaseTypes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the state of a given stack. This is only used in the editor to handle entering
|
||||
/// and exiting of play mode and domain reload.
|
||||
/// </summary>
|
||||
/// <param name="stack">The stack to check.</param>
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public void CheckStack(VolumeStack stack)
|
||||
{
|
||||
// The editor doesn't reload the domain when exiting play mode but still kills every
|
||||
// object created while in play mode, like stacks' component states
|
||||
var components = stack.components;
|
||||
|
||||
if (components == null)
|
||||
{
|
||||
stack.Reload(baseComponentTypeArray);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var kvp in components)
|
||||
{
|
||||
if (kvp.Key == null || kvp.Value == null)
|
||||
{
|
||||
stack.Reload(baseComponentTypeArray);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the global state of the Volume manager. Unity usually calls this once per Camera
|
||||
/// in the Update loop before rendering happens.
|
||||
/// </summary>
|
||||
/// <param name="trigger">A reference Transform to consider for positional Volume blending
|
||||
/// </param>
|
||||
/// <param name="layerMask">The LayerMask that the Volume manager uses to filter Volumes that it should consider
|
||||
/// for blending.</param>
|
||||
public void Update(Transform trigger, LayerMask layerMask)
|
||||
{
|
||||
Update(stack, trigger, layerMask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Volume manager and stores the result in a custom <see cref="VolumeStack"/>.
|
||||
/// </summary>
|
||||
/// <param name="stack">The stack to store the blending result into.</param>
|
||||
/// <param name="trigger">A reference Transform to consider for positional Volume blending.
|
||||
/// </param>
|
||||
/// <param name="layerMask">The LayerMask that Unity uses to filter Volumes that it should consider
|
||||
/// for blending.</param>
|
||||
/// <seealso cref="VolumeStack"/>
|
||||
public void Update(VolumeStack stack, Transform trigger, LayerMask layerMask)
|
||||
{
|
||||
Assert.IsNotNull(stack);
|
||||
|
||||
CheckBaseTypes();
|
||||
CheckStack(stack);
|
||||
|
||||
// Start by resetting the global state to default values
|
||||
ReplaceData(stack, m_ComponentsDefaultState);
|
||||
|
||||
bool onlyGlobal = trigger == null;
|
||||
var triggerPos = onlyGlobal ? Vector3.zero : trigger.position;
|
||||
|
||||
// Sort the cached volume list(s) for the given layer mask if needed and return it
|
||||
var volumes = GrabVolumes(layerMask);
|
||||
|
||||
Camera camera = null;
|
||||
// Behavior should be fine even if camera is null
|
||||
if (!onlyGlobal)
|
||||
trigger.TryGetComponent<Camera>(out camera);
|
||||
|
||||
// Traverse all volumes
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Skip volumes that aren't in the scene currently displayed in the scene view
|
||||
if (!IsVolumeRenderedByCamera(volume, camera))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Skip disabled volumes and volumes without any data or weight
|
||||
if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f)
|
||||
continue;
|
||||
|
||||
// Global volumes always have influence
|
||||
if (volume.isGlobal)
|
||||
{
|
||||
OverrideData(stack, volume.profileRef.components, Mathf.Clamp01(volume.weight));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (onlyGlobal)
|
||||
continue;
|
||||
|
||||
// If volume isn't global and has no collider, skip it as it's useless
|
||||
var colliders = m_TempColliders;
|
||||
volume.GetComponents(colliders);
|
||||
if (colliders.Count == 0)
|
||||
continue;
|
||||
|
||||
// Find closest distance to volume, 0 means it's inside it
|
||||
float closestDistanceSqr = float.PositiveInfinity;
|
||||
|
||||
foreach (var collider in colliders)
|
||||
{
|
||||
if (!collider.enabled)
|
||||
continue;
|
||||
|
||||
var closestPoint = collider.ClosestPoint(triggerPos);
|
||||
var d = (closestPoint - triggerPos).sqrMagnitude;
|
||||
|
||||
if (d < closestDistanceSqr)
|
||||
closestDistanceSqr = d;
|
||||
}
|
||||
|
||||
colliders.Clear();
|
||||
float blendDistSqr = volume.blendDistance * volume.blendDistance;
|
||||
|
||||
// Volume has no influence, ignore it
|
||||
// Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but we
|
||||
// can't use a >= comparison as blendDistSqr could be set to 0 in which case
|
||||
// volume would have total influence
|
||||
if (closestDistanceSqr > blendDistSqr)
|
||||
continue;
|
||||
|
||||
// Volume has influence
|
||||
float interpFactor = 1f;
|
||||
|
||||
if (blendDistSqr > 0f)
|
||||
interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
|
||||
|
||||
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
|
||||
OverrideData(stack, volume.profileRef.components, interpFactor * Mathf.Clamp01(volume.weight));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all volumes on a given layer mask sorted by influence.
|
||||
/// </summary>
|
||||
/// <param name="layerMask">The LayerMask that Unity uses to filter Volumes that it should consider.</param>
|
||||
/// <returns>An array of volume.</returns>
|
||||
public Volume[] GetVolumes(LayerMask layerMask)
|
||||
{
|
||||
var volumes = GrabVolumes(layerMask);
|
||||
return volumes.ToArray();
|
||||
}
|
||||
|
||||
List<Volume> GrabVolumes(LayerMask mask)
|
||||
{
|
||||
List<Volume> list;
|
||||
|
||||
if (!m_SortedVolumes.TryGetValue(mask, out list))
|
||||
{
|
||||
// New layer mask detected, create a new list and cache all the volumes that belong
|
||||
// to this mask in it
|
||||
list = new List<Volume>();
|
||||
|
||||
foreach (var volume in m_Volumes)
|
||||
{
|
||||
if ((mask & (1 << volume.gameObject.layer)) == 0)
|
||||
continue;
|
||||
|
||||
list.Add(volume);
|
||||
m_SortNeeded[mask] = true;
|
||||
}
|
||||
|
||||
m_SortedVolumes.Add(mask, list);
|
||||
}
|
||||
|
||||
// Check sorting state
|
||||
bool sortNeeded;
|
||||
if (m_SortNeeded.TryGetValue(mask, out sortNeeded) && sortNeeded)
|
||||
{
|
||||
m_SortNeeded[mask] = false;
|
||||
SortByPriority(list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// Stable insertion sort. Faster than List<T>.Sort() for our needs.
|
||||
static void SortByPriority(List<Volume> volumes)
|
||||
{
|
||||
Assert.IsNotNull(volumes, "Trying to sort volumes of non-initialized layer");
|
||||
|
||||
for (int i = 1; i < volumes.Count; i++)
|
||||
{
|
||||
var temp = volumes[i];
|
||||
int j = i - 1;
|
||||
|
||||
// Sort order is ascending
|
||||
while (j >= 0 && volumes[j].priority > temp.priority)
|
||||
{
|
||||
volumes[j + 1] = volumes[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
volumes[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsVolumeRenderedByCamera(Volume volume, Camera camera)
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER && UNITY_EDITOR
|
||||
// IsGameObjectRenderedByCamera does not behave correctly when camera is null so we have to catch it here.
|
||||
return camera == null ? true : UnityEditor.SceneManagement.StageUtility.IsGameObjectRenderedByCamera(volume.gameObject, camera);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A scope in which a Camera filters a Volume.
|
||||
/// </summary>
|
||||
[Obsolete("VolumeIsolationScope is deprecated, it does not have any effect anymore.")]
|
||||
public struct VolumeIsolationScope : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a scope in which a Camera filters a Volume.
|
||||
/// </summary>
|
||||
/// <param name="unused">Unused parameter.</param>
|
||||
public VolumeIsolationScope(bool unused) {}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the Camera from filtering a Volume.
|
||||
/// </summary>
|
||||
void IDisposable.Dispose() {}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,317 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// An Asset which holds a set of settings to use with a <see cref="Volume"/>.
|
||||
/// </summary>
|
||||
[HelpURL(Documentation.baseURLHDRP + Documentation.version + Documentation.subURL + "Volume-Profile" + Documentation.endURL)]
|
||||
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>
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <see 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 <seealso 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 <seealso 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 <seealso 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the state of a Volume blending update. A global stack is
|
||||
/// available by default in <see cref="VolumeManager"/> but you can also create your own using
|
||||
/// <see cref="VolumeManager.CreateStack"/> if you need to update the manager with specific
|
||||
/// settings and store the results for later use.
|
||||
/// </summary>
|
||||
public sealed class VolumeStack : IDisposable
|
||||
{
|
||||
// Holds the state of _all_ component types you can possibly add on volumes
|
||||
internal Dictionary<Type, VolumeComponent> components;
|
||||
|
||||
internal VolumeStack()
|
||||
{
|
||||
}
|
||||
|
||||
internal void Reload(Type[] baseTypes)
|
||||
{
|
||||
if (components == null)
|
||||
components = new Dictionary<Type, VolumeComponent>();
|
||||
else
|
||||
components.Clear();
|
||||
|
||||
foreach (var type in baseTypes)
|
||||
{
|
||||
var inst = (VolumeComponent)ScriptableObject.CreateInstance(type);
|
||||
components.Add(type, inst);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of the <see cref="VolumeComponent"/> of type <typeparamref name="T"/>
|
||||
/// in the stack.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A type of <see cref="VolumeComponent"/>.</typeparam>
|
||||
/// <returns>The current state of the <see cref="VolumeComponent"/> of type <typeparamref name="T"/>
|
||||
/// in the stack.</returns>
|
||||
public T GetComponent<T>()
|
||||
where T : VolumeComponent
|
||||
{
|
||||
var comp = GetComponent(typeof(T));
|
||||
return (T)comp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of the <see cref="VolumeComponent"/> of the specified type in the
|
||||
/// stack.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of <see cref="VolumeComponent"/> to look for.</param>
|
||||
/// <returns>The current state of the <see cref="VolumeComponent"/> of the specified type,
|
||||
/// or <c>null</c> if the type is invalid.</returns>
|
||||
public VolumeComponent GetComponent(Type type)
|
||||
{
|
||||
components.TryGetValue(type, out var comp);
|
||||
return comp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up the content of this stack. Once a <c>VolumeStack</c> is disposed, it souldn't
|
||||
/// be used anymore.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var component in components)
|
||||
CoreUtils.Destroy(component.Value);
|
||||
|
||||
components.Clear();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user