A game about forced loneliness, made by TACStudios
at master 166 lines 5.2 kB view raw
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}