CinemachineVirtualCameraBaseEditor.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Cinemachine.Utility;
  7. #if CINEMACHINE_UNITY_INPUTSYSTEM
  8. using UnityEngine.InputSystem;
  9. #endif
  10. #if CINEMACHINE_HDRP || CINEMACHINE_URP
  11. #if CINEMACHINE_HDRP_7_3_1
  12. using UnityEngine.Rendering.HighDefinition;
  13. #else
  14. #if CINEMACHINE_URP
  15. using UnityEngine.Rendering.Universal;
  16. #else
  17. using UnityEngine.Experimental.Rendering.HDPipeline;
  18. #endif
  19. #endif
  20. #endif
  21. #if UNITY_2019_1_OR_NEWER
  22. using CameraExtensions = UnityEngine.Camera;
  23. #else
  24. // Needed only for Unity pre-2019.1 because Camera doesn't have these methods
  25. static class CameraExtensions
  26. {
  27. public static float HorizontalToVerticalFieldOfView(float f, float aspect)
  28. {
  29. return Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(f * Mathf.Deg2Rad * 0.5f) / aspect);
  30. }
  31. public static float VerticalToHorizontalFieldOfView(float f, float aspect)
  32. {
  33. return Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(f * Mathf.Deg2Rad * 0.5f) * aspect);
  34. }
  35. public static float FieldOfViewToFocalLength(float fov, float sensorHeight)
  36. {
  37. return sensorHeight * 0.5f / Mathf.Tan(Mathf.Deg2Rad * fov * 0.5f);
  38. }
  39. public static float FocalLengthToFieldOfView(float focalLength, float sensorHeight)
  40. {
  41. if (focalLength < UnityVectorExtensions.Epsilon)
  42. return 180f;
  43. return Mathf.Rad2Deg * 2.0f * Mathf.Atan(sensorHeight * 0.5f / focalLength);
  44. }
  45. }
  46. #endif
  47. namespace Cinemachine.Editor
  48. {
  49. /// <summary>
  50. /// Base class for virtual camera editors.
  51. /// Handles drawing the header and the basic properties.
  52. /// </summary>
  53. /// <typeparam name="T">The type of CinemachineVirtualCameraBase being edited</typeparam>
  54. public class CinemachineVirtualCameraBaseEditor<T> : BaseEditor<T> where T : CinemachineVirtualCameraBase
  55. {
  56. /// <summary>A collection of GUIContent for use in the inspector</summary>
  57. public static class Styles
  58. {
  59. /// <summary>GUIContent for Add Extension</summary>
  60. public static GUIContent addExtensionLabel = new GUIContent("Add Extension");
  61. /// <summary>GUIContent for no-multi-select message</summary>
  62. public static GUIContent virtualCameraChildrenInfoMsg
  63. = new GUIContent("The Virtual Camera Children field is not available when multiple objects are selected.");
  64. }
  65. static Type[] sExtensionTypes; // First entry is null
  66. static string[] sExtensionNames;
  67. bool IsPrefabBase { get; set; }
  68. /// <summary>Obsolete, do not use. Use the overload, which is more performant</summary>
  69. /// <returns>List of property names to exclude</returns>
  70. protected override List<string> GetExcludedPropertiesInInspector()
  71. { return base.GetExcludedPropertiesInInspector(); }
  72. /// <summary>Get the property names to exclude in the inspector.
  73. /// Implementation should call the base class implementation</summary>
  74. /// <param name="excluded">Add the names to this list</param>
  75. protected override void GetExcludedPropertiesInInspector(List<string> excluded)
  76. {
  77. base.GetExcludedPropertiesInInspector(excluded);
  78. if (Target.m_ExcludedPropertiesInInspector != null)
  79. excluded.AddRange(Target.m_ExcludedPropertiesInInspector);
  80. }
  81. /// <summary>Update state information on undo/redo</summary>
  82. void UpdateCameraState()
  83. {
  84. if (Target != null)
  85. Target.InternalUpdateCameraState(Vector3.up, -1);
  86. }
  87. /// <summary>Inspector panel is being enabled.
  88. /// Implementation should call the base class implementation</summary>
  89. protected virtual void OnEnable()
  90. {
  91. Undo.undoRedoPerformed += UpdateCameraState;
  92. IsPrefabBase = Target.gameObject.scene.name == null; // causes a small GC alloc
  93. if (sExtensionTypes == null)
  94. {
  95. // Populate the extension list
  96. List<Type> exts = new List<Type>();
  97. List<string> names = new List<string>();
  98. exts.Add(null);
  99. names.Add("(select)");
  100. var allExtensions
  101. = ReflectionHelpers.GetTypesInAllDependentAssemblies(
  102. (Type t) => typeof(CinemachineExtension).IsAssignableFrom(t) && !t.IsAbstract);
  103. foreach (Type t in allExtensions)
  104. {
  105. exts.Add(t);
  106. names.Add(t.Name);
  107. }
  108. sExtensionTypes = exts.ToArray();
  109. sExtensionNames = names.ToArray();
  110. }
  111. }
  112. /// <summary>Inspector panel is being disabled.
  113. /// Implementation should call the base class implementation</summary>
  114. protected virtual void OnDisable()
  115. {
  116. Undo.undoRedoPerformed -= UpdateCameraState;
  117. if (CinemachineBrain.SoloCamera == (ICinemachineCamera)Target)
  118. {
  119. CinemachineBrain.SoloCamera = null;
  120. InspectorUtility.RepaintGameView();
  121. }
  122. }
  123. /// <summary>Create the contents of the inspector panel.
  124. /// This implementation draws header and Extensions widget, and uses default algorithms
  125. /// to draw the properties in the inspector</summary>
  126. public override void OnInspectorGUI()
  127. {
  128. BeginInspector();
  129. DrawHeaderInInspector();
  130. DrawRemainingPropertiesInInspector();
  131. DrawExtensionsWidgetInInspector();
  132. }
  133. /// <summary>
  134. /// Draw the virtual camera header in the inspector.
  135. /// This includes Solo button, Live status, and global settings
  136. /// </summary>
  137. protected void DrawHeaderInInspector()
  138. {
  139. if (!IsPropertyExcluded("Header"))
  140. {
  141. DrawCameraStatusInInspector();
  142. DrawGlobalControlsInInspector();
  143. #if CINEMACHINE_UNITY_INPUTSYSTEM
  144. DrawInputProviderButtonInInspector();
  145. #endif
  146. ExcludeProperty("Header");
  147. }
  148. }
  149. #if CINEMACHINE_UNITY_INPUTSYSTEM
  150. static GUIContent s_InputProviderAddLabel = new GUIContent("Add Input Provider",
  151. "Adds CinemachineInputProvider component to this vcam, "
  152. + "if it does not have one, enabling the vcam to read input from Input Actions. "
  153. + "By default, a simple mouse XY input action is added.");
  154. static InputActionReference s_InputActionReference = null;
  155. void DrawInputProviderButtonInInspector()
  156. {
  157. bool needsButton = false;
  158. for (int i = 0; !needsButton && i < targets.Length; ++i)
  159. {
  160. var vcam = targets[i] as CinemachineVirtualCameraBase;
  161. if (vcam.RequiresUserInput() && vcam.GetComponent<AxisState.IInputAxisProvider>() == null)
  162. needsButton = true;
  163. }
  164. if (!needsButton)
  165. return;
  166. EditorGUILayout.Space();
  167. EditorGUILayout.HelpBox(
  168. "The InputSystem package is installed, but it is not used to control this vcam.",
  169. MessageType.Info);
  170. var rect = EditorGUILayout.GetControlRect(true);
  171. rect.x += EditorGUIUtility.labelWidth;
  172. rect.width -= EditorGUIUtility.labelWidth;
  173. if (GUI.Button(rect, s_InputProviderAddLabel))
  174. {
  175. if (s_InputActionReference == null)
  176. {
  177. s_InputActionReference = (InputActionReference)AssetDatabase.LoadAllAssetsAtPath(
  178. "Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions").
  179. FirstOrDefault(x => x.name == "Player/Look");
  180. }
  181. Undo.SetCurrentGroupName("Add CinemachineInputProvider");
  182. for (int i = 0; i < targets.Length; ++i)
  183. {
  184. var vcam = targets[i] as CinemachineVirtualCameraBase;
  185. if (vcam.GetComponent<AxisState.IInputAxisProvider>() != null)
  186. continue;
  187. var inputProvider = Undo.AddComponent<CinemachineInputProvider>(vcam.gameObject);
  188. inputProvider.XYAxis = s_InputActionReference;
  189. }
  190. }
  191. EditorGUILayout.Space();
  192. }
  193. #endif
  194. /// <summary>
  195. /// Draw the LookAt and Follow targets in the inspector
  196. /// </summary>
  197. /// <param name="followTarget">Follow target SerializedProperty</param>
  198. /// <param name="lookAtTarget">LookAt target SerializedProperty</param>
  199. protected void DrawTargetsInInspector(
  200. SerializedProperty followTarget, SerializedProperty lookAtTarget)
  201. {
  202. EditorGUI.BeginChangeCheck();
  203. if (!IsPropertyExcluded(followTarget.name))
  204. {
  205. if (Target.ParentCamera == null || Target.ParentCamera.Follow == null)
  206. EditorGUILayout.PropertyField(followTarget);
  207. else
  208. EditorGUILayout.PropertyField(followTarget,
  209. new GUIContent(followTarget.displayName + " Override"));
  210. ExcludeProperty(followTarget.name);
  211. }
  212. if (!IsPropertyExcluded(lookAtTarget.name))
  213. {
  214. if (Target.ParentCamera == null || Target.ParentCamera.LookAt == null)
  215. EditorGUILayout.PropertyField(lookAtTarget);
  216. else
  217. EditorGUILayout.PropertyField(lookAtTarget,
  218. new GUIContent(lookAtTarget.displayName + " Override"));
  219. ExcludeProperty(lookAtTarget.name);
  220. }
  221. if (EditorGUI.EndChangeCheck())
  222. serializedObject.ApplyModifiedProperties();
  223. }
  224. /// <summary>
  225. /// Draw the Extensions dropdown in the inspector
  226. /// </summary>
  227. protected void DrawExtensionsWidgetInInspector()
  228. {
  229. if (!IsPropertyExcluded("Extensions"))
  230. {
  231. EditorGUILayout.Space();
  232. EditorGUILayout.LabelField("Extensions", EditorStyles.boldLabel);
  233. Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
  234. rect = EditorGUI.PrefixLabel(rect, Styles.addExtensionLabel);
  235. int selection = EditorGUI.Popup(rect, 0, sExtensionNames);
  236. if (selection > 0)
  237. {
  238. Type extType = sExtensionTypes[selection];
  239. for (int i = 0; i < targets.Length; i++)
  240. {
  241. var targetGO = (targets[i] as CinemachineVirtualCameraBase).gameObject;
  242. if (targetGO != null && targetGO.GetComponent(extType) == null)
  243. Undo.AddComponent(targetGO, extType);
  244. }
  245. }
  246. ExcludeProperty("Extensions");
  247. }
  248. }
  249. /// <summary>
  250. /// Draw the Live status in the inspector, and the Solo button
  251. /// </summary>
  252. protected void DrawCameraStatusInInspector()
  253. {
  254. if (Selection.objects.Length > 1)
  255. return;
  256. // Is the camera navel-gazing?
  257. CameraState state = Target.State;
  258. if (state.HasLookAt && (state.ReferenceLookAt - state.CorrectedPosition).AlmostZero())
  259. EditorGUILayout.HelpBox(
  260. "The camera is positioned on the same point at which it is trying to look.",
  261. MessageType.Warning);
  262. // No status and Solo for prefabs
  263. if (IsPrefabBase)
  264. return;
  265. // Active status and Solo button
  266. Rect rect = EditorGUILayout.GetControlRect(true);
  267. Rect rectLabel = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height);
  268. rect.width -= rectLabel.width;
  269. rect.x += rectLabel.width;
  270. Color color = GUI.color;
  271. bool isSolo = (CinemachineBrain.SoloCamera == (ICinemachineCamera)Target);
  272. if (isSolo)
  273. GUI.color = CinemachineBrain.GetSoloGUIColor();
  274. bool isLive = CinemachineCore.Instance.IsLive(Target);
  275. GUI.enabled = isLive;
  276. GUI.Label(rectLabel, isLive ? "Status: Live"
  277. : (Target.isActiveAndEnabled ? "Status: Standby" : "Status: Disabled"));
  278. GUI.enabled = true;
  279. float labelWidth = 0;
  280. GUIContent updateText = GUIContent.none;
  281. UpdateTracker.UpdateClock updateMode = CinemachineCore.Instance.GetVcamUpdateStatus(Target);
  282. if (Application.isPlaying)
  283. {
  284. updateText = new GUIContent(
  285. updateMode == UpdateTracker.UpdateClock.Fixed ? " Fixed Update" : " Late Update");
  286. var textDimensions = GUI.skin.label.CalcSize(updateText);
  287. labelWidth = textDimensions.x;
  288. }
  289. rect.width -= labelWidth;
  290. if (GUI.Button(rect, "Solo", "Button"))
  291. {
  292. isSolo = !isSolo;
  293. CinemachineBrain.SoloCamera = isSolo ? Target : null;
  294. InspectorUtility.RepaintGameView();
  295. }
  296. GUI.color = color;
  297. if (isSolo && !Application.isPlaying)
  298. InspectorUtility.RepaintGameView();
  299. if (labelWidth > 0)
  300. {
  301. GUI.enabled = false;
  302. rect.x += rect.width; rect.width = labelWidth;
  303. GUI.Label(rect, updateText);
  304. GUI.enabled = true;
  305. }
  306. }
  307. static GUIContent ShowInGameGuidesLabel = new GUIContent(
  308. "Game Window Guides",
  309. "Enable the display of overlays in the Game window. "
  310. + "You can adjust colours and opacity in Cinemachine Preferences.");
  311. static GUIContent SaveDuringPlayLabel = new GUIContent(
  312. "Save During Play",
  313. "If checked, Virtual Camera settings changes made during Play Mode "
  314. + "will be propagated back to the scene when Play Mode is exited.");
  315. /// <summary>
  316. /// Draw the global settings controls in the inspector
  317. /// </summary>
  318. protected void DrawGlobalControlsInInspector()
  319. {
  320. CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides
  321. = EditorGUILayout.Toggle(ShowInGameGuidesLabel,
  322. CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides);
  323. SaveDuringPlay.SaveDuringPlay.Enabled
  324. = EditorGUILayout.Toggle(SaveDuringPlayLabel, SaveDuringPlay.SaveDuringPlay.Enabled);
  325. if (Application.isPlaying && SaveDuringPlay.SaveDuringPlay.Enabled)
  326. EditorGUILayout.HelpBox(
  327. " Virtual Camera settings changes made during Play Mode will be "
  328. + "propagated back to the scene when Play Mode is exited.",
  329. MessageType.Info);
  330. }
  331. LensSettingsInspectorHelper m_LensSettingsInspectorHelper;
  332. internal bool IsHorizontalFOVUsed() =>
  333. m_LensSettingsInspectorHelper != null && m_LensSettingsInspectorHelper.UseHorizontalFOV;
  334. /// <summary>
  335. /// Draw the Lens Settings controls in the inspector
  336. /// </summary>
  337. /// <param name="property">The SerializedProperty for the field of type LensSettings field</param>
  338. protected void DrawLensSettingsInInspector(SerializedProperty property)
  339. {
  340. if (IsPropertyExcluded(property.name))
  341. return;
  342. if (m_LensSettingsInspectorHelper == null)
  343. m_LensSettingsInspectorHelper = new LensSettingsInspectorHelper();
  344. // This should be a global UX setting, but the best we can do now is to query
  345. // the associated camera (if any) for the lens FOV display mode
  346. Camera camera = null;
  347. var brain = CinemachineCore.Instance.FindPotentialTargetBrain(Target);
  348. if (brain != null)
  349. camera = brain.OutputCamera;
  350. m_LensSettingsInspectorHelper.SnapshotCameraShadowValues(property, camera);
  351. m_LensSettingsInspectorHelper.DrawLensSettingsInInspector(property);
  352. ExcludeProperty(property.name);
  353. }
  354. }
  355. // Helper for drawing lensSettings in inspector
  356. class LensSettingsInspectorHelper
  357. {
  358. GUIContent[] m_PresetOptions;
  359. GUIContent[] m_PhysicalPresetOptions;
  360. LensSettings m_LensSettingsDef = new LensSettings(); // to access name strings
  361. static readonly GUIContent EditPresetsLabel = new GUIContent("Edit Presets...");
  362. static readonly GUIContent LensLabel = new GUIContent("Lens", "Lens settings to apply to the camera");
  363. static readonly GUIContent HFOVLabel = new GUIContent("Horizontal FOV", "Horizontal Field of View");
  364. static readonly GUIContent VFOVLabel = new GUIContent("Vertical FOV", "Vertical Field of View");
  365. static readonly GUIContent FocalLengthLabel = new GUIContent("Focal Length", "The length of the lens (in mm)");
  366. static readonly GUIContent OrthoSizeLabel = new GUIContent("Ortho Size", "When using an orthographic camera, "
  367. + "this defines the half-height, in world coordinates, of the camera view.");
  368. static readonly GUIContent SensorSizeLabel = new GUIContent("Sensor Size",
  369. "Actual size of the image sensor (in mm), used to "
  370. + "convert between focal length and field of vue.");
  371. static readonly GUIContent AdvancedLabel = new GUIContent("Advanced");
  372. bool IsOrtho;
  373. bool IsPhysical;
  374. Vector2 SensorSize;
  375. internal bool UseHorizontalFOV;
  376. static bool s_AdvancedExpanded;
  377. SerializedProperty ModeOverrideProperty;
  378. #if CINEMACHINE_HDRP
  379. GUIContent PhysicalPropertiesLabel;
  380. static bool mPhysicalExapnded;
  381. #endif
  382. public LensSettingsInspectorHelper()
  383. {
  384. #if CINEMACHINE_HDRP
  385. PhysicalPropertiesLabel = new GUIContent("Physical Properties", "Physical properties of the lens");
  386. #endif
  387. var options = new List<GUIContent>();
  388. CinemachineLensPresets presets = CinemachineLensPresets.InstanceIfExists;
  389. for (int i = 0; presets != null && i < presets.m_Presets.Length; ++i)
  390. options.Add(new GUIContent(presets.m_Presets[i].m_Name));
  391. options.Add(EditPresetsLabel);
  392. m_PresetOptions = options.ToArray();
  393. options.Clear();
  394. for (int i = 0; presets != null && i < presets.m_PhysicalPresets.Length; ++i)
  395. options.Add(new GUIContent(presets.m_PhysicalPresets[i].m_Name));
  396. options.Add(EditPresetsLabel);
  397. m_PhysicalPresetOptions = options.ToArray();
  398. }
  399. public void SnapshotCameraShadowValues(SerializedProperty property, Camera camera)
  400. {
  401. ModeOverrideProperty = property.FindPropertyRelative(() => m_LensSettingsDef.ModeOverride);
  402. // Assume lens is up-to-date
  403. UseHorizontalFOV = false;
  404. var lensObject = SerializedPropertyHelper.GetPropertyValue(property);
  405. IsOrtho = AccessProperty<bool>(typeof(LensSettings), lensObject, "Orthographic");
  406. IsPhysical = AccessProperty<bool>(typeof(LensSettings), lensObject, "IsPhysicalCamera");
  407. SensorSize = AccessProperty<Vector2>(typeof(LensSettings), lensObject, "SensorSize");
  408. // Then pull from actual camera if appropriate
  409. if (camera != null)
  410. {
  411. #if UNITY_2019_1_OR_NEWER
  412. // This should really be a global setting, but for now there is no better way than this!
  413. var p = new SerializedObject(camera).FindProperty("m_FOVAxisMode");
  414. if (p != null && p.intValue == (int)Camera.FieldOfViewAxis.Horizontal)
  415. UseHorizontalFOV = true;
  416. #endif
  417. // It's possible that the lens isn't synched with its camera - fix that here
  418. if (ModeOverrideProperty.intValue == (int)LensSettings.OverrideModes.None)
  419. {
  420. IsOrtho = camera.orthographic;
  421. IsPhysical = camera.usePhysicalProperties;
  422. SensorSize = IsPhysical ? camera.sensorSize : new Vector2(camera.aspect, 1f);
  423. }
  424. }
  425. var nearClipPlaneProperty = property.FindPropertyRelative("NearClipPlane");
  426. if (!IsOrtho)
  427. {
  428. nearClipPlaneProperty.floatValue = Mathf.Max(nearClipPlaneProperty.floatValue, 0.01f);
  429. property.serializedObject.ApplyModifiedPropertiesWithoutUndo();
  430. }
  431. }
  432. static T AccessProperty<T>(Type type, object obj, string memberName)
  433. {
  434. if (string.IsNullOrEmpty(memberName) || (type == null))
  435. return default(T);
  436. System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.Public;
  437. if (obj != null)
  438. bindingFlags |= System.Reflection.BindingFlags.Instance;
  439. else
  440. bindingFlags |= System.Reflection.BindingFlags.Static;
  441. System.Reflection.PropertyInfo pi = type.GetProperty(memberName, bindingFlags);
  442. if ((pi != null) && (pi.PropertyType == typeof(T)))
  443. return (T)pi.GetValue(obj, null);
  444. else
  445. return default(T);
  446. }
  447. public void DrawLensSettingsInInspector(SerializedProperty property)
  448. {
  449. Rect rect = EditorGUILayout.GetControlRect(true);
  450. var lensLabelWidth = GUI.skin.label.CalcSize(LensLabel).x;
  451. var foldoutLabelWidth = lensLabelWidth;
  452. property.isExpanded = EditorGUI.Foldout(
  453. new Rect(rect.x, rect.y, foldoutLabelWidth, rect.height),
  454. property.isExpanded, LensLabel, true);
  455. if (!property.isExpanded)
  456. {
  457. // Put the FOV on the same line
  458. var oldLabelWidth = EditorGUIUtility.labelWidth;
  459. EditorGUIUtility.labelWidth -= foldoutLabelWidth;
  460. rect.x += foldoutLabelWidth; rect.width -= foldoutLabelWidth;
  461. DrawLensFocusInInspector(rect, property);
  462. EditorGUIUtility.labelWidth = oldLabelWidth;
  463. }
  464. else
  465. {
  466. ++EditorGUI.indentLevel;
  467. rect = EditorGUILayout.GetControlRect(true);
  468. DrawLensFocusInInspector(rect, property);
  469. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.NearClipPlane));
  470. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.FarClipPlane));
  471. if (IsPhysical)
  472. {
  473. #if CINEMACHINE_HDRP
  474. mPhysicalExapnded = EditorGUILayout.Foldout(mPhysicalExapnded, PhysicalPropertiesLabel, true);
  475. if (mPhysicalExapnded)
  476. {
  477. ++EditorGUI.indentLevel;
  478. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.Aperture));
  479. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.Iso));
  480. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.ShutterSpeed));
  481. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.BladeCount));
  482. rect = EditorGUILayout.GetControlRect(true);
  483. var curvature = property.FindPropertyRelative(() => m_LensSettingsDef.Curvature);
  484. using (var propertyScope = new EditorGUI.PropertyScope(rect, new GUIContent("Curvature"), curvature))
  485. {
  486. var v = curvature.vector2Value;
  487. // The layout system breaks alignment when mixing inspector fields with custom layout'd
  488. // fields as soon as a scrollbar is needed in the inspector, so we'll do the layout
  489. // manually instead
  490. const int kFloatFieldWidth = 50;
  491. const int kSeparatorWidth = 5;
  492. float indentOffset = EditorGUI.indentLevel * 15f;
  493. var labelRect = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth - indentOffset, rect.height);
  494. var floatFieldLeft = new Rect(labelRect.xMax, rect.y, kFloatFieldWidth + indentOffset, rect.height);
  495. var sliderRect = new Rect(floatFieldLeft.xMax + kSeparatorWidth - indentOffset, rect.y, rect.width - labelRect.width - kFloatFieldWidth * 2 - kSeparatorWidth * 2, rect.height);
  496. var floatFieldRight = new Rect(sliderRect.xMax + kSeparatorWidth - indentOffset, rect.y, kFloatFieldWidth + indentOffset, rect.height);
  497. EditorGUI.PrefixLabel(labelRect, propertyScope.content);
  498. v.x = EditorGUI.FloatField(floatFieldLeft, v.x);
  499. EditorGUI.MinMaxSlider(sliderRect, ref v.x, ref v.y, HDPhysicalCamera.kMinAperture, HDPhysicalCamera.kMaxAperture);
  500. v.y = EditorGUI.FloatField(floatFieldRight, v.y);
  501. curvature.vector2Value = v;
  502. }
  503. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.BarrelClipping));
  504. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.Anamorphism));
  505. DrawSensorSizeInInspector(property);
  506. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.LensShift));
  507. if (ModeOverrideProperty.intValue != (int)LensSettings.OverrideModes.None)
  508. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.GateFit));
  509. #if UNITY_2022_2_OR_NEWER
  510. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.FocusDistance));
  511. #endif
  512. --EditorGUI.indentLevel;
  513. }
  514. #else
  515. DrawSensorSizeInInspector(property);
  516. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.LensShift));
  517. if (ModeOverrideProperty.intValue != (int)LensSettings.OverrideModes.None)
  518. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.GateFit));
  519. #if UNITY_2022_2_OR_NEWER
  520. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.FocusDistance));
  521. #endif
  522. #endif
  523. }
  524. EditorGUILayout.PropertyField(property.FindPropertyRelative(() => m_LensSettingsDef.Dutch));
  525. s_AdvancedExpanded = EditorGUILayout.Foldout(s_AdvancedExpanded, AdvancedLabel);
  526. if (s_AdvancedExpanded)
  527. {
  528. ++EditorGUI.indentLevel;
  529. EditorGUILayout.HelpBox("Setting a mode override here implies changes to the Camera component when "
  530. + "Cinemachine activates this Virtual Camera, and the changes will remain after the Virtual "
  531. + "Camera deactivation. If you set a mode override in any Virtual Camera, you should set "
  532. + "one in all Virtual Cameras.", MessageType.Info);
  533. EditorGUILayout.PropertyField(ModeOverrideProperty);
  534. --EditorGUI.indentLevel;
  535. }
  536. --EditorGUI.indentLevel;
  537. }
  538. property.serializedObject.ApplyModifiedProperties();
  539. }
  540. static float ExtraSpaceHackWTF() { return EditorGUI.indentLevel * (EditorGUIUtility.singleLineHeight - 3); }
  541. void DrawSensorSizeInInspector(SerializedProperty property)
  542. {
  543. if (ModeOverrideProperty.intValue != (int)LensSettings.OverrideModes.None)
  544. {
  545. property = property.FindPropertyRelative("m_SensorSize");
  546. var rect = EditorGUILayout.GetControlRect(true);
  547. EditorGUI.BeginProperty(rect, SensorSizeLabel, property);
  548. var v = EditorGUI.Vector2Field(rect, SensorSizeLabel, property.vector2Value);
  549. v.x = Mathf.Max(v.x, 0.1f);
  550. v.y = Mathf.Max(v.y, 0.1f);
  551. property.vector2Value = v;
  552. EditorGUI.EndProperty();
  553. }
  554. }
  555. void DrawLensFocusInInspector(Rect rect, SerializedProperty property)
  556. {
  557. if (IsOrtho)
  558. EditorGUI.PropertyField(
  559. rect, property.FindPropertyRelative(() => m_LensSettingsDef.OrthographicSize), OrthoSizeLabel);
  560. else if (IsPhysical)
  561. DrawFocalLengthControl(rect, property, FocalLengthLabel);
  562. else
  563. DrawFOVControl(rect, property);
  564. }
  565. void DrawFOVControl(Rect rect, SerializedProperty property)
  566. {
  567. const float hSpace = 2;
  568. var label = UseHorizontalFOV ? HFOVLabel : VFOVLabel;
  569. var FOVProperty = property.FindPropertyRelative(() => m_LensSettingsDef.FieldOfView);
  570. float aspect = SensorSize.x / SensorSize.y;
  571. float dropdownWidth = (rect.width - EditorGUIUtility.labelWidth) / 4;
  572. rect.width -= dropdownWidth + hSpace;
  573. float f = FOVProperty.floatValue;
  574. if (UseHorizontalFOV)
  575. f = CameraExtensions.VerticalToHorizontalFieldOfView(f, aspect);
  576. EditorGUI.BeginProperty(rect, label, FOVProperty);
  577. f = EditorGUI.FloatField(rect, label, f);
  578. if (UseHorizontalFOV)
  579. f = CameraExtensions.HorizontalToVerticalFieldOfView(Mathf.Clamp(f, 1, 179), aspect);
  580. if (!Mathf.Approximately(FOVProperty.floatValue, f))
  581. FOVProperty.floatValue = Mathf.Clamp(f, 1, 179);
  582. EditorGUI.EndProperty();
  583. rect.x += rect.width + hSpace; rect.width = dropdownWidth;
  584. CinemachineLensPresets presets = CinemachineLensPresets.InstanceIfExists;
  585. int preset = (presets == null) ? -1 : presets.GetMatchingPreset(FOVProperty.floatValue);
  586. rect.x -= ExtraSpaceHackWTF(); rect.width += ExtraSpaceHackWTF();
  587. int selection = EditorGUI.Popup(rect, GUIContent.none, preset, m_PresetOptions);
  588. if (selection == m_PresetOptions.Length-1 && CinemachineLensPresets.Instance != null)
  589. Selection.activeObject = presets = CinemachineLensPresets.Instance;
  590. else if (selection >= 0 && selection < m_PresetOptions.Length-1)
  591. {
  592. var vfov = presets.m_Presets[selection].m_FieldOfView;
  593. FOVProperty.floatValue = vfov;
  594. property.serializedObject.ApplyModifiedProperties();
  595. }
  596. }
  597. void DrawFocalLengthControl(Rect rect, SerializedProperty property, GUIContent label)
  598. {
  599. const float hSpace = 2;
  600. var FOVProperty = property.FindPropertyRelative(() => m_LensSettingsDef.FieldOfView);
  601. float dropdownWidth = (rect.width - EditorGUIUtility.labelWidth) / 4;
  602. rect.width -= dropdownWidth + hSpace;
  603. float f = CameraExtensions.FieldOfViewToFocalLength(FOVProperty.floatValue, SensorSize.y);
  604. EditorGUI.BeginProperty(rect, label, FOVProperty);
  605. f = EditorGUI.FloatField(rect, label, f);
  606. f = CameraExtensions.FocalLengthToFieldOfView(Mathf.Max(f, 0.0001f), SensorSize.y);
  607. if (!Mathf.Approximately(FOVProperty.floatValue, f))
  608. FOVProperty.floatValue = Mathf.Clamp(f, 1, 179);
  609. EditorGUI.EndProperty();
  610. rect.x += rect.width + hSpace; rect.width = dropdownWidth;
  611. #if CINEMACHINE_HDRP
  612. CinemachineLensPresets presets = CinemachineLensPresets.InstanceIfExists;
  613. int preset = -1;
  614. if (presets != null)
  615. {
  616. var focalLength = CameraExtensions.FieldOfViewToFocalLength(FOVProperty.floatValue, SensorSize.y);
  617. var aperture = property.FindPropertyRelative(() => m_LensSettingsDef.Aperture).floatValue;
  618. var iso = property.FindPropertyRelative(() => m_LensSettingsDef.Iso).intValue;
  619. var shutterSpeed = property.FindPropertyRelative(() => m_LensSettingsDef.ShutterSpeed).floatValue;
  620. var bladeCount = property.FindPropertyRelative(() => m_LensSettingsDef.BladeCount).intValue;
  621. var curvature = property.FindPropertyRelative(() => m_LensSettingsDef.Curvature).vector2Value;
  622. var barrelClipping = property.FindPropertyRelative(() => m_LensSettingsDef.BarrelClipping).floatValue;
  623. var anamprphism = property.FindPropertyRelative(() => m_LensSettingsDef.Anamorphism).floatValue;
  624. var lensShift = property.FindPropertyRelative(() => m_LensSettingsDef.LensShift).vector2Value;
  625. preset = presets.GetMatchingPhysicalPreset(
  626. focalLength, iso, shutterSpeed, aperture, bladeCount,
  627. curvature, barrelClipping, anamprphism, lensShift);
  628. }
  629. rect.x -= ExtraSpaceHackWTF(); rect.width += ExtraSpaceHackWTF();
  630. int selection = EditorGUI.Popup(rect, GUIContent.none, preset, m_PhysicalPresetOptions);
  631. if (selection == m_PhysicalPresetOptions.Length-1 && CinemachineLensPresets.Instance != null)
  632. Selection.activeObject = presets = CinemachineLensPresets.Instance;
  633. else if (selection >= 0 && selection < m_PhysicalPresetOptions.Length-1)
  634. {
  635. var v = presets.m_PhysicalPresets[selection];
  636. FOVProperty.floatValue = CameraExtensions.FocalLengthToFieldOfView(v.m_FocalLength, SensorSize.y);
  637. property.FindPropertyRelative(() => m_LensSettingsDef.Aperture).floatValue = v.Aperture;
  638. property.FindPropertyRelative(() => m_LensSettingsDef.Iso).intValue = v.Iso;
  639. property.FindPropertyRelative(() => m_LensSettingsDef.ShutterSpeed).floatValue = v.ShutterSpeed;
  640. property.FindPropertyRelative(() => m_LensSettingsDef.BladeCount).intValue = v.BladeCount;
  641. property.FindPropertyRelative(() => m_LensSettingsDef.Curvature).vector2Value = v.Curvature;
  642. property.FindPropertyRelative(() => m_LensSettingsDef.BarrelClipping).floatValue = v.BarrelClipping;
  643. property.FindPropertyRelative(() => m_LensSettingsDef.Anamorphism).floatValue = v.Anamorphism;
  644. property.FindPropertyRelative(() => m_LensSettingsDef.LensShift).vector2Value = v.LensShift;
  645. property.serializedObject.ApplyModifiedProperties();
  646. }
  647. #else
  648. CinemachineLensPresets presets = CinemachineLensPresets.InstanceIfExists;
  649. int preset = (presets == null) ? -1 : presets.GetMatchingPhysicalPreset(
  650. CameraExtensions.FieldOfViewToFocalLength(FOVProperty.floatValue, SensorSize.y));
  651. rect.x -= ExtraSpaceHackWTF(); rect.width += ExtraSpaceHackWTF();
  652. int selection = EditorGUI.Popup(rect, GUIContent.none, preset, m_PhysicalPresetOptions);
  653. if (selection == m_PhysicalPresetOptions.Length-1 && CinemachineLensPresets.Instance != null)
  654. Selection.activeObject = presets = CinemachineLensPresets.Instance;
  655. else if (selection >= 0 && selection < m_PhysicalPresetOptions.Length-1)
  656. {
  657. FOVProperty.floatValue = CameraExtensions.FocalLengthToFieldOfView(
  658. presets.m_PhysicalPresets[selection].m_FocalLength, SensorSize.y);
  659. property.serializedObject.ApplyModifiedProperties();
  660. }
  661. #endif
  662. }
  663. }
  664. }