629 lines
27 KiB
C#
629 lines
27 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
using System.Linq;
|
|
using System;
|
|
using System.Collections;
|
|
using UnityEngine.Serialization;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using System.Reflection;
|
|
#endif
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
/// <summary>
|
|
/// Unity Monobehavior that manages the execution of custom passes.
|
|
/// It provides
|
|
/// </summary>
|
|
[ExecuteAlways]
|
|
[HDRPHelpURLAttribute("Custom-Pass")]
|
|
public class CustomPassVolume : MonoBehaviour, IVolume
|
|
{
|
|
[SerializeField, FormerlySerializedAs("isGlobal")]
|
|
private bool m_IsGlobal = true;
|
|
|
|
/// <summary>
|
|
/// Whether or not the volume is global. If true, the component will ignore all colliders attached to it
|
|
/// </summary>
|
|
public bool isGlobal
|
|
{
|
|
get => m_IsGlobal;
|
|
set => m_IsGlobal = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Distance where the volume start to be rendered, the fadeValue field in C# will be updated to the normalized blend factor for your custom C# passes
|
|
/// In the fullscreen shader pass and DrawRenderers shaders you can access the _FadeValue
|
|
/// </summary>
|
|
[Min(0)]
|
|
public float fadeRadius;
|
|
|
|
/// <summary>
|
|
/// The volume priority, used to determine the execution order when there is multiple volumes with the same injection point. Custom Pass Volume with a higher value is rendered first.
|
|
/// </summary>
|
|
public float priority;
|
|
|
|
/// <summary>
|
|
/// List of custom passes to execute
|
|
/// </summary>
|
|
[SerializeReference]
|
|
public List<CustomPass> customPasses = new List<CustomPass>();
|
|
|
|
/// <summary>
|
|
/// Where the custom passes are going to be injected in HDRP
|
|
/// </summary>
|
|
public CustomPassInjectionPoint injectionPoint = CustomPassInjectionPoint.BeforeTransparent;
|
|
|
|
[SerializeField]
|
|
internal Camera m_TargetCamera;
|
|
|
|
/// <summary>
|
|
/// Use this field to force the custom pass volume to be executed only for one camera.
|
|
/// </summary>
|
|
public Camera targetCamera
|
|
{
|
|
/// <summary>
|
|
/// Get the target camera of the custom pass. The target camera can be null if the custom pass is in local or global mode.
|
|
/// </summary>
|
|
get => useTargetCamera ? m_TargetCamera : null;
|
|
/// <summary>
|
|
/// Sets the target camera of the custom pass volume, this will bypass the volume mode (local or global) and the volume mask of the camera.
|
|
/// </summary>
|
|
/// <value>The new camera value. A null value will disable target camera mode and fall back to local or global.</value>
|
|
set
|
|
{
|
|
m_TargetCamera = value;
|
|
useTargetCamera = value != null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fade value between 0 and 1. it represent how close you camera is from the collider of the custom pass.
|
|
/// 0 when the camera is outside the volume + fade radius and 1 when it is inside the collider.
|
|
/// </summary>
|
|
/// <value>The fade value that should be applied to the custom pass effect</value>
|
|
public float fadeValue { get; private set; }
|
|
|
|
#if UNITY_EDITOR
|
|
[System.NonSerialized]
|
|
bool visible = true;
|
|
#endif
|
|
|
|
[SerializeField]
|
|
internal bool useTargetCamera;
|
|
|
|
// The current active custom pass volume is simply the smallest overlapping volume with the trigger transform
|
|
static HashSet<CustomPassVolume> m_ActivePassVolumes = new HashSet<CustomPassVolume>();
|
|
static List<CustomPassVolume> m_OverlappingPassVolumes = new List<CustomPassVolume>();
|
|
static readonly Dictionary<CustomPassInjectionPoint, List<GlobalCustomPass>> m_GlobalCustomPasses = new();
|
|
static readonly List<GlobalCustomPass> s_EmptyGlobalCustomPassList = new();
|
|
|
|
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;
|
|
|
|
List<Collider> m_OverlappingColliders = new List<Collider>();
|
|
|
|
static List<CustomPassInjectionPoint> m_InjectionPoints;
|
|
static List<CustomPassInjectionPoint> injectionPoints
|
|
{
|
|
get
|
|
{
|
|
if (m_InjectionPoints == null)
|
|
m_InjectionPoints = Enum.GetValues(typeof(CustomPassInjectionPoint)).Cast<CustomPassInjectionPoint>().ToList();
|
|
return m_InjectionPoints;
|
|
}
|
|
}
|
|
|
|
// List used to temporarily store the passes to execute at a given injection point, static to avoid GC.Alloc
|
|
static readonly List<CustomPassVolume> m_CurrentInjectionPointList = new();
|
|
|
|
/// <summary>The current injection point used to render passes. Used to determine the injection point of global custom passes.</summary>
|
|
internal static CustomPassInjectionPoint currentGlobalInjectionPoint;
|
|
|
|
/// <summary>
|
|
/// Data structure used to store the global custom pass data needed for evaluation during the frame.
|
|
/// </summary>
|
|
public readonly struct GlobalCustomPass
|
|
{
|
|
/// <summary>The priority this custom pass has been added. Define in which order the global custom passes will be executed.</summary>
|
|
public readonly float priority;
|
|
/// <summary>The custom pass instance to be executed.</summary>
|
|
public readonly CustomPass instance;
|
|
|
|
/// <summary>
|
|
/// Utility function to deconstruct a global custom pass into a tuple.
|
|
/// You can use it directly in a foreach to avoid declaring an intermediate field to store the GlobalCustomPass instance.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// foreach (var (customPass, priority) in GetGlobalCustomPasses(injectionPoint))
|
|
/// </code>
|
|
/// </example>
|
|
/// <param name="instance">The instance of the custom pass stored in this GlobalCustomPass.</param>
|
|
/// <param name="priority">The priority stored in this GlobalCustomPass.</param>
|
|
public void Deconstruct(out CustomPass instance, out float priority)
|
|
{
|
|
instance = this.instance;
|
|
priority = this.priority;
|
|
}
|
|
|
|
internal GlobalCustomPass(float priority, CustomPass pass)
|
|
{
|
|
this.priority = priority;
|
|
this.instance = pass;
|
|
}
|
|
}
|
|
|
|
// List insertion sort by dichotomie: https://www.jacksondunstan.com/articles/3189
|
|
static void InsertSorted(List<GlobalCustomPass> list, GlobalCustomPass value)
|
|
{
|
|
var startIndex = 0;
|
|
var endIndex = list.Count;
|
|
while (endIndex > startIndex)
|
|
{
|
|
var windowSize = endIndex - startIndex;
|
|
var middleIndex = startIndex + (windowSize / 2);
|
|
var compareToResult = list[middleIndex].priority.CompareTo(value.priority);
|
|
if (compareToResult == 0)
|
|
{
|
|
list.Insert(middleIndex, value);
|
|
return;
|
|
}
|
|
else if (compareToResult < 0)
|
|
startIndex = middleIndex + 1;
|
|
else
|
|
endIndex = middleIndex;
|
|
}
|
|
list.Insert(startIndex, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register a custom pass instance at a given injection point. This custom pass will be executed by every camera
|
|
/// with custom pass enabled in their frame settings.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">The injection point where the custom pass will be executed.</param>
|
|
/// <param name="customPassInstance">The instance of the custom pass to execute. The same custom pass instance can be registered multiple times.</param>
|
|
/// <param name="priority">Define the execution order of the custom pass. Custom passes are executed from high to low priority.</param>
|
|
public static void RegisterGlobalCustomPass(CustomPassInjectionPoint injectionPoint, CustomPass customPassInstance, float priority = 0)
|
|
{
|
|
if (!m_GlobalCustomPasses.TryGetValue(injectionPoint, out var customPassList))
|
|
m_GlobalCustomPasses[injectionPoint] = customPassList = new();
|
|
InsertSorted(customPassList, new GlobalCustomPass(priority, customPassInstance));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Similar to the RegisterGlobalCustomPass with a protection ensuring that only one custom pass type is existing
|
|
/// at a certain injection point. The same custom pass type can still be registered to multiple injection point
|
|
/// as long as there is only one of this type per injection point.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">The injection point where the custom pass will be executed.</param>
|
|
/// <param name="customPassInstance">The instance of the custom pass to execute. The type of the custom pass will also be used to check if there is already a custom pass of this type registered.</param>
|
|
/// <param name="priority">Define the execution order of the custom pass. Custom passes are executed from high to low priority.</param>
|
|
/// <returns></returns>
|
|
public static bool RegisterUniqueGlobalCustomPass(CustomPassInjectionPoint injectionPoint, CustomPass customPassInstance, float priority = 0)
|
|
{
|
|
var customPassType = customPassInstance.GetType();
|
|
if (m_GlobalCustomPasses.TryGetValue(injectionPoint, out var customPassList))
|
|
{
|
|
foreach (var customPass in customPassList)
|
|
{
|
|
if (customPass.instance.GetType() == customPassType)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
RegisterGlobalCustomPass(injectionPoint, customPassInstance, priority);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a custom from the execution lists.
|
|
/// </summary>
|
|
/// <param name="customPassInstance">The custom pass instance to remove. If the custom pass instance exists at multiple injection points, they will all be removed.</param>
|
|
/// <returns>True if one or more custom passes were removed. False otherwise.</returns>
|
|
public static bool UnregisterGlobalCustomPass(CustomPass customPassInstance)
|
|
{
|
|
bool removed = false;
|
|
foreach (var injectionPoint in injectionPoints)
|
|
removed |= UnregisterGlobalCustomPass(injectionPoint, customPassInstance);
|
|
return removed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregister all global custom passes registered by any system.
|
|
/// This function unregister both active and inactive custom passes.
|
|
/// </summary>
|
|
/// <returns>True if at least one custom pass was removed. False otherwise.</returns>
|
|
public static bool UnregisterAllGlobalCustomPasses()
|
|
{
|
|
bool removed = false;
|
|
foreach (var injectionPoint in injectionPoints)
|
|
removed |= UnregisterAllGlobalCustomPasses(injectionPoint);
|
|
return removed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregister all global custom passes at a specific injection point.
|
|
/// This function unregister both active and inactive custom passes.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">The injection point where you want all the custom passes to be removed.</param>
|
|
/// <returns>True if at least one custom pass was removed. False otherwise.</returns>
|
|
public static bool UnregisterAllGlobalCustomPasses(CustomPassInjectionPoint injectionPoint)
|
|
{
|
|
bool removed = false;
|
|
foreach (var (customPass, priority) in GetGlobalCustomPasses(injectionPoint))
|
|
removed |= UnregisterGlobalCustomPass(injectionPoint, customPass);
|
|
return removed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregister a custom from the injection point execution list. If the same custom pass instance is registered multiple times, they will all be removed.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">Selects from which injection point the custom pass instance will be removed</param>
|
|
/// <param name="customPassInstance">The custom pass instance to unregister.</param>
|
|
/// <returns>True if one or more custom passes were removed. False otherwise.</returns>
|
|
public static bool UnregisterGlobalCustomPass(CustomPassInjectionPoint injectionPoint, CustomPass customPassInstance)
|
|
{
|
|
if (m_GlobalCustomPasses.TryGetValue(injectionPoint, out var customPassList))
|
|
return customPassList.RemoveAll(data => data.instance == customPassInstance) > 0;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all the custom passes registered at a specific injection point. This includes both enabled and disabled custom passes.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">The injection point used to filer the resulting custom pass list.</param>
|
|
/// <returns>The list of all the global custom passes in the injection point provided in parameter. If no custom pass were registered for the injection point, an empty list is returned.</returns>
|
|
public static List<GlobalCustomPass> GetGlobalCustomPasses(CustomPassInjectionPoint injectionPoint)
|
|
{
|
|
if (m_GlobalCustomPasses.TryGetValue(injectionPoint, out var customPassList))
|
|
return customPassList;
|
|
return s_EmptyGlobalCustomPassList;
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
// Remove null passes in case of something happens during the deserialization of the passes
|
|
customPasses.RemoveAll(c => c is null);
|
|
GetComponents(m_Colliders);
|
|
Register(this);
|
|
|
|
#if UNITY_EDITOR
|
|
SceneVisibilityManager.visibilityChanged -= UpdateCustomPassVolumeVisibility;
|
|
SceneVisibilityManager.visibilityChanged += UpdateCustomPassVolumeVisibility;
|
|
#endif
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
UnRegister(this);
|
|
CleanupPasses();
|
|
#if UNITY_EDITOR
|
|
SceneVisibilityManager.visibilityChanged -= UpdateCustomPassVolumeVisibility;
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
void UpdateCustomPassVolumeVisibility()
|
|
{
|
|
visible = !SceneVisibilityManager.instance.IsHidden(gameObject);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
bool IsVisible(HDCamera hdCamera)
|
|
{
|
|
#if UNITY_EDITOR
|
|
// Scene visibility
|
|
if (hdCamera.camera.cameraType == CameraType.SceneView && !visible)
|
|
return false;
|
|
|
|
// Prefab context mode visibility
|
|
var stage = PrefabStageUtility.GetCurrentPrefabStage();
|
|
if (stage != null)
|
|
{
|
|
// Check if the current volume is inside the currently edited prefab
|
|
bool isVolumeInPrefab = gameObject.scene == stage.scene;
|
|
|
|
if (!isVolumeInPrefab && stage.mode == PrefabStage.Mode.InIsolation)
|
|
return false;
|
|
|
|
if (!isVolumeInPrefab)
|
|
{
|
|
// Prefab context is hidden and the current volume is outside of the prefab so we don't render the effects
|
|
if (CoreUtils.IsSceneViewPrefabStageContextHidden())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
if (useTargetCamera)
|
|
return targetCamera == hdCamera.camera;
|
|
|
|
// We never execute volume if the layer is not within the culling layers of the camera
|
|
// Special case for the scene view: we can't easily change it's volume later mask, so by default we show all custom passes
|
|
if (hdCamera.camera.cameraType != CameraType.SceneView && (hdCamera.volumeLayerMask & (1 << gameObject.layer)) == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Execute(RenderGraph renderGraph, HDCamera hdCamera, CullingResults cullingResult, CullingResults cameraCullingResult, in CustomPass.RenderTargets targets)
|
|
{
|
|
bool executed = false;
|
|
|
|
if (!IsVisible(hdCamera))
|
|
return false;
|
|
|
|
foreach (var pass in customPasses)
|
|
{
|
|
if (pass != null && pass.WillBeExecuted(hdCamera))
|
|
{
|
|
pass.ExecuteInternal(renderGraph, hdCamera, cullingResult, cameraCullingResult, targets, this);
|
|
executed = true;
|
|
}
|
|
}
|
|
|
|
return executed;
|
|
}
|
|
|
|
internal static bool ExecuteAllCustomPasses(RenderGraph renderGraph, HDCamera hdCamera, CullingResults cullingResults, CullingResults cameraCullingResults, CustomPassInjectionPoint injectionPoint, in CustomPass.RenderTargets customPassTargets)
|
|
{
|
|
bool executed = false;
|
|
|
|
currentGlobalInjectionPoint = injectionPoint;
|
|
foreach (var (customPass, priority) in GetGlobalCustomPasses(injectionPoint))
|
|
{
|
|
if (customPass == null || !customPass.WillBeExecuted(hdCamera))
|
|
continue;
|
|
executed = true;
|
|
customPass.ExecuteInternal(renderGraph, hdCamera, cullingResults, cameraCullingResults, customPassTargets, null);
|
|
}
|
|
|
|
GetActivePassVolumes(injectionPoint, m_CurrentInjectionPointList);
|
|
foreach (var customPass in m_CurrentInjectionPointList)
|
|
{
|
|
if (customPass == null)
|
|
return false;
|
|
executed |= customPass.Execute(renderGraph, hdCamera, cullingResults, cameraCullingResults, customPassTargets);
|
|
}
|
|
|
|
return executed;
|
|
}
|
|
|
|
internal bool WillExecuteInjectionPoint(HDCamera hdCamera)
|
|
{
|
|
bool executed = false;
|
|
|
|
if (!IsVisible(hdCamera))
|
|
return false;
|
|
|
|
foreach (var pass in customPasses)
|
|
{
|
|
if (pass != null && pass.WillBeExecuted(hdCamera))
|
|
executed = true;
|
|
}
|
|
|
|
return executed;
|
|
}
|
|
|
|
internal void CleanupPasses()
|
|
{
|
|
foreach (var pass in customPasses)
|
|
pass.CleanupPassInternal();
|
|
}
|
|
|
|
static void Register(CustomPassVolume volume) => m_ActivePassVolumes.Add(volume);
|
|
|
|
static void UnRegister(CustomPassVolume volume) => m_ActivePassVolumes.Remove(volume);
|
|
|
|
internal static void Update(HDCamera camera)
|
|
{
|
|
var triggerPos = camera.volumeAnchor.position;
|
|
|
|
m_OverlappingPassVolumes.Clear();
|
|
|
|
// Traverse all volumes
|
|
foreach (var volume in m_ActivePassVolumes)
|
|
{
|
|
if (!volume.IsVisible(camera))
|
|
continue;
|
|
|
|
if (volume.useTargetCamera)
|
|
{
|
|
if (volume.targetCamera == camera.camera)
|
|
m_OverlappingPassVolumes.Add(volume);
|
|
continue;
|
|
}
|
|
|
|
// Global volumes always have influence
|
|
if (volume.isGlobal)
|
|
{
|
|
volume.fadeValue = 1.0f;
|
|
m_OverlappingPassVolumes.Add(volume);
|
|
continue;
|
|
}
|
|
|
|
// If volume isn't global and has no collider, skip it as it's useless
|
|
if (volume.m_Colliders.Count == 0)
|
|
continue;
|
|
|
|
volume.m_OverlappingColliders.Clear();
|
|
|
|
float sqrFadeRadius = Mathf.Max(float.Epsilon, volume.fadeRadius * volume.fadeRadius);
|
|
float minSqrDistance = 1e20f;
|
|
|
|
foreach (var collider in volume.m_Colliders)
|
|
{
|
|
if (!collider || !collider.enabled)
|
|
continue;
|
|
|
|
// We don't support concave colliders
|
|
if (collider is MeshCollider m && !m.convex)
|
|
continue;
|
|
|
|
var closestPoint = collider.ClosestPoint(triggerPos);
|
|
var d = (closestPoint - triggerPos).sqrMagnitude;
|
|
|
|
minSqrDistance = Mathf.Min(minSqrDistance, d);
|
|
|
|
// Update the list of overlapping colliders
|
|
if (d <= sqrFadeRadius)
|
|
volume.m_OverlappingColliders.Add(collider);
|
|
}
|
|
|
|
// update the fade value:
|
|
volume.fadeValue = 1.0f - Mathf.Clamp01(Mathf.Sqrt(minSqrDistance / sqrFadeRadius));
|
|
|
|
if (volume.m_OverlappingColliders.Count > 0)
|
|
m_OverlappingPassVolumes.Add(volume);
|
|
}
|
|
|
|
// Sort the overlapping volumes by priority order (smaller first, then larger and finally globals)
|
|
m_OverlappingPassVolumes.Sort((v1, v2) =>
|
|
{
|
|
static float GetVolumeExtent(CustomPassVolume volume)
|
|
{
|
|
float extent = 0;
|
|
foreach (var collider in volume.m_OverlappingColliders)
|
|
extent += collider.bounds.extents.magnitude;
|
|
return extent;
|
|
}
|
|
|
|
// Sort by priority and then by volume extent
|
|
if (v1.priority == v2.priority)
|
|
{
|
|
if (v1.isGlobal && v2.isGlobal) return 0;
|
|
if (v1.isGlobal) return 1;
|
|
if (v2.isGlobal) return -1;
|
|
|
|
return GetVolumeExtent(v1).CompareTo(GetVolumeExtent(v2));
|
|
}
|
|
else
|
|
{
|
|
return v2.priority.CompareTo(v1.priority);
|
|
}
|
|
});
|
|
}
|
|
|
|
internal void AggregateCullingParameters(ref ScriptableCullingParameters cullingParameters, HDCamera hdCamera)
|
|
{
|
|
foreach (var pass in customPasses)
|
|
{
|
|
if (pass != null && pass.enabled)
|
|
pass.InternalAggregateCullingParameters(ref cullingParameters, hdCamera);
|
|
}
|
|
}
|
|
|
|
internal static CullingResults? Cull(ScriptableRenderContext renderContext, HDCamera hdCamera)
|
|
{
|
|
CullingResults? result = null;
|
|
|
|
// We need to sort the volumes first to know which one will be executed
|
|
// TODO: cache the results per camera in the HDRenderPipeline so it's not executed twice per camera
|
|
Update(hdCamera);
|
|
|
|
// For each injection points, we gather the culling results for
|
|
hdCamera.camera.TryGetCullingParameters(out var cullingParameters);
|
|
|
|
// By default we don't want the culling to return any objects
|
|
cullingParameters.cullingMask = 0;
|
|
cullingParameters.cullingOptions = CullingOptions.None;
|
|
|
|
foreach (var volume in m_OverlappingPassVolumes)
|
|
volume?.AggregateCullingParameters(ref cullingParameters, hdCamera);
|
|
|
|
// Try to share the culling results from the camera to the custom passes by default
|
|
bool shareCullingResults = true;
|
|
|
|
// If we don't have anything to cull or the pass is asking for the same culling layers than the camera, we don't have to re-do the culling
|
|
shareCullingResults &= (cullingParameters.cullingMask & hdCamera.camera.cullingMask) == cullingParameters.cullingMask;
|
|
shareCullingResults &= cullingParameters.cullingMatrix == hdCamera.camera.cullingMatrix;
|
|
shareCullingResults &= cullingParameters.isOrthographic == hdCamera.camera.orthographic;
|
|
|
|
if (!shareCullingResults)
|
|
result = renderContext.Cull(ref cullingParameters);
|
|
|
|
return result;
|
|
}
|
|
|
|
internal static void Cleanup()
|
|
{
|
|
// Cleanup is called when HDRP is destroyed, so we need to cleanup all the passes that were rendered
|
|
foreach (var pass in m_ActivePassVolumes)
|
|
pass.CleanupPasses();
|
|
|
|
foreach (var passList in m_GlobalCustomPasses.Values)
|
|
{
|
|
foreach (var pass in passList)
|
|
if (pass.instance != null)
|
|
pass.instance.CleanupPassInternal();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the currently active Custom Pass Volume for a given injection point.
|
|
/// Note this function returns only the first active volume, not the others that will be executed.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">The injection point to get the currently active Custom Pass Volume for.</param>
|
|
/// <returns>Returns the Custom Pass Volume instance associated with the injection point.</returns>
|
|
[Obsolete("In order to support multiple custom pass volume per injection points, please use GetActivePassVolumes.")]
|
|
public static CustomPassVolume GetActivePassVolume(CustomPassInjectionPoint injectionPoint)
|
|
{
|
|
var volumes = new List<CustomPassVolume>();
|
|
GetActivePassVolumes(injectionPoint, volumes);
|
|
return volumes.FirstOrDefault();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the currently active Custom Pass Volume for a given injection point.
|
|
/// </summary>
|
|
/// <param name="injectionPoint">The injection point to get the currently active Custom Pass Volume for.</param>
|
|
/// <param name="volumes">The list of custom pass volumes to popuplate with the active volumes.</param>
|
|
public static void GetActivePassVolumes(CustomPassInjectionPoint injectionPoint, List<CustomPassVolume> volumes)
|
|
{
|
|
volumes.Clear();
|
|
foreach (var volume in m_OverlappingPassVolumes)
|
|
if (volume.injectionPoint == injectionPoint)
|
|
volumes.Add(volume);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a pass of type passType in the active pass list
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the CustomPass to create</typeparam>
|
|
/// <returns>The new custom</returns>
|
|
public T AddPassOfType<T>() where T : CustomPass => AddPassOfType(typeof(T)) as T;
|
|
|
|
/// <summary>
|
|
/// Add a pass of type passType in the active pass list
|
|
/// </summary>
|
|
/// <param name="passType">The type of the CustomPass to create</param>
|
|
/// <returns>The new custom</returns>
|
|
public CustomPass AddPassOfType(Type passType)
|
|
{
|
|
if (!typeof(CustomPass).IsAssignableFrom(passType))
|
|
{
|
|
Debug.LogError($"Can't add pass type {passType} to the list because it does not inherit from CustomPass.");
|
|
return null;
|
|
}
|
|
|
|
var customPass = Activator.CreateInstance(passType) as CustomPass;
|
|
customPasses.Add(customPass);
|
|
return customPass;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
// In the editor, we refresh the list of colliders at every frame because it's frequent to add/remove them
|
|
void Update() => GetComponents(m_Colliders);
|
|
#endif
|
|
}
|
|
}
|