A game about forced loneliness, made by TACStudios
at master 299 lines 11 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5using Unity.Mathematics; 6using UnityEditor; 7 8namespace UnityEngine.Rendering 9{ 10 /// <summary> 11 /// The volume settings 12 /// </summary> 13 /// <typeparam name="T">A <see cref="MonoBehaviour"/> with <see cref="IAdditionalData"/></typeparam> 14 public abstract partial class VolumeDebugSettings<T> : IVolumeDebugSettings 15 where T : MonoBehaviour, IAdditionalData 16 { 17 /// <summary>Current volume component to debug.</summary> 18 public int selectedComponent { get; set; } = 0; 19 20 /// <summary>Current camera to debug.</summary> 21 public Camera selectedCamera => selectedCameraIndex < 0 ? null : cameras.ElementAt(selectedCameraIndex); 22 23 /// <summary> 24 /// The selected camera index, use the property for better handling 25 /// </summary> 26 protected int m_SelectedCameraIndex = -1; 27 28 /// <summary>Selected camera index.</summary> 29 public int selectedCameraIndex 30 { 31 get 32 { 33 var count = cameras.Count(); 34 return count > 0 ? Math.Clamp(m_SelectedCameraIndex, 0, count-1) : -1; 35 } 36 set 37 { 38 var count = cameras.Count(); 39 m_SelectedCameraIndex = Math.Clamp(value, 0, count-1); 40 } 41 } 42 43 private Camera[] m_CamerasArray; 44 private List<Camera> m_Cameras = new List<Camera>(); 45 46 /// <summary>Returns the collection of registered cameras.</summary> 47 public IEnumerable<Camera> cameras 48 { 49 get 50 { 51 m_Cameras.Clear(); 52 53#if UNITY_EDITOR 54 if (SceneView.lastActiveSceneView != null) 55 { 56 var sceneCamera = SceneView.lastActiveSceneView.camera; 57 if (sceneCamera != null) 58 m_Cameras.Add(sceneCamera); 59 } 60#endif 61 62 if (m_CamerasArray == null || m_CamerasArray.Length != Camera.allCamerasCount) 63 { 64 m_CamerasArray = new Camera[Camera.allCamerasCount]; 65 } 66 67 Camera.GetAllCameras(m_CamerasArray); 68 69 foreach (var camera in m_CamerasArray) 70 { 71 if (camera == null) 72 continue; 73 74 if (camera.cameraType != CameraType.Preview && camera.cameraType != CameraType.Reflection) 75 { 76 if (camera.TryGetComponent<T>(out T additionalData)) 77 m_Cameras.Add(camera); 78 } 79 } 80 81 return m_Cameras; 82 } 83 } 84 85 /// <summary>Selected camera volume stack.</summary> 86 public abstract VolumeStack selectedCameraVolumeStack { get; } 87 88 /// <summary>Selected camera volume layer mask.</summary> 89 public abstract LayerMask selectedCameraLayerMask { get; } 90 91 /// <summary>Selected camera volume position.</summary> 92 public abstract Vector3 selectedCameraPosition { get; } 93 94 /// <summary>Type of the current component to debug.</summary> 95 public Type selectedComponentType 96 { 97 get => selectedComponent > 0 ? volumeComponentsPathAndType[selectedComponent - 1].Item2 : null; 98 set 99 { 100 var index = volumeComponentsPathAndType.FindIndex(t => t.Item2 == value); 101 if (index != -1) 102 selectedComponent = index + 1; 103 } 104 } 105 106 /// <summary>List of Volume component types.</summary> 107 public List<(string, Type)> volumeComponentsPathAndType => VolumeManager.instance.GetVolumeComponentsForDisplay(GraphicsSettings.currentRenderPipelineAssetType); 108 109 /// <summary> 110 /// Specifies the render pipeline for this volume settings 111 /// </summary> 112 [Obsolete("This property is obsolete and kept only for not breaking user code. VolumeDebugSettings will use current pipeline when it needs to gather volume component types and paths. #from(23.2)", false)] 113 public virtual Type targetRenderPipeline { get; } 114 115 internal VolumeParameter GetParameter(VolumeComponent component, FieldInfo field) 116 { 117 return (VolumeParameter)field.GetValue(component); 118 } 119 120 internal VolumeParameter GetParameter(FieldInfo field) 121 { 122 VolumeStack stack = selectedCameraVolumeStack; 123 return stack == null ? null : GetParameter(stack.GetComponent(selectedComponentType), field); 124 } 125 126 internal VolumeParameter GetParameter(Volume volume, FieldInfo field) 127 { 128 var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile; 129 if (!profile.TryGet(selectedComponentType, out VolumeComponent component)) 130 return null; 131 var param = GetParameter(component, field); 132 if (!param.overrideState) 133 return null; 134 return param; 135 } 136 137 float[] weights = null; 138 float ComputeWeight(Volume volume, Vector3 triggerPos) 139 { 140 if (volume == null) return 0; 141 142 var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile; 143 144 if (!volume.gameObject.activeInHierarchy) return 0; 145 if (!volume.enabled || profile == null || volume.weight <= 0f) return 0; 146 if (!profile.TryGet(selectedComponentType, out VolumeComponent component)) return 0; 147 if (!component.active) return 0; 148 149 float weight = Mathf.Clamp01(volume.weight); 150 if (!volume.isGlobal) 151 { 152 var colliders = volume.GetComponents<Collider>(); 153 154 // Find closest distance to volume, 0 means it's inside it 155 float closestDistanceSqr = float.PositiveInfinity; 156 foreach (var collider in colliders) 157 { 158 if (!collider.enabled) 159 continue; 160 161 var closestPoint = collider.ClosestPoint(triggerPos); 162 var d = (closestPoint - triggerPos).sqrMagnitude; 163 164 if (d < closestDistanceSqr) 165 closestDistanceSqr = d; 166 } 167 float blendDistSqr = volume.blendDistance * volume.blendDistance; 168 if (closestDistanceSqr > blendDistSqr) 169 weight = 0f; 170 else if (blendDistSqr > 0f) 171 weight *= 1f - (closestDistanceSqr / blendDistSqr); 172 } 173 return weight; 174 } 175 176 Volume[] volumes = null; 177 178 /// <summary>Get an array of volumes on the <see cref="selectedCameraLayerMask"/></summary> 179 /// <returns>An array of volumes sorted by influence.</returns> 180 public Volume[] GetVolumes() 181 { 182 return VolumeManager.instance.GetVolumes(selectedCameraLayerMask) 183 .Where(v => v.sharedProfile != null) 184 .Reverse().ToArray(); 185 } 186 187 VolumeParameter[,] savedStates = null; 188 VolumeParameter[,] GetStates() 189 { 190 var fields = selectedComponentType 191 .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) 192 .Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter))) 193 .ToArray(); 194 195 VolumeParameter[,] states = new VolumeParameter[volumes.Length, fields.Length]; 196 for (int i = 0; i < volumes.Length; i++) 197 { 198 var profile = volumes[i].HasInstantiatedProfile() ? volumes[i].profile : volumes[i].sharedProfile; 199 if (!profile.TryGet(selectedComponentType, out VolumeComponent component)) 200 continue; 201 202 for (int j = 0; j < fields.Length; j++) 203 { 204 var param = GetParameter(component, fields[j]); ; 205 states[i, j] = param.overrideState ? param : null; 206 } 207 } 208 return states; 209 } 210 211 bool ChangedStates(VolumeParameter[,] newStates) 212 { 213 if (savedStates.GetLength(1) != newStates.GetLength(1)) 214 return true; 215 for (int i = 0; i < savedStates.GetLength(0); i++) 216 { 217 for (int j = 0; j < savedStates.GetLength(1); j++) 218 { 219 if ((savedStates[i, j] == null) != (newStates[i, j] == null)) 220 return true; 221 } 222 } 223 return false; 224 } 225 226 /// <summary> 227 /// Refreshes the volumes, fetches the stored volumes on the panel 228 /// </summary> 229 /// <param name="newVolumes">The list of <see cref="Volume"/> to refresh</param> 230 /// <returns>If the volumes have been refreshed</returns> 231 public bool RefreshVolumes(Volume[] newVolumes) 232 { 233 bool ret = false; 234 if (volumes == null || !newVolumes.SequenceEqual(volumes)) 235 { 236 volumes = (Volume[])newVolumes.Clone(); 237 savedStates = GetStates(); 238 ret = true; 239 } 240 else 241 { 242 var newStates = GetStates(); 243 if (savedStates == null || ChangedStates(newStates)) 244 { 245 savedStates = newStates; 246 ret = true; 247 } 248 } 249 250 var triggerPos = selectedCameraPosition; 251 weights = new float[volumes.Length]; 252 for (int i = 0; i < volumes.Length; i++) 253 weights[i] = ComputeWeight(volumes[i], triggerPos); 254 255 return ret; 256 } 257 258 /// <summary> 259 /// Obtains the volume weight 260 /// </summary> 261 /// <param name="volume"><see cref="Volume"/></param> 262 /// <returns>The weight of the volume</returns> 263 public float GetVolumeWeight(Volume volume) 264 { 265 if (weights == null) 266 return 0; 267 268 float total = 0f, weight = 0f; 269 for (int i = 0; i < volumes.Length; i++) 270 { 271 weight = weights[i]; 272 weight *= 1f - total; 273 total += weight; 274 275 if (volumes[i] == volume) 276 return weight; 277 } 278 279 return 0f; 280 } 281 282 /// <summary> 283 /// Return if the <see cref="Volume"/> has influence 284 /// </summary> 285 /// <param name="volume"><see cref="Volume"/> to check the influence</param> 286 /// <returns>If the volume has influence</returns> 287 public bool VolumeHasInfluence(Volume volume) 288 { 289 if (weights == null) 290 return false; 291 292 int index = Array.IndexOf(volumes, volume); 293 if (index == -1) 294 return false; 295 296 return weights[index] != 0f; 297 } 298 } 299}