using System; using System.Collections.Generic; using System.Reflection; using System.Linq; using UnityEditor; namespace UnityEngine.Rendering.HighDefinition { /// /// Volume debug settings. /// public class VolumeDebugSettings { /// Current volume component to debug. internal int selectedComponent = 0; int m_SelectedCamera = 0; internal int selectedCameraIndex { get { #if UNITY_EDITOR if (m_SelectedCamera < 0 || m_SelectedCamera > cameras.Count + 1) return 0; #else if (m_SelectedCamera < 0 || m_SelectedCamera > cameras.Count) return 0; #endif return m_SelectedCamera; } set { m_SelectedCamera = value; } } /// Current camera to debug. public Camera selectedCamera { get { #if UNITY_EDITOR if (m_SelectedCamera <= 0 || m_SelectedCamera > cameras.Count + 1) return null; if (m_SelectedCamera == 1) return SceneView.lastActiveSceneView.camera; else return cameras[m_SelectedCamera - 2].GetComponent(); #else if (m_SelectedCamera <= 0 || m_SelectedCamera > cameras.Count) return null; return cameras[m_SelectedCamera - 1].GetComponent(); #endif } } /// Selected camera volume stack. public VolumeStack selectedCameraVolumeStack { get { Camera cam = selectedCamera; if (cam == null) return null; var stack = HDCamera.GetOrCreate(cam).volumeStack; if (stack != null) return stack; return VolumeManager.instance.stack; } } /// Selected camera volume layer mask. public LayerMask selectedCameraLayerMask { get { #if UNITY_EDITOR if (m_SelectedCamera <= 0 || m_SelectedCamera > cameras.Count + 1) return (LayerMask)0; if (m_SelectedCamera == 1) return -1; return cameras[m_SelectedCamera - 2].volumeLayerMask; #else if (m_SelectedCamera <= 0 || m_SelectedCamera > cameras.Count) return (LayerMask)0; return cameras[m_SelectedCamera - 1].volumeLayerMask; #endif } } /// Selected camera volume position. public Vector3 selectedCameraPosition { get { Camera cam = selectedCamera; if (cam == null) return Vector3.zero; var anchor = HDCamera.GetOrCreate(cam).volumeAnchor; if (anchor == null) // means the hdcamera has not been initialized { // So we have to update the stack manually if (cam.TryGetComponent(out var data)) anchor = data.volumeAnchorOverride; if (anchor == null) anchor = cam.transform; var stack = selectedCameraVolumeStack; if (stack != null) VolumeManager.instance.Update(stack, anchor, selectedCameraLayerMask); } return anchor.position; } } /// Type of the current component to debug. public Type selectedComponentType { get { return componentTypes[selectedComponent - 1]; } set { var index = componentTypes.FindIndex(t => t == value); if (index != -1) selectedComponent = index + 1; } } static List s_ComponentTypes; /// List of Volume component types. static public List componentTypes { get { if (s_ComponentTypes == null) { s_ComponentTypes = VolumeManager.instance.baseComponentTypeArray .Where(t => !t.IsDefined(typeof(VolumeComponentDeprecated), false)) .OrderBy(t => ComponentDisplayName(t)) .ToList(); } return s_ComponentTypes; } } /// Returns the name of a component from its VolumeComponentMenu. /// A volume component. /// The component display name. static public string ComponentDisplayName(Type component) { Attribute attrib = component.GetCustomAttribute(typeof(VolumeComponentMenu), false); if (attrib != null) return (attrib as VolumeComponentMenu).menu; return component.Name; } internal static List cameras {get; private set; } = new List(); internal static void RegisterCamera(HDAdditionalCameraData camera) { if (!cameras.Contains(camera)) cameras.Add(camera); } internal static void UnRegisterCamera(HDAdditionalCameraData camera) { if (cameras.Contains(camera)) cameras.Remove(camera); } 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) { 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; } internal 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; } internal 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; } internal bool VolumeHasInfluence(Volume volume) { if (weights == null) return false; int index = Array.IndexOf(volumes, volume); if (index == -1) return false; return weights[index] != 0f; } } }