using UnityEngine;
using UnityEngine.SceneManagement;
//-----------------------------------------------------------------------------
// Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProMovieCapture
{
///
/// Selects the camera to use for captures based on various factors
/// TODO: add support for persisting across scene changes
///
[AddComponentMenu("AVPro Movie Capture/Utils/Camera Selector", 300)]
public class CameraSelector : MonoBehaviour
{
public enum SelectByMode
{
HighestDepthCamera,
MainCameraTag,
EditorSceneView,
Tag,
Name,
Manual,
}
public enum ScanFrequencyMode
{
Manual,
SceneLoad,
Frame,
}
[SerializeField] SelectByMode _selectBy = SelectByMode.HighestDepthCamera;
[SerializeField] ScanFrequencyMode _scanFrequency = ScanFrequencyMode.SceneLoad;
[SerializeField] bool _scanHiddenCameras = false;
[SerializeField] string _tag = "MainCamera";
[SerializeField] string _name = "Main Camera";
[SerializeField] Camera _camera = null;
#if UNITY_EDITOR
private const string SceneCameraName = "SceneCamera";
#endif
private Camera[] _cameraCache = new Camera[0];
private int _cameraCount;
private int _cameraCacheFrame = -1;
private bool _selectionDirty = false;
#if UNITY_EDITOR
public int CameraCacheCount
{
get { return _cameraCount; }
}
public Camera[] CameraCache
{
get { return _cameraCache; }
}
#endif
public Camera Camera
{
get { return _camera; }
set { _camera = value; _selectBy = SelectByMode.Manual; }
}
public SelectByMode SelectBy
{
get { return _selectBy; }
set { _selectBy = value; _selectionDirty = true; }
}
public ScanFrequencyMode ScanFrequency
{
get { return _scanFrequency; }
set { _scanFrequency = value; ResetSceneLoading(); }
}
public bool ScanHiddenCameras
{
get { return _scanHiddenCameras; }
set { _scanHiddenCameras = value; _selectionDirty = true; }
}
public string SelectTag
{
get { return _tag; }
set { _tag = value; _selectionDirty = true; }
}
public string SelectName
{
get { return _name; }
set { _name = value; _selectionDirty = true; }
}
void Awake()
{
ResetSceneLoading();
}
void Start()
{
ScanForCameraChange();
}
void OnValidate()
{
ResetSceneLoading();
}
void Update()
{
if (_selectionDirty || _scanFrequency == ScanFrequencyMode.Frame)
{
ScanForCameraChange();
}
}
void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void ResetSceneLoading()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
if (_scanFrequency == ScanFrequencyMode.SceneLoad)
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (_scanFrequency == ScanFrequencyMode.SceneLoad)
{
ScanForCameraChange();
}
}
public bool ScanForCameraChange()
{
bool result = false;
Camera camera = FindCamera();
if (_camera != camera)
{
_camera = camera;
if (_camera != null)
{
//Debug.Log("Camera " + _camera.name);
}
result = true;
}
_selectionDirty = false;
return result;
}
Camera FindCamera()
{
Camera result = null;
switch (_selectBy)
{
case SelectByMode.HighestDepthCamera:
UpdateCameraCache();
result = FindCameraByHighestDepth(_cameraCount, _cameraCache);
break;
case SelectByMode.MainCameraTag:
result = Camera.main;
break;
case SelectByMode.EditorSceneView:
#if UNITY_EDITOR
UpdateCameraCache(true);
result = FindCameraByName(_cameraCount, _cameraCache, SceneCameraName);
#else
Debug.LogWarning("[AVProMovieCapture] Scene View can only be captured in the Unity editor. Select a different camera.");
#endif
break;
case SelectByMode.Tag:
UpdateCameraCache();
result = FindCameraByTag(_cameraCount, _cameraCache, _tag);
break;
case SelectByMode.Name:
UpdateCameraCache();
result = FindCameraByName(_cameraCount, _cameraCache, _name);
break;
case SelectByMode.Manual:
result = _camera;
break;
}
return result;
}
public void UpdateCameraCache(bool forceScanHiddenCameras = false)
{
// Prevent multiple scans per frame
if (Time.frameCount == _cameraCacheFrame) return;
if (!_scanHiddenCameras && !forceScanHiddenCameras)
{
// FAST - list active cameras
if (_cameraCache.Length < Camera.allCamerasCount)
{
_cameraCache = new Camera[Camera.allCamerasCount];
}
_cameraCount = Camera.GetAllCameras(_cameraCache);
}
else
{
// SLOW - list all cameras
_cameraCache = Resources.FindObjectsOfTypeAll();
_cameraCount = _cameraCache.Length;
}
_cameraCacheFrame = Time.frameCount;
}
static Camera FindCameraByHighestDepth(int cameraCount, Camera[] cameras)
{
Camera result = null;
float maxDepth = float.MinValue;
for (int i = 0; i < cameraCount; i++)
{
Camera c = cameras[i];
if (result == null || c.depth > maxDepth)
{
result = c;
maxDepth = c.depth;
}
}
return result;
}
static Camera FindCameraByTag(int cameraCount, Camera[] cameras, string tag)
{
Camera result = null;
for (int i = 0; i < cameraCount; i++)
{
Camera c = cameras[i];
if (c.CompareTag(tag))
{
result = c;
break;
}
}
return result;
}
static Camera FindCameraByName(int cameraCount, Camera[] cameras, string name)
{
Camera result = null;
for (int i = 0; i < cameraCount; i++)
{
Camera c = cameras[i];
if (c.name == name)
{
result = c;
break;
}
}
return result;
}
}
}