using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Unity.Mathematics; using UnityEditor; namespace UnityEngine.Rendering { /// /// The volume settings /// /// A with public abstract partial class VolumeDebugSettings : IVolumeDebugSettings where T : MonoBehaviour, IAdditionalData { /// Current volume component to debug. public int selectedComponent { get; set; } = 0; /// Current camera to debug. public Camera selectedCamera => selectedCameraIndex < 0 ? null : cameras.ElementAt(selectedCameraIndex); /// /// The selected camera index, use the property for better handling /// protected int m_SelectedCameraIndex = -1; /// Selected camera index. public int selectedCameraIndex { get { var count = cameras.Count(); return count > 0 ? Math.Clamp(m_SelectedCameraIndex, 0, count-1) : -1; } set { var count = cameras.Count(); m_SelectedCameraIndex = Math.Clamp(value, 0, count-1); } } private Camera[] m_CamerasArray; private List m_Cameras = new List(); /// Returns the collection of registered cameras. public IEnumerable cameras { get { m_Cameras.Clear(); #if UNITY_EDITOR if (SceneView.lastActiveSceneView != null) { var sceneCamera = SceneView.lastActiveSceneView.camera; if (sceneCamera != null) m_Cameras.Add(sceneCamera); } #endif if (m_CamerasArray == null || m_CamerasArray.Length != Camera.allCamerasCount) { m_CamerasArray = new Camera[Camera.allCamerasCount]; } Camera.GetAllCameras(m_CamerasArray); foreach (var camera in m_CamerasArray) { if (camera == null) continue; if (camera.cameraType != CameraType.Preview && camera.cameraType != CameraType.Reflection) { if (camera.TryGetComponent(out T additionalData)) m_Cameras.Add(camera); } } return m_Cameras; } } /// Selected camera volume stack. public abstract VolumeStack selectedCameraVolumeStack { get; } /// Selected camera volume layer mask. public abstract LayerMask selectedCameraLayerMask { get; } /// Selected camera volume position. public abstract Vector3 selectedCameraPosition { get; } /// Type of the current component to debug. public Type selectedComponentType { get => selectedComponent > 0 ? volumeComponentsPathAndType[selectedComponent - 1].Item2 : null; set { var index = volumeComponentsPathAndType.FindIndex(t => t.Item2 == value); if (index != -1) selectedComponent = index + 1; } } /// List of Volume component types. public List<(string, Type)> volumeComponentsPathAndType => VolumeManager.instance.GetVolumeComponentsForDisplay(GraphicsSettings.currentRenderPipelineAssetType); /// /// Specifies the render pipeline for this volume settings /// [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)] public virtual Type targetRenderPipeline { get; } internal VolumeParameter GetParameter(VolumeComponent component, FieldInfo field) { return (VolumeParameter)field.GetValue(component); } internal VolumeParameter GetParameter(FieldInfo field) { VolumeStack stack = selectedCameraVolumeStack; return stack == null ? null : GetParameter(stack.GetComponent(selectedComponentType), field); } internal VolumeParameter GetParameter(Volume volume, FieldInfo field) { var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile; if (!profile.TryGet(selectedComponentType, out VolumeComponent component)) return null; var param = GetParameter(component, field); if (!param.overrideState) return null; return param; } float[] weights = null; float ComputeWeight(Volume volume, Vector3 triggerPos) { if (volume == null) return 0; var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile; if (!volume.gameObject.activeInHierarchy) return 0; if (!volume.enabled || profile == null || volume.weight <= 0f) return 0; if (!profile.TryGet(selectedComponentType, out VolumeComponent component)) return 0; if (!component.active) return 0; float weight = Mathf.Clamp01(volume.weight); if (!volume.isGlobal) { var colliders = volume.GetComponents(); // Find closest distance to volume, 0 means it's inside it float closestDistanceSqr = float.PositiveInfinity; foreach (var collider in colliders) { if (!collider.enabled) continue; var closestPoint = collider.ClosestPoint(triggerPos); var d = (closestPoint - triggerPos).sqrMagnitude; if (d < closestDistanceSqr) closestDistanceSqr = d; } float blendDistSqr = volume.blendDistance * volume.blendDistance; if (closestDistanceSqr > blendDistSqr) weight = 0f; else if (blendDistSqr > 0f) weight *= 1f - (closestDistanceSqr / blendDistSqr); } return weight; } Volume[] volumes = null; /// Get an array of volumes on the /// An array of volumes sorted by influence. public Volume[] GetVolumes() { return VolumeManager.instance.GetVolumes(selectedCameraLayerMask) .Where(v => v.sharedProfile != null) .Reverse().ToArray(); } VolumeParameter[,] savedStates = null; VolumeParameter[,] GetStates() { var fields = selectedComponentType .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) .Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter))) .ToArray(); VolumeParameter[,] states = new VolumeParameter[volumes.Length, fields.Length]; for (int i = 0; i < volumes.Length; i++) { var profile = volumes[i].HasInstantiatedProfile() ? volumes[i].profile : volumes[i].sharedProfile; if (!profile.TryGet(selectedComponentType, out VolumeComponent component)) continue; for (int j = 0; j < fields.Length; j++) { var param = GetParameter(component, fields[j]); ; states[i, j] = param.overrideState ? param : null; } } return states; } bool ChangedStates(VolumeParameter[,] newStates) { if (savedStates.GetLength(1) != newStates.GetLength(1)) return true; for (int i = 0; i < savedStates.GetLength(0); i++) { for (int j = 0; j < savedStates.GetLength(1); j++) { if ((savedStates[i, j] == null) != (newStates[i, j] == null)) return true; } } return false; } /// /// Refreshes the volumes, fetches the stored volumes on the panel /// /// The list of to refresh /// If the volumes have been refreshed public bool RefreshVolumes(Volume[] newVolumes) { bool ret = false; if (volumes == null || !newVolumes.SequenceEqual(volumes)) { volumes = (Volume[])newVolumes.Clone(); savedStates = GetStates(); ret = true; } else { var newStates = GetStates(); if (savedStates == null || ChangedStates(newStates)) { savedStates = newStates; ret = true; } } var triggerPos = selectedCameraPosition; weights = new float[volumes.Length]; for (int i = 0; i < volumes.Length; i++) weights[i] = ComputeWeight(volumes[i], triggerPos); return ret; } /// /// Obtains the volume weight /// /// /// The weight of the volume public float GetVolumeWeight(Volume volume) { if (weights == null) return 0; float total = 0f, weight = 0f; for (int i = 0; i < volumes.Length; i++) { weight = weights[i]; weight *= 1f - total; total += weight; if (volumes[i] == volume) return weight; } return 0f; } /// /// Return if the has influence /// /// to check the influence /// If the volume has influence public bool VolumeHasInfluence(Volume volume) { if (weights == null) return false; int index = Array.IndexOf(volumes, volume); if (index == -1) return false; return weights[index] != 0f; } } }