167 lines
5.1 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|