intial commit

This commit is contained in:
Caleb Sandford deQuincey
2025-06-27 23:27:49 +01:00
commit ecdd3e2a9e
31269 changed files with 4937644 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
/// <summary>
/// An interface for Volumes
/// </summary>
public interface IVolume
{
/// <summary>
/// Specifies whether to apply the Volume to the entire Scene or not.
/// </summary>
bool isGlobal { get; set; }
/// <summary>
/// The colliders of the volume if <see cref="isGlobal"/> is false
/// </summary>
List<Collider> colliders { get; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff60b2884be449c4c84d8b77cdee9422
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,313 @@
using System.Diagnostics.CodeAnalysis;
using Unity.Collections;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
/// <summary>
/// A helper function for interpolating AnimationCurves together. In general, curves can not be directly blended
/// because they will have keypoints at different places. InterpAnimationCurve traverses through the keypoints.
/// If both curves have a keypoint at the same time, they keypoints are trivially lerped together. However
/// if one curve has a keypoint at a time that is missing in the other curve (which is the most common case),
/// InterpAnimationCurve calculates a synthetic keypoint at that time based on value and derivative, and interpolates
/// the resulting keys.
/// Note that this function should only be called by internal rendering code. It creates a small pool of animation
/// curves and reuses them to avoid creating garbage. The number of curves needed is quite small, since curves only need
/// to be used when interpolating multiple volumes together with different curve parameters. The underlying interp
/// function isn't allowed to fail, so in the case where we run out of memory we fall back to returning a single keyframe.
/// </summary>
///
/// <example>
/// <code>
/// {
/// AnimationCurve curve0 = new AnimationCurve();
/// curve0.AddKey(new Keyframe(0.0f, 3.0f));
/// curve0.AddKey(new Keyframe(4.0f, 2.0f));
///
/// AnimationCurve curve1 = new AnimationCurve();
/// curve1.AddKey(new Keyframe(0.0f, 0.0f));
/// curve1.AddKey(new Keyframe(2.0f, 1.0f));
/// curve1.AddKey(new Keyframe(4.0f, 4.0f));
///
/// float t = 0.5f;
/// KeyframeUtility.InterpAnimationCurve(curve0, curve1, t);
///
/// // curve0 now stores the resulting interpolated curve
/// }
/// </code>
/// </example>
public class KeyframeUtility
{
/// <summary>
/// Helper function to remove all control points for an animation curve. Since animation curves are reused in a pool,
/// this function clears existing keys so the curve is ready for reuse.
/// </summary>
/// <param name="curve">The curve to reset.</param>
static public void ResetAnimationCurve(AnimationCurve curve)
{
curve.ClearKeys();
}
static private Keyframe LerpSingleKeyframe(Keyframe lhs, Keyframe rhs, float t)
{
var ret = new Keyframe();
ret.time = Mathf.Lerp(lhs.time, rhs.time, t);
ret.value = Mathf.Lerp(lhs.value, rhs.value, t);
ret.inTangent = Mathf.Lerp(lhs.inTangent, rhs.inTangent, t);
ret.outTangent = Mathf.Lerp(lhs.outTangent, rhs.outTangent, t);
ret.inWeight = Mathf.Lerp(lhs.inWeight, rhs.inWeight, t);
ret.outWeight = Mathf.Lerp(lhs.outWeight, rhs.outWeight, t);
// it's not possible to lerp the weightedMode, so use the lhs mode.
ret.weightedMode = lhs.weightedMode;
// Note: ret.tangentMode is deprecated, so we will use the value from the constructor
return ret;
}
/// In an animation curve, the inTangent and outTangent don't match the edge of the curve. For example,
/// the first key might have inTangent=3.0f but the actual incoming tangent is 0.0 because the curve is
/// clamped outside the time domain. So this helper fetches a key, but zeroes out the inTangent of the first
/// key and the outTangent of the last key.
static private Keyframe GetKeyframeAndClampEdge([DisallowNull] NativeArray<Keyframe> keys, int index)
{
var lastKeyIndex = keys.Length - 1;
if (index < 0 || index > lastKeyIndex)
{
Debug.LogWarning("Invalid index in GetKeyframeAndClampEdge. This is likely a bug.");
return new Keyframe();
}
var currKey = keys[index];
if (index == 0)
{
currKey.inTangent = 0.0f;
}
if (index == lastKeyIndex)
{
currKey.outTangent = 0.0f;
}
return currKey;
}
/// Fetch a key from the keys list. If index<0, then expand the first key backwards to startTime. If index>=keys.length,
/// then extend the last key to endTime. Keys must be a valid array with at least one element.
static private Keyframe FetchKeyFromIndexClampEdge([DisallowNull] NativeArray<Keyframe> keys, int index, float segmentStartTime, float segmentEndTime)
{
float startTime = Mathf.Min(segmentStartTime, keys[0].time);
float endTime = Mathf.Max(segmentEndTime, keys[keys.Length - 1].time);
float startValue = keys[0].value;
float endValue = keys[keys.Length - 1].value;
// In practice, we are lerping animcurves for post processing curves that are always clamping at the begining and the end,
// so we are not implementing the other wrap modes like Loop, PingPong, etc.
Keyframe ret;
if (index < 0)
{
// when you are at a time either before the curve start time the value is clamped to the start time and the input tangent is ignored.
ret = new Keyframe(startTime, startValue, 0.0f, 0.0f);
}
else if (index >= keys.Length)
{
// if we are after the end of the curve, there slope is always zero just like before the start of a curve
var lastKey = keys[keys.Length - 1];
ret = new Keyframe(endTime, endValue, 0.0f, 0.0f);
}
else
{
// only remaining case is that we have a proper index
ret = GetKeyframeAndClampEdge(keys, index);
}
return ret;
}
/// Given a desiredTime, interpoloate between two keys to find the value and derivative. This function assumes that lhsKey.time <= desiredTime <= rhsKey.time,
/// but will return a reasonable float value if that's not the case.
static private void EvalCurveSegmentAndDeriv(out float dstValue, out float dstDeriv, Keyframe lhsKey, Keyframe rhsKey, float desiredTime)
{
// This is the same epsilon used internally
const float epsilon = 0.0001f;
float currTime = Mathf.Clamp(desiredTime, lhsKey.time, rhsKey.time);
// (lhsKey.time <= rhsKey.time) should always be true. But theoretically, if garbage values get passed in, the value would
// be clamped here to epsilon, and we would still end up with a reasonable value for dx.
float dx = Mathf.Max(rhsKey.time - lhsKey.time, epsilon);
float dy = rhsKey.value - lhsKey.value;
float length = 1.0f / dx;
float lengthSqr = length * length;
float m1 = lhsKey.outTangent;
float m2 = rhsKey.inTangent;
float d1 = m1 * dx;
float d2 = m2 * dx;
// Note: The coeffecients are calculated to match what the editor does internally. These coeffeceients expect a
// t in the range of [0,dx]. We could change the function to accept a range between [0,1], but then this logic would
// be different from internal editor logic which could cause subtle bugs later.
float c0 = (d1 + d2 - dy - dy) * lengthSqr * length;
float c1 = (dy + dy + dy - d1 - d1 - d2) * lengthSqr;
float c2 = m1;
float c3 = lhsKey.value;
float t = Mathf.Clamp(currTime - lhsKey.time, 0.0f, dx);
dstValue = (t * (t * (t * c0 + c1) + c2)) + c3;
dstDeriv = (t * (3.0f * t * c0 + 2.0f * c1)) + c2;
}
/// lhsIndex and rhsIndex are the indices in the keys array. The lhsIndex/rhsIndex may be -1, in which it creates a synthetic first key
/// at startTime, or beyond the length of the array, in which case it creates a synthetic key at endTime.
static private Keyframe EvalKeyAtTime([DisallowNull] NativeArray<Keyframe> keys, int lhsIndex, int rhsIndex, float startTime, float endTime, float currTime)
{
var lhsKey = KeyframeUtility.FetchKeyFromIndexClampEdge(keys, lhsIndex, startTime, endTime);
var rhsKey = KeyframeUtility.FetchKeyFromIndexClampEdge(keys, rhsIndex, startTime, endTime);
float currValue;
float currDeriv;
KeyframeUtility.EvalCurveSegmentAndDeriv(out currValue, out currDeriv, lhsKey, rhsKey, currTime);
return new Keyframe(currTime, currValue, currDeriv, currDeriv);
}
/// <summary>
/// Interpolates two AnimationCurves. Since both curves likely have control points at different places
/// in the curve, this method will create a new curve from the union of times between both curves. However, to avoid creating
/// garbage, this function will always replace the keys of lhsAndResultCurve with the final result, and return lhsAndResultCurve.
/// </summary>
/// <param name="lhsAndResultCurve">The start value. Additionaly, this instance will be reused and returned as the result.</param>
/// <param name="rhsCurve">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
static public void InterpAnimationCurve(ref AnimationCurve lhsAndResultCurve, [DisallowNull] AnimationCurve rhsCurve, float t)
{
if (t <= 0.0f || rhsCurve.length == 0)
{
// no op. lhsAndResultCurve is already the result
}
else if (t >= 1.0f || lhsAndResultCurve.length == 0)
{
// In this case the obvious solution would be to return the rhsCurve. BUT (!) the lhsCurve and rhsCurve are different. This function is
// called by:
// stateParam.Interp(stateParam, toParam, interpFactor);
//
// stateParam (lhsCurve) is a temporary in/out parameter, but toParam (rhsCurve) might point to the original component, so it's unsafe to
// change that data. Thus, we need to copy the keys from the rhsCurve to the lhsCurve instead of returning rhsCurve.
lhsAndResultCurve.CopyFrom(rhsCurve);
}
else
{
// Note: If we reached this code, we are guaranteed that both lhsCurve and rhsCurve are valid with at least 1 key
// create a native array for the temp keys to avoid GC
var lhsCurveKeys = new NativeArray<Keyframe>(lhsAndResultCurve.length, Allocator.Temp);
var rhsCurveKeys = new NativeArray<Keyframe>(rhsCurve.length, Allocator.Temp);
for (int i = 0; i < lhsAndResultCurve.length; i++)
{
lhsCurveKeys[i] = lhsAndResultCurve[i];
}
for (int i = 0; i < rhsCurve.length; i++)
{
rhsCurveKeys[i] = rhsCurve[i];
}
float startTime = Mathf.Min(lhsCurveKeys[0].time, rhsCurveKeys[0].time);
float endTime = Mathf.Max(lhsCurveKeys[lhsAndResultCurve.length - 1].time, rhsCurveKeys[rhsCurve.length - 1].time);
// we don't know how many keys the resulting curve will have (because we will compact keys that are at the exact
// same time), but in most cases we will need the worst case number of keys. So allocate the worst case.
int maxNumKeys = lhsAndResultCurve.length + rhsCurve.length;
int currNumKeys = 0;
var dstKeys = new NativeArray<Keyframe>(maxNumKeys, Allocator.Temp);
int lhsKeyCurr = 0;
int rhsKeyCurr = 0;
while (lhsKeyCurr < lhsCurveKeys.Length || rhsKeyCurr < rhsCurveKeys.Length)
{
// the index is considered invalid once it goes off the end of the array
bool lhsValid = lhsKeyCurr < lhsCurveKeys.Length;
bool rhsValid = rhsKeyCurr < rhsCurveKeys.Length;
// it's actually impossible for lhsKey/rhsKey to be uninitialized, but have to
// add initialize here to prevent compiler erros
var lhsKey = new Keyframe();
var rhsKey = new Keyframe();
if (lhsValid && rhsValid)
{
lhsKey = GetKeyframeAndClampEdge(lhsCurveKeys, lhsKeyCurr);
rhsKey = GetKeyframeAndClampEdge(rhsCurveKeys, rhsKeyCurr);
if (lhsKey.time == rhsKey.time)
{
lhsKeyCurr++;
rhsKeyCurr++;
}
else if (lhsKey.time < rhsKey.time)
{
// in this case:
// rhsKey[curr-1].time <= lhsKey.time <= rhsKey[curr].time
// so interpolate rhsKey at the lhsKey.time.
rhsKey = KeyframeUtility.EvalKeyAtTime(rhsCurveKeys, rhsKeyCurr - 1, rhsKeyCurr, startTime, endTime, lhsKey.time);
lhsKeyCurr++;
}
else
{
// only case left is (lhsKey.time > rhsKey.time)
Assert.IsTrue(lhsKey.time > rhsKey.time);
// this is the reverse of the lhs key case
// lhsKey[curr-1].time <= rhsKey.time <= lhsKey[curr].time
// so interpolate lhsKey at the rhsKey.time.
lhsKey = KeyframeUtility.EvalKeyAtTime(lhsCurveKeys, lhsKeyCurr - 1, lhsKeyCurr, startTime, endTime, rhsKey.time);
rhsKeyCurr++;
}
}
else if (lhsValid)
{
// we are still processing lhsKeys, but we are out of rhsKeys, so increment lhs and evaluate rhs
lhsKey = GetKeyframeAndClampEdge(lhsCurveKeys, lhsKeyCurr);
// rhs will be evaluated between the last rhs key and the extrapolated rhs key at the end time
rhsKey = KeyframeUtility.EvalKeyAtTime(rhsCurveKeys, rhsKeyCurr - 1, rhsKeyCurr, startTime, endTime, lhsKey.time);
lhsKeyCurr++;
}
else
{
// either lhsValid is True, rhsValid is True, or they are both True. So to miss the first two cases,
// right here rhsValid must be true.
Assert.IsTrue(rhsValid);
// we still have rhsKeys to lerp, but we are out of lhsKeys, to increment rhs and evaluate lhs
rhsKey = GetKeyframeAndClampEdge(rhsCurveKeys, rhsKeyCurr);
// lhs will be evaluated between the last lhs key and the extrapolated lhs key at the end time
lhsKey = KeyframeUtility.EvalKeyAtTime(lhsCurveKeys, lhsKeyCurr - 1, lhsKeyCurr, startTime, endTime, rhsKey.time);
rhsKeyCurr++;
}
var dstKey = KeyframeUtility.LerpSingleKeyframe(lhsKey, rhsKey, t);
dstKeys[currNumKeys] = dstKey;
currNumKeys++;
}
// Replace the keys in lhsAndResultCurve with our interpolated curve.
KeyframeUtility.ResetAnimationCurve(lhsAndResultCurve);
for (int i = 0; i < currNumKeys; i++)
{
lhsAndResultCurve.AddKey(dstKeys[i]);
}
dstKeys.Dispose();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e28576c73484c844b4b56a5352abe6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using UnityEngine.Serialization;
namespace UnityEngine.Rendering
{
/// <summary>
/// A generic Volume component holding a <see cref="VolumeProfile"/>.
/// </summary>
[CurrentPipelineHelpURL("Volumes")]
[ExecuteAlways]
[AddComponentMenu("Miscellaneous/Volume")]
public class Volume : MonoBehaviour, IVolume
{
[SerializeField, FormerlySerializedAs("isGlobal")]
bool m_IsGlobal = true;
/// <summary>
/// Specifies whether to apply the Volume to the entire Scene or not.
/// </summary>
public bool isGlobal
{
get => m_IsGlobal;
set => m_IsGlobal = value;
}
/// <summary>
/// A value which determines which Volume is being used when Volumes have an equal amount of influence on the Scene. Volumes with a higher priority will override lower ones.
/// </summary>
[Delayed]
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>
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)]
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)
{
m_InternalProfile.name = sharedProfile.name;
foreach (var item in sharedProfile.components)
{
var itemCopy = Instantiate(item);
m_InternalProfile.components.Add(itemCopy);
}
}
}
return m_InternalProfile;
}
set => m_InternalProfile = value;
}
internal List<Collider> m_Colliders = new List<Collider>();
/// <summary>
/// The colliders of the volume if <see cref="isGlobal"/> is false
/// </summary>
public List<Collider> colliders => m_Colliders;
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);
GetComponents(m_Colliders);
}
void OnDisable()
{
VolumeManager.instance.Unregister(this);
}
void Update()
{
UpdateLayer();
UpdatePriority();
#if UNITY_EDITOR
// In the editor, we refresh the list of colliders at every frame because it's frequent to add/remove them
GetComponents(m_Colliders);
#endif
}
internal void UpdateLayer()
{
// 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 :/
int layer = gameObject.layer;
if (layer == m_PreviousLayer)
return;
VolumeManager.instance.UpdateVolumeLayer(this, m_PreviousLayer, layer);
m_PreviousLayer = layer;
}
internal void UpdatePriority()
{
if (!(Math.Abs(priority - m_PreviousPriority) > Mathf.Epsilon))
return;
// 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.
VolumeManager.instance.SetLayerDirty(gameObject.layer);
m_PreviousPriority = priority;
}
void OnValidate()
{
blendDistance = Mathf.Max(blendDistance, 0f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 172515602e62fb746b5d573b38a5fe58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 32b23d5d74f3aee4f9364e34e2f59379, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
internal class VolumeCollection
{
// Max amount of layers available in Unity
internal const int k_MaxLayerCount = 32;
// Cached lists of all volumes (sorted by priority) by layer mask
readonly Dictionary<int, List<Volume>> m_SortedVolumes = new();
// Holds all the registered volumes
readonly List<Volume> m_Volumes = new();
// Keep track of sorting states for layer masks
readonly Dictionary<int, bool> m_SortNeeded = new();
public int count => m_Volumes.Count;
public bool Register(Volume volume, int layer)
{
if (volume == null)
throw new ArgumentNullException(nameof(volume), "The volume to register is null");
if (m_Volumes.Contains(volume))
return false;
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);
}
SetLayerIndexDirty(layer);
return true;
}
public bool Unregister(Volume volume, int layer)
{
if (volume == null)
throw new ArgumentNullException(nameof(volume), "The volume to unregister is null");
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);
}
SetLayerIndexDirty(layer);
return true;
}
public bool ChangeLayer(Volume volume, int previousLayerIndex, int currentLayerIndex)
{
if (volume == null)
throw new ArgumentNullException(nameof(volume), "The volume to change layer is null");
Assert.IsTrue(previousLayerIndex >= 0 && previousLayerIndex <= k_MaxLayerCount, "Invalid layer bit");
Unregister(volume, previousLayerIndex);
return Register(volume, currentLayerIndex);
}
// Stable insertion sort. Faster than List<T>.Sort() for our needs.
internal static void SortByPriority(List<Volume> volumes)
{
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;
}
}
public 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>();
var numVolumes = m_Volumes.Count;
for (int i = 0; i < numVolumes; i++)
{
var volume = m_Volumes[i];
if ((mask & (1 << volume.gameObject.layer)) == 0)
continue;
list.Add(volume);
m_SortNeeded[mask] = true;
}
m_SortedVolumes.Add(mask, list);
}
// Check sorting state
if (m_SortNeeded.TryGetValue(mask, out var sortNeeded) && sortNeeded)
{
m_SortNeeded[mask] = false;
SortByPriority(list);
}
return list;
}
public void SetLayerIndexDirty(int layerIndex)
{
Assert.IsTrue(layerIndex >= 0 && layerIndex <= k_MaxLayerCount, "Invalid layer bit");
foreach (var kvp in m_SortedVolumes)
{
var mask = kvp.Key;
if ((mask & (1 << layerIndex)) != 0)
m_SortNeeded[mask] = true;
}
}
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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 993a8382d5f766441859ffb0146774d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
#if UNITY_EDITOR
using System;
using UnityEditor;
namespace UnityEngine.Rendering
{
public partial class VolumeComponent : IApplyRevertPropertyContextMenuItemProvider
{
public bool TryGetRevertMethodForFieldName(SerializedProperty property, out Action<SerializedProperty> revertMethod)
{
revertMethod = property =>
{
var targetObject = property.serializedObject.targetObject;
var type = targetObject.GetType();
var defaultVolumeComponent = (VolumeComponent) CreateInstance(type);
Undo.RecordObject(targetObject, $"Revert property {property.propertyPath} from {property.serializedObject}");
SerializedObject serializedObject = new SerializedObject(defaultVolumeComponent);
if (property.propertyType == SerializedPropertyType.ObjectReference)
{
property.objectReferenceValue = null;
}
else
{
var serializedProperty = serializedObject.FindProperty(property.propertyPath);
property.serializedObject.CopyFromSerializedProperty(serializedProperty);
VolumeManager.instance.OnVolumeComponentChanged(targetObject as VolumeComponent);
}
property.serializedObject.ApplyModifiedProperties();
};
return true;
}
public string GetSourceTerm()
{
return "Property";
}
public bool TryGetApplyMethodForFieldName(SerializedProperty property, out Action<SerializedProperty> applyMethod)
{
applyMethod = null;
return false;
}
public string GetSourceName(Component comp)
{
return string.Empty;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c62ee15c68690246a39d8c29726cb19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,358 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
namespace UnityEngine.Rendering
{
/// <summary>
/// This attribute allows you to add commands to the <b>Add Override</b> popup menu
/// on Volumes.
/// To filter VolumeComponentMenu based on current Render Pipeline, add SupportedOnRenderPipeline attribute to the class alongside with this attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public 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 <see 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>
/// This attribute allows you to add commands to the <b>Add Override</b> popup menu
/// on Volumes and specify for which render pipelines will be supported
/// </summary>
[Obsolete(@"VolumeComponentMenuForRenderPipelineAttribute is deprecated. Use VolumeComponentMenu with SupportedOnCurrentPipeline instead. #from(2023.1)", false)]
public class VolumeComponentMenuForRenderPipeline : VolumeComponentMenu
{
/// <summary>
/// The list of pipeline types that the target class supports
/// </summary>
public Type[] pipelineTypes { get; }
/// <summary>
/// Creates a new <see cref="VolumeComponentMenuForRenderPipeline"/> instance.
/// </summary>
/// <param name="menu">The name of the entry in the override list. You can use slashes to
/// create sub-menus.</param>
/// <param name="pipelineTypes">The list of pipeline types that the target class supports</param>
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;
}
}
/// <summary>
/// An attribute to hide the volume component to be added through `Add Override` button on the volume component list
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
[Obsolete("VolumeComponentDeprecated has been deprecated (UnityUpgradable) -> [UnityEngine] UnityEngine.HideInInspector", 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, VolumeComponentMenuForRenderPipeline("Custom/Example Component")]
/// public class ExampleComponent : VolumeComponent
/// {
/// public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
/// }
/// </code>
/// </example>
[Serializable]
public partial class VolumeComponent : ScriptableObject
{
/// <summary>
/// Local attribute for VolumeComponent fields only.
/// It handles relative indentation of a property for inspector.
/// </summary>
public sealed class Indent : PropertyAttribute
{
/// <summary> Relative indent amount registered in this attribute </summary>
public readonly int relativeAmount;
/// <summary> Constructor </summary>
/// <param name="relativeAmount">Relative indent change to use</param>
public Indent(int relativeAmount = 1)
=> this.relativeAmount = relativeAmount;
}
/// <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>
/// The backing storage of <see cref="parameters"/>. Use this for performance-critical work.
/// </summary>
internal readonly List<VolumeParameter> parameterList = new();
ReadOnlyCollection<VolumeParameter> m_ParameterReadOnlyCollection;
/// <summary>
/// A read-only collection of all the <see cref="VolumeParameter"/>s defined in this class.
/// </summary>
public ReadOnlyCollection<VolumeParameter> parameters
{
get
{
if (m_ParameterReadOnlyCollection == null)
m_ParameterReadOnlyCollection = parameterList.AsReadOnly();
return m_ParameterReadOnlyCollection;
}
}
/// <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)
{
VolumeParameter volumeParameter = (VolumeParameter)field.GetValue(o);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
var attr = (DisplayInfoAttribute[])field.GetCustomAttributes(typeof(DisplayInfoAttribute), true);
if (attr.Length != 0)
{
volumeParameter.debugId = attr[0].name;
}
else
{
volumeParameter.debugId = field.Name;
}
#endif
parameters.Add(volumeParameter);
}
}
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
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.");
}
}
/// <summary>
/// Unity calls this method when the object goes out of scope.
/// </summary>
protected virtual void OnDisable()
{
foreach (var parameter in parameterList)
{
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>
/// <para> Below is the default implementation for blending:</para>
/// <code>
/// public virtual void Override(VolumeComponent state, float interpFactor)
/// {
/// int count = parameters.Count;
///
/// for (int i = 0; i &lt; 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);
/// }
/// }
/// }
/// </code>
/// </example>
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);
}
}
}
/// <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(parameterList, 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 < parameterList.Count; i++)
hash = hash * 23 + parameterList[i].GetHashCode();
return hash;
}
}
/// <summary>
/// Returns true if any of the volume properites has been overridden.
/// </summary>
/// <returns>True if any of the volume properites has been overridden.</returns>
public bool AnyPropertiesIsOverridden()
{
for (int i = 0; i < parameterList.Count; ++i)
{
if (parameterList[i].overrideState) return true;
}
return false;
}
/// <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()
{
if (parameterList == null)
return;
for (int i = 0; i < parameterList.Count; i++)
{
if (parameterList[i] != null)
parameterList[i].Release();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 556096baecf4aeb4e9f0d3efce77e08f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,806 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Unity.Profiling;
using UnityEngine.Assertions;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Rendering;
#endif
namespace UnityEngine.Rendering
{
/// <summary>
/// A global manager that tracks all the Volumes in the currently loaded Scenes and does all the
/// interpolation work.
/// </summary>
public sealed partial class VolumeManager
{
static readonly ProfilerMarker k_ProfilerMarkerUpdate = new ("VolumeManager.Update");
static readonly ProfilerMarker k_ProfilerMarkerReplaceData = new ("VolumeManager.ReplaceData");
static readonly ProfilerMarker k_ProfilerMarkerEvaluateVolumeDefaultState = new ("VolumeManager.EvaluateVolumeDefaultState");
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; 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 => baseComponentTypeArray;
static readonly Dictionary<Type, List<(string, Type)>> s_SupportedVolumeComponentsForRenderPipeline = new();
internal List<(string, Type)> GetVolumeComponentsForDisplay(Type currentPipelineAssetType)
{
if (currentPipelineAssetType == null)
return new List<(string, Type)>();
if (!currentPipelineAssetType.IsSubclassOf(typeof(RenderPipelineAsset)))
throw new ArgumentException(nameof(currentPipelineAssetType));
if (s_SupportedVolumeComponentsForRenderPipeline.TryGetValue(currentPipelineAssetType, out var supportedVolumeComponents))
return supportedVolumeComponents;
if (baseComponentTypeArray == null)
LoadBaseTypes(currentPipelineAssetType);
supportedVolumeComponents = BuildVolumeComponentDisplayList(baseComponentTypeArray);
s_SupportedVolumeComponentsForRenderPipeline[currentPipelineAssetType] = supportedVolumeComponents;
return supportedVolumeComponents;
}
List<(string, Type)> BuildVolumeComponentDisplayList(Type[] types)
{
if (types == null)
throw new ArgumentNullException(nameof(types));
var volumes = new List<(string, Type)>();
foreach (var t in types)
{
string path = string.Empty;
bool skipComponent = false;
// Look for the attributes of this volume component and decide how is added and if it needs to be skipped
var attrs = t.GetCustomAttributes(false);
foreach (var attr in attrs)
{
switch (attr)
{
case VolumeComponentMenu attrMenu:
{
path = attrMenu.menu;
break;
}
case HideInInspector:
case ObsoleteAttribute:
skipComponent = true;
break;
}
}
if (skipComponent)
continue;
// If no attribute or in case something went wrong when grabbing it, fallback to a
// beautified class name
if (string.IsNullOrEmpty(path))
{
#if UNITY_EDITOR
path = ObjectNames.NicifyVariableName(t.Name);
#else
path = t.Name;
#endif
}
volumes.Add((path, t));
}
return volumes
.OrderBy(i => i.Item1)
.ToList();
}
/// <summary>
/// The current list of all available types that derive from <see cref="VolumeComponent"/>.
/// </summary>
public Type[] baseComponentTypeArray { get; internal set; } // internal only for tests
/// <summary>
/// Global default profile that provides default values for volume components. VolumeManager applies
/// this profile to its internal component default state first, before <see cref="qualityDefaultProfile"/>
/// and <see cref="customDefaultProfiles"/>.
/// </summary>
public VolumeProfile globalDefaultProfile { get; private set; }
/// <summary>
/// Quality level specific volume profile that is applied to the default state after
/// <see cref="globalDefaultProfile"/> and before <see cref="customDefaultProfiles"/>.
/// </summary>
public VolumeProfile qualityDefaultProfile { get; private set; }
/// <summary>
/// Collection of additional default profiles that can be used to override default values for volume components
/// in a way that doesn't cause any overhead at runtime. Unity applies these Volume Profiles to its internal
/// component default state after <see cref="globalDefaultProfile"/> and <see cref="qualityDefaultProfile"/>.
/// The custom profiles are applied in the order that they appear in the collection.
/// </summary>
public ReadOnlyCollection<VolumeProfile> customDefaultProfiles { get; private set; }
private readonly VolumeCollection m_VolumeCollection = new VolumeCollection();
// 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)
// The "Default State" is evaluated as follows:
// Default-constructed VolumeComponents (VolumeParameter values coming from code)
// + Values from globalDefaultProfile
// + Values from qualityDefaultProfile
// + Values from customDefaultProfiles
// = Default State.
VolumeComponent[] m_ComponentsDefaultState;
// Flat list of every volume parameter in default state for faster per-frame stack reset.
internal VolumeParameter[] m_ParametersDefaultState;
/// <summary>
/// Retrieve the default state for a given VolumeComponent type. Default state is defined as
/// "default-constructed VolumeComponent + Default Profiles evaluated in order".
/// </summary>
/// <remarks>
/// If you want just the VolumeComponent with default-constructed values without overrides from
/// Default Profiles, use <see cref="ScriptableObject.CreateInstance(Type)"/>.
/// </remarks>
/// <param name="volumeComponentType">Type of VolumeComponent</param>
/// <returns>VolumeComponent in default state, or null if the type is not found</returns>
public VolumeComponent GetVolumeComponentDefaultState(Type volumeComponentType)
{
if (!typeof(VolumeComponent).IsAssignableFrom(volumeComponentType))
return null;
foreach (VolumeComponent component in m_ComponentsDefaultState)
{
if (component.GetType() == volumeComponentType)
return component;
}
return null;
}
// Recycled list used for volume traversal
readonly List<Collider> m_TempColliders = new(8);
// The default stack the volume manager uses.
// We cache this as users able to change the stack through code and
// we want to be able to switch to the default one through the ResetMainStack() function.
VolumeStack m_DefaultStack;
// List of stacks created through VolumeManager.
readonly List<VolumeStack> m_CreatedVolumeStacks = new();
// Internal for tests
internal VolumeManager()
{
}
// Note: The "isInitialized" state and explicit Initialize/Deinitialize are only required because VolumeManger
// is a singleton whose lifetime exceeds that of RenderPipelines. Thus it must be initialized & deinitialized
// explicitly by the RP to handle pipeline switch gracefully. It would be better to get rid of singletons and
// have the RP own the class instance instead.
/// <summary>
/// Returns whether <see cref="VolumeManager.Initialize(VolumeProfile,VolumeProfile)"/> has been called, and the
/// class is in valid state. It is not valid to use VolumeManager before this returns true.
/// </summary>
public bool isInitialized { get; private set; }
/// <summary>
/// Initialize VolumeManager with specified global and quality default volume profiles that are used to evaluate
/// the default state of all VolumeComponents. Should be called from <see cref="RenderPipeline"/> constructor.
/// </summary>
/// <param name="globalDefaultVolumeProfile">Global default volume profile.</param>
/// <param name="qualityDefaultVolumeProfile">Quality default volume profile.</param>
public void Initialize(VolumeProfile globalDefaultVolumeProfile = null, VolumeProfile qualityDefaultVolumeProfile = null)
{
Debug.Assert(!isInitialized);
Debug.Assert(m_CreatedVolumeStacks.Count == 0);
LoadBaseTypes(GraphicsSettings.currentRenderPipelineAssetType);
InitializeVolumeComponents();
globalDefaultProfile = globalDefaultVolumeProfile;
qualityDefaultProfile = qualityDefaultVolumeProfile;
EvaluateVolumeDefaultState();
m_DefaultStack = CreateStack();
stack = m_DefaultStack;
isInitialized = true;
}
/// <summary>
/// Deinitialize VolumeManager. Should be called from <see cref="RenderPipeline.Dispose()"/>.
/// </summary>
public void Deinitialize()
{
Debug.Assert(isInitialized);
DestroyStack(m_DefaultStack);
m_DefaultStack = null;
foreach (var s in m_CreatedVolumeStacks)
s.Dispose();
m_CreatedVolumeStacks.Clear();
baseComponentTypeArray = null;
globalDefaultProfile = null;
qualityDefaultProfile = null;
customDefaultProfiles = null;
isInitialized = false;
}
/// <summary>
/// Assign the given VolumeProfile as the global default profile and update the default component state.
/// </summary>
/// <param name="profile">The VolumeProfile to use as the global default profile.</param>
public void SetGlobalDefaultProfile(VolumeProfile profile)
{
globalDefaultProfile = profile;
EvaluateVolumeDefaultState();
}
/// <summary>
/// Assign the given VolumeProfile as the quality default profile and update the default component state.
/// </summary>
/// <param name="profile">The VolumeProfile to use as the quality level default profile.</param>
public void SetQualityDefaultProfile(VolumeProfile profile)
{
qualityDefaultProfile = profile;
EvaluateVolumeDefaultState();
}
/// <summary>
/// Assign the given VolumeProfiles as custom default profiles and update the default component state.
/// </summary>
/// <param name="profiles">List of VolumeProfiles to set as default profiles, or null to clear them.</param>
public void SetCustomDefaultProfiles(List<VolumeProfile> profiles)
{
var validProfiles = profiles ?? new List<VolumeProfile>();
validProfiles.RemoveAll(x => x == null);
customDefaultProfiles = new ReadOnlyCollection<VolumeProfile>(validProfiles);
EvaluateVolumeDefaultState();
}
/// <summary>
/// Call when a VolumeProfile is modified to trigger default state update if necessary.
/// </summary>
/// <param name="profile">VolumeProfile that has changed.</param>
public void OnVolumeProfileChanged(VolumeProfile profile)
{
if (!isInitialized)
return;
if (globalDefaultProfile == profile ||
qualityDefaultProfile == profile ||
(customDefaultProfiles != null && customDefaultProfiles.Contains(profile)))
EvaluateVolumeDefaultState();
}
/// <summary>
/// Call when a VolumeComponent is modified to trigger default state update if necessary.
/// </summary>
/// <param name="component">VolumeComponent that has changed.</param>
public void OnVolumeComponentChanged(VolumeComponent component)
{
var defaultProfiles = new List<VolumeProfile> { globalDefaultProfile, globalDefaultProfile };
if (customDefaultProfiles != null)
defaultProfiles.AddRange(customDefaultProfiles);
foreach (var defaultProfile in defaultProfiles)
{
if (defaultProfile.components.Contains(component))
{
EvaluateVolumeDefaultState();
return;
}
}
}
/// <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>A new <see cref="VolumeStack"/> instance with freshly loaded components.</returns>
/// <seealso cref="VolumeStack"/>
/// <seealso cref="Update(VolumeStack,Transform,LayerMask)"/>
public VolumeStack CreateStack()
{
var stack = new VolumeStack();
stack.Reload(baseComponentTypeArray);
m_CreatedVolumeStacks.Add(stack);
return stack;
}
/// <summary>
/// Resets the main stack to be the default one.
/// Call this function if you've assigned the main stack to something other than the default one.
/// </summary>
public void ResetMainStack()
{
stack = m_DefaultStack;
}
/// <summary>
/// Destroy a Volume Stack
/// </summary>
/// <param name="stack">Volume Stack that needs to be destroyed.</param>
public void DestroyStack(VolumeStack stack)
{
m_CreatedVolumeStacks.Remove(stack);
stack.Dispose();
}
// For now, if a user is having a VolumeComponent with the old attribute for filtering support.
// We are adding it to the supported volume components, but we are showing a warning.
bool IsSupportedByObsoleteVolumeComponentMenuForRenderPipeline(Type t, Type pipelineAssetType)
{
var legacySupported = false;
#pragma warning disable CS0618
var legacyPipelineAttribute = t.GetCustomAttribute<VolumeComponentMenuForRenderPipeline>();
if (legacyPipelineAttribute != null)
{
Debug.LogWarning($"{nameof(VolumeComponentMenuForRenderPipeline)} is deprecated, use {nameof(SupportedOnRenderPipelineAttribute)} and {nameof(VolumeComponentMenu)} with {t} instead. #from(2023.1)");
#if UNITY_EDITOR
var renderPipelineTypeFromAsset = RenderPipelineEditorUtility.GetPipelineTypeFromPipelineAssetType(pipelineAssetType);
for (int i = 0; i < legacyPipelineAttribute.pipelineTypes.Length; ++i)
{
if (legacyPipelineAttribute.pipelineTypes[i] == renderPipelineTypeFromAsset)
{
legacySupported = true;
break;
}
}
#endif
}
#pragma warning restore CS0618
return legacySupported;
}
// This will be called only once at runtime and on domain reload / pipeline switch in the editor
// as we need to keep track of any compatible component in the project
internal void LoadBaseTypes(Type pipelineAssetType)
{
// Grab all the component types we can find that are compatible with current pipeline
using (ListPool<Type>.Get(out var list))
{
foreach (var t in CoreUtils.GetAllTypesDerivedFrom<VolumeComponent>())
{
if (t.IsAbstract)
continue;
var isSupported = SupportedOnRenderPipelineAttribute.IsTypeSupportedOnRenderPipeline(t, pipelineAssetType) ||
IsSupportedByObsoleteVolumeComponentMenuForRenderPipeline(t, pipelineAssetType);
if (isSupported)
list.Add(t);
}
baseComponentTypeArray = list.ToArray();
}
}
internal void InitializeVolumeComponents()
{
// Call custom static Init method if present
var flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
foreach (var type in baseComponentTypeArray)
{
var initMethod = type.GetMethod("Init", flags);
if (initMethod != null)
{
initMethod.Invoke(null, null);
}
}
}
// Evaluate static default values for VolumeComponents, which is the baseline to reset the values to at the start of Update.
internal void EvaluateVolumeDefaultState()
{
if (baseComponentTypeArray == null || baseComponentTypeArray.Length == 0)
return;
using var profilerScope = k_ProfilerMarkerEvaluateVolumeDefaultState.Auto();
// TODO consider if the "component default values" array should be kept in memory separately. Creating the
// instances is likely the slowest operation here, so doing that would mean it can only be done once in
// Initialize() and the default state can be updated a lot quicker.
// First, default-construct all VolumeComponents
List<VolumeComponent> componentsDefaultStateList = new();
foreach (var type in baseComponentTypeArray)
{
componentsDefaultStateList.Add((VolumeComponent) ScriptableObject.CreateInstance(type));
}
void ApplyDefaultProfile(VolumeProfile profile)
{
if (profile == null)
return;
for (int i = 0; i < profile.components.Count; i++)
{
var profileComponent = profile.components[i];
var defaultStateComponent = componentsDefaultStateList.FirstOrDefault(
x => x.GetType() == profileComponent.GetType());
if (defaultStateComponent != null && profileComponent.active)
{
// Ideally we would just call SetValue here. However, there are custom non-trivial
// implementations of VolumeParameter.Interp() (such as DiffusionProfileList) that make it
// necessary for us to call the it. This ensures the new DefaultProfile behavior works
// consistently with the old HDRP implementation where the Default Profile was implemented as
// a regular global volume inside the scene.
profileComponent.Override(defaultStateComponent, 1.0f);
}
}
}
ApplyDefaultProfile(globalDefaultProfile); // Apply global default profile first
ApplyDefaultProfile(qualityDefaultProfile); // Apply quality default profile second
if (customDefaultProfiles != null) // Finally, apply custom default profiles in order
foreach (var profile in customDefaultProfiles)
ApplyDefaultProfile(profile);
// Build the flat parametersDefaultState list for fast per-frame resets
var parametersDefaultStateList = new List<VolumeParameter>();
foreach (var component in componentsDefaultStateList)
{
parametersDefaultStateList.AddRange(component.parameters);
}
m_ComponentsDefaultState = componentsDefaultStateList.ToArray();
m_ParametersDefaultState = parametersDefaultStateList.ToArray();
// All properties in stacks must be reset because the default state has changed
foreach (var s in m_CreatedVolumeStacks)
{
s.requiresReset = true;
s.requiresResetForAllProperties = true;
}
}
/// <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>
/// <seealso cref="Unregister"/>
public void Register(Volume volume)
{
m_VolumeCollection.Register(volume, volume.gameObject.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>
/// <seealso cref="Register"/>
public void Unregister(Volume volume)
{
m_VolumeCollection.Unregister(volume, volume.gameObject.layer);
}
/// <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
{
return m_VolumeCollection.IsComponentActiveInMask<T>(layerMask);
}
internal void SetLayerDirty(int layer)
{
m_VolumeCollection.SetLayerIndexDirty(layer);
}
internal void UpdateVolumeLayer(Volume volume, int prevLayer, int newLayer)
{
m_VolumeCollection.ChangeLayer(volume, prevLayer, newLayer);
}
// Go through all listed components and lerp overridden values in the global state
void OverrideData(VolumeStack stack, List<VolumeComponent> components, float interpFactor)
{
var numComponents = components.Count;
for (int i = 0; i < numComponents; i++)
{
var component = components[i];
if (!component.active)
continue;
var state = stack.GetComponent(component.GetType());
if (state != null)
{
component.Override(state, interpFactor);
}
}
}
// Faster version of OverrideData to force replace values in the global state.
// NOTE: As an optimization, only the VolumeParameters with overrideState=true are reset. All other parameters
// are assumed to be in their correct default state so no reset is necessary.
internal void ReplaceData(VolumeStack stack)
{
using var profilerScope = k_ProfilerMarkerReplaceData.Auto();
var stackParams = stack.parameters;
bool resetAllParameters = stack.requiresResetForAllProperties;
int count = stackParams.Length;
Debug.Assert(count == m_ParametersDefaultState.Length);
for (int i = 0; i < count; i++)
{
var stackParam = stackParams[i];
if (stackParam.overrideState || resetAllParameters) // Only reset the parameters that have been overriden by a scene volume
{
stackParam.overrideState = false;
stackParam.SetValue(m_ParametersDefaultState[i]);
}
}
stack.requiresResetForAllProperties = false;
}
/// <summary>
/// Checks component default state. This is only used in the editor to handle entering and exiting play mode
/// because the instances created during playmode are automatically destroyed.
/// </summary>
[Conditional("UNITY_EDITOR")]
public void CheckDefaultVolumeState()
{
if (m_ComponentsDefaultState == null || (m_ComponentsDefaultState.Length > 0 && m_ComponentsDefaultState[0] == null))
{
EvaluateVolumeDefaultState();
}
}
/// <summary>
/// Checks the state of a given stack. This is only used in the editor to handle entering and exiting play mode
/// because the instances created during playmode are automatically destroyed.
/// </summary>
/// <param name="stack">The stack to check.</param>
[Conditional("UNITY_EDITOR")]
public void CheckStack(VolumeStack stack)
{
if (stack.components == null)
{
stack.Reload(baseComponentTypeArray);
return;
}
foreach (var kvp in stack.components)
{
if (kvp.Key == null || kvp.Value == null)
{
stack.Reload(baseComponentTypeArray);
return;
}
}
}
// Returns true if must execute Update() in full, and false if we can early exit.
bool CheckUpdateRequired(VolumeStack stack)
{
if (m_VolumeCollection.count == 0)
{
if (stack.requiresReset)
{
// Update the stack one more time in case there was a volume that just ceased to exist. This ensures
// the stack will return to default values correctly.
stack.requiresReset = false;
return true;
}
// There were no volumes last frame either, and stack has been returned to defaults, so no update is
// needed and we can early exit from Update().
return false;
}
stack.requiresReset = true; // Stack must be reset every frame whenever there are volumes present
return true;
}
/// <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)
{
using var profilerScope = k_ProfilerMarkerUpdate.Auto();
if (!isInitialized)
return;
Assert.IsNotNull(stack);
CheckDefaultVolumeState();
CheckStack(stack);
if (!CheckUpdateRequired(stack))
return;
// Start by resetting the global state to default values.
ReplaceData(stack);
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
int numVolumes = volumes.Count;
for (int i = 0; i < numVolumes; i++)
{
Volume volume = volumes[i];
if (volume == null)
continue;
#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;
int numColliders = colliders.Count;
for (int c = 0; c < numColliders; c++)
{
var collider = colliders[c];
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);
volumes.RemoveAll(v => v == null);
return volumes.ToArray();
}
List<Volume> GrabVolumes(LayerMask mask)
{
return m_VolumeCollection.GrabVolumes(mask);
}
static bool IsVolumeRenderedByCamera(Volume volume, Camera camera)
{
#if UNITY_2018_3_OR_NEWER && UNITY_EDITOR
// GameObject for default global volume may not belong to any scene, following check prevents it from being culled
if (!volume.gameObject.scene.IsValid())
return true;
// 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() { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f709aacbf8d0b0048889f52315d67d58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,1934 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace UnityEngine.Rendering
{
// We need this base class to be able to store a list of VolumeParameter in collections as we
// can't store VolumeParameter<T> with variable T types in the same collection. As a result some
// of the following is a bit hacky...
/// <summary>
/// The base class for all parameters types stored in a <see cref="VolumeComponent"/>.
/// </summary>
/// <seealso cref="VolumeParameter{T}"/>
public abstract class VolumeParameter : ICloneable
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
internal string debugId { get; set; }
#endif
/// <summary>
/// A beautified string for debugger output. This is set on a <c>DebuggerDisplay</c> on every
/// parameter types.
/// </summary>
public const string k_DebuggerDisplay = "{m_Value} ({m_OverrideState})";
/// <summary>
/// The current override state for this parameter. The Volume system considers overriden parameters
/// for blending, and ignores non-overriden ones.
/// </summary>
/// <seealso cref="overrideState"/>
[SerializeField]
protected bool m_OverrideState;
/// <summary>
/// The current override state for this parameter. The Volume system considers overriden parameters
/// for blending, and ignores non-overriden ones. It is also used in the VolumeStack to determine whether
/// a given parameter has been overridden and thus needs to be reset to its default state in the next update.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the override state
/// changes.
/// </remarks>
/// <seealso cref="m_OverrideState"/>
public virtual bool overrideState
{
get => m_OverrideState;
set => m_OverrideState = value;
}
internal abstract void Interp(VolumeParameter from, VolumeParameter to, float t);
/// <summary>
/// Casts and gets the typed value of this parameter.
/// </summary>
/// <typeparam name="T">The type of the value stored in this parameter</typeparam>
/// <returns>A value of type <typeparamref name="T"/>.</returns>
/// <remarks>
/// This method is unsafe and does not do any type checking.
/// </remarks>
public T GetValue<T>()
{
return ((VolumeParameter<T>)this).value;
}
/// <summary>
/// Sets the value of this parameter to the value in <paramref name="parameter"/>.
/// </summary>
/// <param name="parameter">The <see cref="VolumeParameter"/> to copy the value from.</param>
public abstract void SetValue(VolumeParameter parameter);
/// <summary>
/// Unity calls this method when the parent <see cref="VolumeComponent"/> loads.
/// </summary>
/// <remarks>
/// Use this if you need to access fields and properties that you can not access in
/// the constructor of a <c>ScriptableObject</c>. (<see cref="VolumeParameter"/> are
/// generally declared and initialized in a <see cref="VolumeComponent"/>, which is a
/// <c>ScriptableObject</c>). Unity calls this right after it constructs the parent
/// <see cref="VolumeComponent"/>, thus allowing access to previously
/// inaccessible fields and properties.
/// </remarks>
protected internal virtual void OnEnable()
{
}
/// <summary>
/// Unity calls this method when the parent <see cref="VolumeComponent"/> goes out of scope.
/// </summary>
protected internal virtual void OnDisable()
{
}
/// <summary>
/// Checks if a given type is an <see cref="ObjectParameter{T}"/>.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns><c>true</c> if <paramref name="type"/> is an <see cref="ObjectParameter{T}"/>,
/// <c>false</c> otherwise.</returns>
public static bool IsObjectParameter(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ObjectParameter<>))
return true;
return type.BaseType != null
&& IsObjectParameter(type.BaseType);
}
/// <summary>
/// Override this method to free all allocated resources
/// </summary>
public virtual void Release() { }
/// <summary>
/// Clones the current instance of the <see cref="VolumeParameter"/>
/// </summary>
/// <returns>A new created instance with the same values as the current instance of <see cref="VolumeParameter"/></returns>
public abstract object Clone();
}
/// <summary>
/// A generic implementation of <see cref="VolumeParameter"/>. Custom parameters should derive
/// from this class and implement their own behavior.
/// </summary>
/// <typeparam name="T">The type of value to hold in this parameter.</typeparam>
/// <remarks>
/// <typeparamref name="T"/> should a serializable type.
/// Due to limitations with the serialization system in Unity, you should not use this class
/// directly to declare parameters in a <see cref="VolumeComponent"/>. Instead, use one of the
/// pre-flatten types (like <see cref="FloatParameter"/>, or make your own by extending this
/// class.
/// </remarks>
/// <example>
/// <para>This sample code shows how to make a custom parameter holding a <c>float</c>:</para>
/// <code>
/// using UnityEngine.Rendering;
///
/// [Serializable]
/// public sealed class MyFloatParameter : VolumeParameter&lt;float&gt;
/// {
/// public MyFloatParameter(float value, bool overrideState = false)
/// : base(value, overrideState) { }
///
/// public sealed override void Interp(float from, float to, float t)
/// {
/// m_Value = from + (to - from) * t;
/// }
/// }
/// </code>
/// </example>
/// <seealso cref="VolumeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class VolumeParameter<T> : VolumeParameter, IEquatable<VolumeParameter<T>>
{
/// <summary>
/// The value stored and serialized by this parameter.
/// </summary>
[SerializeField]
protected T m_Value;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public virtual T value
{
get => m_Value;
set => m_Value = value;
}
/// <summary>
/// Creates a new <see cref="VolumeParameter{T}"/> instance.
/// </summary>
public VolumeParameter()
: this(default, false)
{
}
/// <summary>
/// Creates a new <see cref="VolumeParameter{T}"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
protected VolumeParameter(T value, bool overrideState = false)
{
m_Value = value;
this.overrideState = overrideState;
}
internal override void Interp(VolumeParameter from, VolumeParameter to, float t)
{
// Note: this is relatively unsafe (assumes that from and to are both holding type T)
Interp((from as VolumeParameter<T>).value, (to as VolumeParameter<T>).value, t);
}
/// <summary>
/// Interpolates two values using a factor <paramref name="t"/>.
/// </summary>
/// <remarks>
/// By default, this method does a "snap" interpolation, meaning it returns the value
/// <paramref name="to"/> if <paramref name="t"/> is higher than 0, and <paramref name="from"/>
/// otherwise.
/// </remarks>
/// <param name="from">The start value.</param>
/// <param name="to">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
public virtual void Interp(T from, T to, float t)
{
// Default interpolation is naive
m_Value = t > 0f ? to : from;
}
/// <summary>
/// Sets the value for this parameter and sets its override state to <c>true</c>.
/// </summary>
/// <param name="x">The value to assign to this parameter.</param>
public void Override(T x)
{
overrideState = true;
m_Value = x;
}
/// <summary>
/// Sets the value of this parameter to the value in <paramref name="parameter"/>.
/// </summary>
/// <param name="parameter">The <see cref="VolumeParameter"/> to copy the value from.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void SetValue(VolumeParameter parameter)
{
m_Value = ((VolumeParameter<T>)parameter).m_Value;
}
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + overrideState.GetHashCode();
if (!EqualityComparer<T>.Default.Equals(value, default)) // Catches null for references with boxing of value types
hash = hash * 23 + value.GetHashCode();
return hash;
}
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>A string that represents the current object.</returns>
public override string ToString() => $"{value} ({overrideState})";
/// <summary>
/// Compares the value in a parameter with another value of the same type.
/// </summary>
/// <param name="lhs">The first value in a <see cref="VolumeParameter"/>.</param>
/// <param name="rhs">The second value.</param>
/// <returns><c>true</c> if both values are equal, <c>false</c> otherwise.</returns>
public static bool operator ==(VolumeParameter<T> lhs, T rhs) => lhs != null && !ReferenceEquals(lhs.value, null) && lhs.value.Equals(rhs);
/// <summary>
/// Compares the value store in a parameter with another value of the same type.
/// </summary>
/// <param name="lhs">The first value in a <see cref="VolumeParameter"/>.</param>
/// <param name="rhs">The second value.</param>
/// <returns><c>false</c> if both values are equal, <c>true</c> otherwise</returns>
public static bool operator !=(VolumeParameter<T> lhs, T rhs) => !(lhs == rhs);
/// <summary>
/// Checks if this parameter is equal to another.
/// </summary>
/// <param name="other">The other parameter to check against.</param>
/// <returns><c>true</c> if both parameters are equal, <c>false</c> otherwise</returns>
public bool Equals(VolumeParameter<T> other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return EqualityComparer<T>.Default.Equals(m_Value, other.m_Value);
}
/// <summary>
/// Determines whether two object instances are equal.
/// </summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns><c>true</c> if the specified object is equal to the current object, <c>false</c> otherwise.</returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != GetType())
return false;
return Equals((VolumeParameter<T>)obj);
}
/// <inheritdoc/>
public override object Clone()
{
return new VolumeParameter<T>(GetValue<T>(), overrideState);
}
/// <summary>
/// Explicitly downcast a <see cref="VolumeParameter{T}"/> to a value of type
/// <typeparamref name="T"/>.
/// </summary>
/// <param name="prop">The parameter to downcast.</param>
/// <returns>A value of type <typeparamref name="T"/>.</returns>
public static explicit operator T(VolumeParameter<T> prop) => prop.m_Value;
}
/// <summary>
/// Generic Enum volume parameter.
/// </summary>
/// <typeparam name="T">The type of value to hold in this parameter.</typeparam>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public sealed class EnumParameter<T> : VolumeParameter<T>
{
/// <summary>
/// Creates a new <see cref="EnumParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public EnumParameter(T value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>bool</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class BoolParameter : VolumeParameter<bool>
{
/// <summary>
/// Creates a new <see cref="BoolParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter</param>
/// <param name="overrideState">The initial override state for the parameter</param>
public BoolParameter(bool value, bool overrideState = false)
: base(value, overrideState)
{
}
/// <summary>
/// Creates a new <see cref="BoolParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter</param>
/// <param name="displayType">The display type to use for the parameter</param>
/// <param name="overrideState">The initial override state for the parameter</param>
public BoolParameter(bool value, DisplayType displayType, bool overrideState = false)
: base(value, overrideState)
{
this.displayType = displayType;
}
/// <summary>
/// Boolean widget type.
/// </summary>
public enum DisplayType
{
/// <summary> Display boolean parameter as checkbox. </summary>
Checkbox,
/// <summary> Display boolean parameter as enum popup with Disabled/Enabled options. </summary>
EnumPopup
}
/// <summary>
/// Type of widget used to display the <see cref="BoolParameter"/> in the UI.
/// </summary>
[NonSerialized]
public DisplayType displayType = DisplayType.Checkbox;
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>LayerMask</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class LayerMaskParameter : VolumeParameter<LayerMask>
{
/// <summary>
/// Creates a new <see cref="LayerMaskParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public LayerMaskParameter(LayerMask value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>LayerMask</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class RenderingLayerMaskParameter : VolumeParameter<RenderingLayerMask>
{
/// <summary>
/// Creates a new <see cref="LayerMaskParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public RenderingLayerMaskParameter(RenderingLayerMask value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds an <c>int</c> value.
/// </summary>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class IntParameter : VolumeParameter<int>
{
/// <summary>
/// Creates a new <see cref="IntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public IntParameter(int value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Interpolates between two <c>int</c> values.
/// </summary>
/// <param name="from">The start value</param>
/// <param name="to">The end value</param>
/// <param name="t">The interpolation factor in range [0,1]</param>
public sealed override void Interp(int from, int to, float t)
{
// Int snapping interpolation. Don't use this for enums as they don't necessarily have
// contiguous values. Use the default interpolator instead (same as bool).
m_Value = (int)(from + (to - from) * t);
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>int</c> value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpIntParameter : VolumeParameter<int>
{
/// <summary>
/// Creates a new <see cref="NoInterpIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpIntParameter(int value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds an <c>int</c> value clamped to a
/// minimum value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class MinIntParameter : IntParameter
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int min;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override int value
{
get => m_Value;
set => m_Value = Mathf.Max(value, min);
}
/// <summary>
/// Creates a new <see cref="MinIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public MinIntParameter(int value, int min, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>int</c> value that
/// clamped to a minimum value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpMinIntParameter : VolumeParameter<int>
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int min;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override int value
{
get => m_Value;
set => m_Value = Mathf.Max(value, min);
}
/// <summary>
/// Creates a new <see cref="NoInterpMinIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpMinIntParameter(int value, int min, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds an <c>int</c> value clamped to a
/// maximum value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class MaxIntParameter : IntParameter
{
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override int value
{
get => m_Value;
set => m_Value = Mathf.Min(value, max);
}
/// <summary>
/// Creates a new <see cref="MaxIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public MaxIntParameter(int value, int max, bool overrideState = false)
: base(value, overrideState)
{
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>int</c> value that
/// clamped to a maximum value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpMaxIntParameter : VolumeParameter<int>
{
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override int value
{
get => m_Value;
set => m_Value = Mathf.Min(value, max);
}
/// <summary>
/// Creates a new <see cref="NoInterpMaxIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpMaxIntParameter(int value, int max, bool overrideState = false)
: base(value, overrideState)
{
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds an <c>int</c> value clamped between a
/// minimum and a maximum value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
/// <seealso cref="NoInterpClampedIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class ClampedIntParameter : IntParameter
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int min;
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override int value
{
get => m_Value;
set => m_Value = Mathf.Clamp(value, min, max);
}
/// <summary>
/// Creates a new <see cref="ClampedIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public ClampedIntParameter(int value, int min, int max, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>int</c> value
/// clamped between a minimum and a maximum value.
/// </summary>
/// <seealso cref="IntParameter"/>
/// <seealso cref="MinIntParameter"/>
/// <seealso cref="MaxIntParameter"/>
/// <seealso cref="ClampedIntParameter"/>
/// <seealso cref="NoInterpIntParameter"/>
/// <seealso cref="NoInterpMinIntParameter"/>
/// <seealso cref="NoInterpMaxIntParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpClampedIntParameter : VolumeParameter<int>
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int min;
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public int max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override int value
{
get => m_Value;
set => m_Value = Mathf.Clamp(value, min, max);
}
/// <summary>
/// Creates a new <see cref="NoInterpClampedIntParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpClampedIntParameter(int value, int min, int max, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>float</c> value.
/// </summary>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class FloatParameter : VolumeParameter<float>
{
/// <summary>
/// Creates a new <see cref="FloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter</param>
/// <param name="overrideState">The initial override state for the parameter</param>
public FloatParameter(float value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Interpolates between two <c>float</c> values.
/// </summary>
/// <param name="from">The start value</param>
/// <param name="to">The end value</param>
/// <param name="t">The interpolation factor in range [0,1]</param>
public sealed override void Interp(float from, float to, float t)
{
m_Value = from + (to - from) * t;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>float</c> value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpFloatParameter : VolumeParameter<float>
{
/// <summary>
/// Creates a new <see cref="NoInterpFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpFloatParameter(float value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>float</c> value clamped to a minimum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class MinFloatParameter : FloatParameter
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float min;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override float value
{
get => m_Value;
set => m_Value = Mathf.Max(value, min);
}
/// <summary>
/// Creates a new <see cref="MinFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public MinFloatParameter(float value, float min, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>float</c> value clamped to
/// a minimum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpMinFloatParameter : VolumeParameter<float>
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float min;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override float value
{
get => m_Value;
set => m_Value = Mathf.Max(value, min);
}
/// <summary>
/// Creates a new <see cref="NoInterpMinFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to storedin the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpMinFloatParameter(float value, float min, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>float</c> value clamped to a max value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class MaxFloatParameter : FloatParameter
{
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override float value
{
get => m_Value;
set => m_Value = Mathf.Min(value, max);
}
/// <summary>
/// Creates a new <see cref="MaxFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public MaxFloatParameter(float value, float max, bool overrideState = false)
: base(value, overrideState)
{
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>float</c> value clamped to
/// a maximum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpMaxFloatParameter : VolumeParameter<float>
{
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override float value
{
get => m_Value;
set => m_Value = Mathf.Min(value, max);
}
/// <summary>
/// Creates a new <see cref="NoInterpMaxFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpMaxFloatParameter(float value, float max, bool overrideState = false)
: base(value, overrideState)
{
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>float</c> value clamped between a minimum and a
/// maximum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class ClampedFloatParameter : FloatParameter
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float min;
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override float value
{
get => m_Value;
set => m_Value = Mathf.Clamp(value, min, max);
}
/// <summary>
/// Creates a new <see cref="ClampedFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public ClampedFloatParameter(float value, float min, float max, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>float</c> value clamped between
/// a minimum and a maximum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpClampedFloatParameter : VolumeParameter<float>
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float min;
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override float value
{
get => m_Value;
set => m_Value = Mathf.Clamp(value, min, max);
}
/// <summary>
/// Creates a new <see cref="NoInterpClampedFloatParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpClampedFloatParameter(float value, float min, float max, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Vector2</c> value holding a range of two
/// <c>float</c> values clamped between a minimum and a maximum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
/// <seealso cref="NoInterpFloatRangeParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class FloatRangeParameter : VolumeParameter<Vector2>
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float min;
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override Vector2 value
{
get => m_Value;
set
{
m_Value.x = Mathf.Max(value.x, min);
m_Value.y = Mathf.Min(value.y, max);
}
}
/// <summary>
/// Creates a new <see cref="FloatRangeParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public FloatRangeParameter(Vector2 value, float min, float max, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
this.max = max;
}
/// <summary>
/// Interpolates between two <c>Vector2</c> values.
/// </summary>
/// <param name="from">The start value</param>
/// <param name="to">The end value</param>
/// <param name="t">The interpolation factor in range [0,1]</param>
public override void Interp(Vector2 from, Vector2 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Vector2</c> value holding
/// a range of two <c>float</c> values clamped between a minimum and a maximum value.
/// </summary>
/// <seealso cref="FloatParameter"/>
/// <seealso cref="MinFloatParameter"/>
/// <seealso cref="MaxFloatParameter"/>
/// <seealso cref="ClampedFloatParameter"/>
/// <seealso cref="FloatRangeParameter"/>
/// <seealso cref="NoInterpFloatParameter"/>
/// <seealso cref="NoInterpMinFloatParameter"/>
/// <seealso cref="NoInterpMaxFloatParameter"/>
/// <seealso cref="NoInterpClampedFloatParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpFloatRangeParameter : VolumeParameter<Vector2>
{
/// <summary>
/// The minimum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float min;
/// <summary>
/// The maximum value to clamp this parameter to.
/// </summary>
[NonSerialized]
public float max;
/// <summary>
/// The value that this parameter stores.
/// </summary>
/// <remarks>
/// You can override this property to define custom behaviors when the value is changed.
/// </remarks>
public override Vector2 value
{
get => m_Value;
set
{
m_Value.x = Mathf.Max(value.x, min);
m_Value.y = Mathf.Min(value.y, max);
}
}
/// <summary>
/// Creates a new <see cref="NoInterpFloatRangeParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="min">The minimum value to clamp the parameter to</param>
/// <param name="max">The maximum value to clamp the parameter to.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpFloatRangeParameter(Vector2 value, float min, float max, bool overrideState = false)
: base(value, overrideState)
{
this.min = min;
this.max = max;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Color</c> value.
/// </summary>
/// <seealso cref="NoInterpColorParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class ColorParameter : VolumeParameter<Color>
{
/// <summary>
/// Is this color HDR?
/// </summary>
[NonSerialized]
public bool hdr = false;
/// <summary>
/// Should the alpha channel be editable in the editor?
/// </summary>
[NonSerialized]
public bool showAlpha = true;
/// <summary>
/// Should the eye dropper be visible in the editor?
/// </summary>
[NonSerialized]
public bool showEyeDropper = true;
/// <summary>
/// Creates a new <see cref="ColorParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public ColorParameter(Color value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Creates a new <see cref="ColorParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="hdr">Specifies whether the color is HDR or not.</param>
/// <param name="showAlpha">Specifies whether you can edit the alpha channel in the Inspector or not.</param>
/// <param name="showEyeDropper">Specifies whether the eye dropper is visible in the editor or not.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public ColorParameter(Color value, bool hdr, bool showAlpha, bool showEyeDropper, bool overrideState = false)
: base(value, overrideState)
{
this.hdr = hdr;
this.showAlpha = showAlpha;
this.showEyeDropper = showEyeDropper;
this.overrideState = overrideState;
}
/// <summary>
/// Interpolates between two <c>Color</c> values.
/// </summary>
/// <remarks>
/// For performance reasons, this function interpolates the RGBA channels directly.
/// </remarks>
/// <param name="from">The start value.</param>
/// <param name="to">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
public override void Interp(Color from, Color to, float t)
{
// Lerping color values is a sensitive subject... We looked into lerping colors using
// HSV and LCH but they have some downsides that make them not work correctly in all
// situations, so we stick with RGB lerping for now, at least its behavior is
// predictable despite looking desaturated when `t ~= 0.5` and it's faster anyway.
m_Value.r = from.r + (to.r - from.r) * t;
m_Value.g = from.g + (to.g - from.g) * t;
m_Value.b = from.b + (to.b - from.b) * t;
m_Value.a = from.a + (to.a - from.a) * t;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Color</c> value.
/// </summary>
/// <seealso cref="ColorParameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpColorParameter : VolumeParameter<Color>
{
/// <summary>
/// Specifies whether the color is HDR or not.
/// </summary>
public bool hdr = false;
/// <summary>
/// Specifies whether you can edit the alpha channel in the Inspector or not.
/// </summary>
[NonSerialized]
public bool showAlpha = true;
/// <summary>
/// Specifies whether the eye dropper is visible in the editor or not.
/// </summary>
[NonSerialized]
public bool showEyeDropper = true;
/// <summary>
/// Creates a new <see cref="NoInterpColorParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpColorParameter(Color value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Creates a new <see cref="NoInterpColorParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="hdr">Specifies whether the color is HDR or not.</param>
/// <param name="showAlpha">Specifies whether you can edit the alpha channel in the Inspector or not.</param>
/// <param name="showEyeDropper">Specifies whether the eye dropper is visible in the editor or not.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpColorParameter(Color value, bool hdr, bool showAlpha, bool showEyeDropper, bool overrideState = false)
: base(value, overrideState)
{
this.hdr = hdr;
this.showAlpha = showAlpha;
this.showEyeDropper = showEyeDropper;
this.overrideState = overrideState;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Vector2</c> value.
/// </summary>
/// <seealso cref="NoInterpVector2Parameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class Vector2Parameter : VolumeParameter<Vector2>
{
/// <summary>
/// Creates a new <see cref="Vector2Parameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public Vector2Parameter(Vector2 value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Interpolates between two <c>Vector2</c> values.
/// </summary>
/// <param name="from">The start value.</param>
/// <param name="to">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
public override void Interp(Vector2 from, Vector2 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Vector2</c> value.
/// </summary>
/// <seealso cref="Vector2Parameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpVector2Parameter : VolumeParameter<Vector2>
{
/// <summary>
/// Creates a new <see cref="NoInterpVector2Parameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpVector2Parameter(Vector2 value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Vector3</c> value.
/// </summary>
/// <seealso cref="NoInterpVector3Parameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class Vector3Parameter : VolumeParameter<Vector3>
{
/// <summary>
/// Creates a new <see cref="Vector3Parameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public Vector3Parameter(Vector3 value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Interpolates between two <c>Vector3</c> values.
/// </summary>
/// <param name="from">The start value.</param>
/// <param name="to">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
public override void Interp(Vector3 from, Vector3 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
m_Value.z = from.z + (to.z - from.z) * t;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Vector3</c> value.
/// </summary>
/// <seealso cref="Vector3Parameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpVector3Parameter : VolumeParameter<Vector3>
{
/// <summary>
/// Creates a new <see cref="Vector3Parameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpVector3Parameter(Vector3 value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Vector4</c> value.
/// </summary>
/// <seealso cref="NoInterpVector4Parameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class Vector4Parameter : VolumeParameter<Vector4>
{
/// <summary>
/// Creates a new <see cref="Vector4Parameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public Vector4Parameter(Vector4 value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Interpolates between two <c>Vector4</c> values.
/// </summary>
/// <param name="from">The start value.</param>
/// <param name="to">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
public override void Interp(Vector4 from, Vector4 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
m_Value.z = from.z + (to.z - from.z) * t;
m_Value.w = from.w + (to.w - from.w) * t;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Vector4</c> value.
/// </summary>
/// <seealso cref="Vector4Parameter"/>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpVector4Parameter : VolumeParameter<Vector4>
{
/// <summary>
/// Creates a new <see cref="Vector4Parameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpVector4Parameter(Vector4 value, bool overrideState = false)
: base(value, overrideState) { }
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Texture</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class TextureParameter : VolumeParameter<Texture>
{
/// <summary>
/// The accepted dimension of textures.
/// </summary>
public TextureDimension dimension;
/// <summary>
/// Creates a new <see cref="TextureParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public TextureParameter(Texture value, bool overrideState = false)
: this(value, TextureDimension.Any, overrideState) { }
/// <summary>
/// Creates a new <see cref="TextureParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="dimension">The accepted dimension of textures.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public TextureParameter(Texture value, TextureDimension dimension, bool overrideState = false)
: base(value, overrideState)
{
this.dimension = dimension;
}
// TODO: Texture interpolation
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Texture</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpTextureParameter : VolumeParameter<Texture>
{
/// <summary>
/// Creates a new <see cref="NoInterpTextureParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpTextureParameter(Texture value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a 2D <c>Texture</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class Texture2DParameter : VolumeParameter<Texture>
{
/// <summary>
/// Creates a new <see cref="Texture2DParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public Texture2DParameter(Texture value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a 3D <c>Texture</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class Texture3DParameter : VolumeParameter<Texture>
{
/// <summary>
/// Creates a new <see cref="Texture3DParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public Texture3DParameter(Texture value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>RenderTexture</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class RenderTextureParameter : VolumeParameter<RenderTexture>
{
/// <summary>
/// Creates a new <see cref="RenderTextureParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public RenderTextureParameter(RenderTexture value, bool overrideState = false)
: base(value, overrideState) { }
// TODO: RenderTexture interpolation
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>RenderTexture</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpRenderTextureParameter : VolumeParameter<RenderTexture>
{
/// <summary>
/// Creates a new <see cref="NoInterpRenderTextureParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpRenderTextureParameter(RenderTexture value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <c>Cubemap</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class CubemapParameter : VolumeParameter<Texture>
{
/// <summary>
/// Creates a new <see cref="CubemapParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public CubemapParameter(Texture value, bool overrideState = false)
: base(value, overrideState) { }
// TODO: Cubemap interpolation
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a non-interpolating <c>Cubemap</c> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class NoInterpCubemapParameter : VolumeParameter<Cubemap>
{
/// <summary>
/// Creates a new <see cref="NoInterpCubemapParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public NoInterpCubemapParameter(Cubemap value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
int hash = base.GetHashCode();
unchecked
{
if (value != null)
hash = 23 * CoreUtils.GetTextureHash(value);
}
return hash;
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a serializable class or struct.
/// </summary>
/// <typeparam name="T">The type of serializable object or struct to hold in this parameter.
/// </typeparam>
// TODO: ObjectParameter<T> doesn't seem to be working as expect, debug me
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class ObjectParameter<T> : VolumeParameter<T>
{
internal ReadOnlyCollection<VolumeParameter> parameters { get; private set; }
/// <summary>
/// The current override state for this parameter. Note that this is always forced enabled
/// on <see cref="ObjectParameter{T}"/>.
/// </summary>
public sealed override bool overrideState
{
get => true;
set => m_OverrideState = true;
}
/// <summary>
/// The value stored by this parameter.
/// </summary>
public sealed override T value
{
get => m_Value;
set
{
m_Value = value;
if (m_Value == null)
{
parameters = null;
return;
}
// Automatically grab all fields of type VolumeParameter contained in this instance
parameters = m_Value.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter)))
.OrderBy(t => t.MetadataToken) // Guaranteed order
.Select(t => (VolumeParameter)t.GetValue(m_Value))
.ToList()
.AsReadOnly();
}
}
/// <summary>
/// Creates a new <see cref="ObjectParameter{T}"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
public ObjectParameter(T value)
{
m_OverrideState = true;
this.value = value;
}
internal override void Interp(VolumeParameter from, VolumeParameter to, float t)
{
if (m_Value == null)
return;
var paramOrigin = parameters;
var paramFrom = ((ObjectParameter<T>)from).parameters;
var paramTo = ((ObjectParameter<T>)to).parameters;
for (int i = 0; i < paramFrom.Count; i++)
{
// Keep track of the override state for debugging purpose
paramOrigin[i].overrideState = paramTo[i].overrideState;
if (paramTo[i].overrideState)
paramOrigin[i].Interp(paramFrom[i], paramTo[i], t);
}
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds an <c>AnimationCurve</c> value.
/// </summary>
[Serializable]
public class AnimationCurveParameter : VolumeParameter<AnimationCurve>
{
/// <summary>
/// Creates a new <see cref="AnimationCurveParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to be stored in the parameter</param>
/// <param name="overrideState">The initial override state for the parameter</param>
public AnimationCurveParameter(AnimationCurve value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Interpolates between two AnimationCurve values. Note that it will overwrite the values in lhsCurve,
/// whereas rhsCurve data will be unchanged. Thus, it is legal to call it as:
/// stateParam.Interp(stateParam, toParam, interpFactor);
/// However, It should NOT be called when the lhsCurve parameter needs to be preserved. But the current
/// framework modifies it anyway in VolumeComponent.Override for all types of VolumeParameters
/// </summary>
/// <param name="lhsCurve">The start value.</param>
/// <param name="rhsCurve">The end value.</param>
/// <param name="t">The interpolation factor in range [0,1].</param>
public override void Interp(AnimationCurve lhsCurve, AnimationCurve rhsCurve, float t)
{
m_Value = lhsCurve;
KeyframeUtility.InterpAnimationCurve(ref m_Value, rhsCurve, t);
}
/// <inheritdoc/>
public override void SetValue(VolumeParameter parameter)
{
m_Value.CopyFrom(((AnimationCurveParameter)parameter).m_Value);
}
/// <inheritdoc/>
public override object Clone()
{
return new AnimationCurveParameter(new AnimationCurve(GetValue<AnimationCurve>().keys), overrideState);
}
/// <summary>
/// Returns a hash code for the animationCurve.
/// </summary>
/// <returns>A hash code for the animationCurve.</returns>
public override int GetHashCode()
{
unchecked
{
var hash = overrideState.GetHashCode();
return hash * 23 + value.GetHashCode();
}
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <see cref="Material"/> value.
/// </summary>
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class MaterialParameter : VolumeParameter<Material>
{
/// <summary>
/// Creates a new <see cref="MaterialParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter</param>
/// <param name="overrideState">The initial override state for the parameter</param>
public MaterialParameter(Material value, bool overrideState = false)
: base(value, overrideState) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d53242bc2649d524aa42f2a84eb077b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,359 @@
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
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d7fd9488000d3734a9e00ee676215985
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,103 @@
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 readonly Dictionary<Type, VolumeComponent> components = new();
// Flat list of every volume parameter for faster per-frame stack reset.
internal VolumeParameter[] parameters;
// Flag indicating that some properties have received overrides, therefore they must be reset in the next update.
internal bool requiresReset = true;
// Flag indicating that default state has changed, therefore all properties in the stack must be reset in the next update.
internal bool requiresResetForAllProperties = true;
internal VolumeStack()
{
}
internal void Clear()
{
foreach (var component in components)
CoreUtils.Destroy(component.Value);
components.Clear();
parameters = null;
}
internal void Reload(Type[] componentTypes)
{
Clear();
requiresReset = true;
requiresResetForAllProperties = true;
List<VolumeParameter> parametersList = new();
foreach (var type in componentTypes)
{
var component = (VolumeComponent)ScriptableObject.CreateInstance(type);
components.Add(type, component);
parametersList.AddRange(component.parameters);
}
parameters = parametersList.ToArray();
isValid = true;
}
/// <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 shouldn't
/// be used anymore.
/// </summary>
public void Dispose()
{
Clear();
isValid = false;
}
/// <summary>
/// Check if the stack is in valid state and can be used.
/// </summary>
public bool isValid { get; private set; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7af226ce5122db0409963decbabf9d9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: