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;
}
}
}