暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VolumeDebugSettings.cs 11KB

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