A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityEngine.Assertions;
4
5namespace UnityEngine.Rendering
6{
7 internal class VolumeCollection
8 {
9 // Max amount of layers available in Unity
10 internal const int k_MaxLayerCount = 32;
11
12 // Cached lists of all volumes (sorted by priority) by layer mask
13 readonly Dictionary<int, List<Volume>> m_SortedVolumes = new();
14
15 // Holds all the registered volumes
16 readonly List<Volume> m_Volumes = new();
17
18 // Keep track of sorting states for layer masks
19 readonly Dictionary<int, bool> m_SortNeeded = new();
20
21 public int count => m_Volumes.Count;
22
23 public bool Register(Volume volume, int layer)
24 {
25 if (volume == null)
26 throw new ArgumentNullException(nameof(volume), "The volume to register is null");
27
28 if (m_Volumes.Contains(volume))
29 return false;
30
31 m_Volumes.Add(volume);
32
33 // Look for existing cached layer masks and add it there if needed
34 foreach (var kvp in m_SortedVolumes)
35 {
36 // We add the volume to sorted lists only if the layer match and if it doesn't contain the volume already.
37 if ((kvp.Key & (1 << layer)) != 0 && !kvp.Value.Contains(volume))
38 kvp.Value.Add(volume);
39 }
40
41 SetLayerIndexDirty(layer);
42 return true;
43 }
44
45 public bool Unregister(Volume volume, int layer)
46 {
47 if (volume == null)
48 throw new ArgumentNullException(nameof(volume), "The volume to unregister is null");
49
50 m_Volumes.Remove(volume);
51
52 foreach (var kvp in m_SortedVolumes)
53 {
54 // Skip layer masks this volume doesn't belong to
55 if ((kvp.Key & (1 << layer)) == 0)
56 continue;
57
58 kvp.Value.Remove(volume);
59 }
60
61 SetLayerIndexDirty(layer);
62
63 return true;
64 }
65
66 public bool ChangeLayer(Volume volume, int previousLayerIndex, int currentLayerIndex)
67 {
68 if (volume == null)
69 throw new ArgumentNullException(nameof(volume), "The volume to change layer is null");
70
71 Assert.IsTrue(previousLayerIndex >= 0 && previousLayerIndex <= k_MaxLayerCount, "Invalid layer bit");
72 Unregister(volume, previousLayerIndex);
73
74 return Register(volume, currentLayerIndex);
75 }
76
77 // Stable insertion sort. Faster than List<T>.Sort() for our needs.
78 internal static void SortByPriority(List<Volume> volumes)
79 {
80 for (int i = 1; i < volumes.Count; i++)
81 {
82 var temp = volumes[i];
83 int j = i - 1;
84
85 // Sort order is ascending
86 while (j >= 0 && volumes[j].priority > temp.priority)
87 {
88 volumes[j + 1] = volumes[j];
89 j--;
90 }
91
92 volumes[j + 1] = temp;
93 }
94 }
95
96 public List<Volume> GrabVolumes(LayerMask mask)
97 {
98 List<Volume> list;
99
100 if (!m_SortedVolumes.TryGetValue(mask, out list))
101 {
102 // New layer mask detected, create a new list and cache all the volumes that belong
103 // to this mask in it
104 list = new List<Volume>();
105
106 var numVolumes = m_Volumes.Count;
107 for (int i = 0; i < numVolumes; i++)
108 {
109 var volume = m_Volumes[i];
110 if ((mask & (1 << volume.gameObject.layer)) == 0)
111 continue;
112
113 list.Add(volume);
114 m_SortNeeded[mask] = true;
115 }
116
117 m_SortedVolumes.Add(mask, list);
118 }
119
120 // Check sorting state
121 if (m_SortNeeded.TryGetValue(mask, out var sortNeeded) && sortNeeded)
122 {
123 m_SortNeeded[mask] = false;
124 SortByPriority(list);
125 }
126
127 return list;
128 }
129
130 public void SetLayerIndexDirty(int layerIndex)
131 {
132 Assert.IsTrue(layerIndex >= 0 && layerIndex <= k_MaxLayerCount, "Invalid layer bit");
133
134 foreach (var kvp in m_SortedVolumes)
135 {
136 var mask = kvp.Key;
137
138 if ((mask & (1 << layerIndex)) != 0)
139 m_SortNeeded[mask] = true;
140 }
141 }
142
143 public bool IsComponentActiveInMask<T>(LayerMask layerMask)
144 where T : VolumeComponent
145 {
146 int mask = layerMask.value;
147
148 foreach (var kvp in m_SortedVolumes)
149 {
150 if (kvp.Key != mask)
151 continue;
152
153 foreach (var volume in kvp.Value)
154 {
155 if (!volume.enabled || volume.profileRef == null)
156 continue;
157
158 if (volume.profileRef.TryGet(out T component) && component.active)
159 return true;
160 }
161 }
162
163 return false;
164 }
165 }
166}