UnityGame/Library/PackageCache/com.unity.render-pipelines.core/Runtime/Volume/VolumeCollection.cs
2024-10-27 10:53:47 +03:00

167 lines
5.1 KiB
C#

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;
}
}
}