CinemachineToolbarOverlay.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #if UNITY_2021_2_OR_NEWER
  2. using System;
  3. using UnityEditor;
  4. using UnityEditor.EditorTools;
  5. using UnityEditor.Overlays;
  6. using UnityEditor.Toolbars;
  7. using UnityEngine;
  8. using UnityEngine.UIElements;
  9. using System.Collections.Generic;
  10. namespace Cinemachine.Editor
  11. {
  12. #if UNITY_2022_1_OR_NEWER
  13. /// <summary>
  14. /// This is a generic Tool class for Cinemachine tools.
  15. /// To create a new tool, inherit from CinemachineTool and implement GetIcon().
  16. /// Your new tool will need to have the [EditorTool("tool-name", typeof(CinemachineVirtualCameraBase))] attribute.
  17. ///
  18. /// A tool will be drawn iff it has been registered using CinemachineSceneToolUtility.RegisterTool.
  19. /// This is generally done in the OnEnable function of the editor script of the cinemachine component
  20. /// (CinemachineVirtualCamera, CinemachineComponentBase), for which the tool was meant.
  21. /// To unregister, call CinemachineSceneToolUtility.UnregisterTool in the same script's OnDisable function.
  22. ///
  23. /// To draw the handles related to the tool, you need to implement your drawing function and call it in the
  24. /// editor script's OnSceneGUI function. An alternative for drawing handles is to override this function's
  25. /// OnToolGUI or OnDrawHandles functions (see EditorTool or IDrawSelectedHandles docs for more information).
  26. ///
  27. /// To check, if a tool has been enabled/disabled in the editor script, use CinemachineSceneToolUtility.IsToolActive.
  28. /// </summary>
  29. public abstract class CinemachineTool : EditorTool, IDrawSelectedHandles
  30. {
  31. GUIContent m_IconContent;
  32. /// <summary>Implement this to set your Tool's icon and tooltip.</summary>
  33. /// <returns>A GUIContent with an icon set.</returns>
  34. protected abstract GUIContent GetIcon();
  35. /// <summary>This lets the editor find the icon of the tool.</summary>
  36. public override GUIContent toolbarIcon
  37. {
  38. get
  39. {
  40. if (m_IconContent == null || m_State.refreshIcon)
  41. {
  42. m_IconContent = GetIcon();
  43. m_State.refreshIcon = false;
  44. }
  45. return m_IconContent;
  46. }
  47. }
  48. /// <summary>This is called when the Tool is selected in the editor.</summary>
  49. public override void OnActivated()
  50. {
  51. base.OnActivated();
  52. CinemachineSceneToolUtility.SetTool(true, GetType());
  53. m_State.refreshIcon = true;
  54. m_State.isSelected = true;
  55. }
  56. /// <summary>This is called when the Tool is deselected in the editor.</summary>
  57. public override void OnWillBeDeactivated()
  58. {
  59. base.OnWillBeDeactivated();
  60. CinemachineSceneToolUtility.SetTool(false, GetType());
  61. m_State.refreshIcon = true;
  62. m_State.isSelected = false;
  63. }
  64. /// <summary>This checks whether this tool should be displayed or not.</summary>
  65. /// <returns>True, when this tool is to be drawn. False, otherwise.</returns>
  66. public override bool IsAvailable() => CinemachineSceneToolUtility.IsToolRequired(GetType());
  67. /// <summary>Implement IDrawSelectedHandles to draw gizmos for this tool even if it is not the active tool.</summary>
  68. public void OnDrawHandles()
  69. {
  70. }
  71. private protected string GetIconPath()
  72. {
  73. m_State.refreshIcon = m_State.isProSkin != EditorGUIUtility.isProSkin;
  74. m_State.isProSkin = EditorGUIUtility.isProSkin;
  75. return ScriptableObjectUtility.CinemachineRealativeInstallPath + "/Editor/EditorResources/Handles/" +
  76. (m_State.isProSkin ?
  77. (m_State.isSelected ? "Dark-Selected" : "Dark") :
  78. (m_State.isSelected ? "Light-Selected" : "Light")) + "/";
  79. }
  80. struct ToolState
  81. {
  82. public bool isSelected;
  83. public bool isProSkin;
  84. public bool refreshIcon;
  85. }
  86. ToolState m_State = new ToolState { refreshIcon = true };
  87. }
  88. [EditorTool("Field of View Tool", typeof(CinemachineVirtualCameraBase))]
  89. class FoVTool : CinemachineTool
  90. {
  91. protected override GUIContent GetIcon() =>
  92. new GUIContent
  93. {
  94. image = AssetDatabase.LoadAssetAtPath<Texture2D>(GetIconPath() + "FOV.png"),
  95. tooltip = "Field of View Tool",
  96. };
  97. }
  98. [EditorTool("Far-Near Clip Tool", typeof(CinemachineVirtualCameraBase))]
  99. class FarNearClipTool : CinemachineTool
  100. {
  101. protected override GUIContent GetIcon() =>
  102. new GUIContent
  103. {
  104. image = AssetDatabase.LoadAssetAtPath<Texture2D>(GetIconPath() + "FarNearClip.png"),
  105. tooltip = "Far/Near Clip Tool",
  106. };
  107. }
  108. [EditorTool("Follow Offset Tool", typeof(CinemachineVirtualCameraBase))]
  109. class FollowOffsetTool : CinemachineTool
  110. {
  111. protected override GUIContent GetIcon() =>
  112. new GUIContent
  113. {
  114. image = AssetDatabase.LoadAssetAtPath<Texture2D>(GetIconPath() + "FollowOffset.png"),
  115. tooltip = "Follow Offset Tool",
  116. };
  117. }
  118. [EditorTool("Tracked Object Offset Tool", typeof(CinemachineVirtualCameraBase))]
  119. class TrackedObjectOffsetTool : CinemachineTool
  120. {
  121. protected override GUIContent GetIcon() =>
  122. new GUIContent
  123. {
  124. image = AssetDatabase.LoadAssetAtPath<Texture2D>(GetIconPath() + "TrackedObjectOffset.png"),
  125. tooltip = "Tracked Object Offset Tool",
  126. };
  127. }
  128. #if false // We disable this tool window, because it has only one thing in it, which isn't so useful and is a bit confusing tbh
  129. /// <summary>
  130. /// To add your custom tools (EditorToolbarElement) to the Cinemachine Tool Settings toolbar,
  131. /// set CinemachineToolSettingsOverlay.customToolbarItems with your custom tools' IDs.
  132. ///
  133. /// By default, CinemachineToolSettingsOverlay.customToolbarItems is null.
  134. /// </summary>
  135. [Overlay(typeof(SceneView), "Cinemachine Tool Settings")]
  136. [Icon("Packages/com.unity.cinemachine/Gizmos/cm_logo.png")]
  137. public class CinemachineToolSettingsOverlay : Overlay, ICreateToolbar
  138. {
  139. static readonly string[] k_CmToolbarItems = { FreelookRigSelection.id };
  140. /// <summary>
  141. /// Override this method to return your visual element content.
  142. /// By default, this draws the same visual element as the HorizontalToolbar
  143. /// </summary>
  144. /// <returns>VisualElement for the Panel conent.</returns>
  145. public override VisualElement CreatePanelContent() => CreateContent(Layout.HorizontalToolbar);
  146. /// <summary>Set this with your custom tools' IDs.</summary>
  147. public static string[] customToolbarItems = null;
  148. /// <summary>The list of tools that this toolbar is going to contain.</summary>
  149. public IEnumerable<string> toolbarElements
  150. {
  151. get
  152. {
  153. if (customToolbarItems != null)
  154. {
  155. var toolbarItems = new List<string>(k_CmToolbarItems);
  156. toolbarItems.AddRange(customToolbarItems);
  157. return toolbarItems;
  158. }
  159. return k_CmToolbarItems;
  160. }
  161. }
  162. }
  163. [EditorToolbarElement(id, typeof(SceneView))]
  164. class FreelookRigSelection : EditorToolbarDropdown
  165. {
  166. public const string id = "FreelookRigSelection/Dropdown";
  167. public static int SelectedRig;
  168. Texture2D[] m_Icons;
  169. public FreelookRigSelection()
  170. {
  171. tooltip = "Freelook Rig Selection";
  172. clicked += FreelookRigSelectionMenu;
  173. EditorApplication.update += ShadowSelectedRigName;
  174. EditorApplication.update += DisplayIfRequired;
  175. m_Icons = new Texture2D[]
  176. {
  177. AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  178. + "/Editor/EditorResources/Handles/FreelookRigTop.png"),
  179. AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  180. + "/Editor/EditorResources/Handles/FreelookRigMiddle.png"),
  181. AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  182. + "/Editor/EditorResources/Handles/FreelookRigBottom.png"),
  183. };
  184. }
  185. ~FreelookRigSelection()
  186. {
  187. clicked -= FreelookRigSelectionMenu;
  188. EditorApplication.update -= ShadowSelectedRigName;
  189. EditorApplication.update -= DisplayIfRequired;
  190. }
  191. Type m_FreelookRigSelectionType = typeof(FreelookRigSelection);
  192. void DisplayIfRequired() => style.display =
  193. CinemachineSceneToolUtility.IsToolRequired(m_FreelookRigSelectionType)
  194. ? DisplayStyle.Flex : DisplayStyle.None;
  195. // text is currently only visibly in Panel mode due to this bug: https://jira.unity3d.com/browse/STO-2278
  196. void ShadowSelectedRigName()
  197. {
  198. var index = Mathf.Clamp(SelectedRig, 0, CinemachineFreeLookEditor.RigNames.Length - 1);
  199. text = CinemachineFreeLookEditor.RigNames[index].text;
  200. icon = m_Icons[index];
  201. }
  202. void FreelookRigSelectionMenu()
  203. {
  204. var menu = new GenericMenu();
  205. for (var i = 0; i < CinemachineFreeLookEditor.RigNames.Length; ++i)
  206. {
  207. var rigIndex = i; // vital to capture the index here for the lambda below
  208. menu.AddItem(CinemachineFreeLookEditor.RigNames[i], false, () =>
  209. {
  210. SelectedRig = rigIndex;
  211. var active = Selection.activeObject as GameObject;
  212. if (active != null)
  213. {
  214. var freelook = active.GetComponent<CinemachineFreeLook>();
  215. if (freelook != null)
  216. CinemachineFreeLookEditor.SetSelectedRig(freelook, rigIndex);
  217. }
  218. });
  219. }
  220. menu.DropDown(worldBound);
  221. }
  222. }
  223. #endif
  224. #else
  225. /// <summary>
  226. /// To display a CinemachineExclusiveEditorToolbarToggle in the Cinemachine Toolbar.
  227. /// </summary>
  228. [Overlay(typeof(SceneView), "Cinemachine")]
  229. [Icon("Packages/com.unity.cinemachine/Gizmos/cm_logo.png")]
  230. class CinemachineToolbarOverlay : ToolbarOverlay
  231. {
  232. public CinemachineToolbarOverlay()
  233. : base(
  234. //FreelookRigSelection.id,
  235. FoVTool.id,
  236. FarNearClipTool.id,
  237. FollowOffsetTool.id,
  238. TrackedObjectOffsetTool.id
  239. )
  240. {
  241. CinemachineSceneToolUtility.RegisterToolbarIsDisplayedHandler(() => displayed);
  242. CinemachineSceneToolUtility.RegisterToolbarDisplayHandler(v =>
  243. {
  244. if (displayed == v)
  245. {
  246. return false;
  247. }
  248. displayed = v;
  249. return true;
  250. });
  251. }
  252. }
  253. /// <summary>
  254. /// Creates a toggle tool on the Cinemachine toolbar that is exclusive with other
  255. /// CinemachineExclusiveEditorToolbarToggles. Meaning, that maximum one
  256. /// CinemachineExclusiveEditorToolbarToggle can be active at any time.
  257. /// </summary>
  258. abstract class CinemachineExclusiveEditorToolbarToggle : EditorToolbarToggle
  259. {
  260. protected CinemachineExclusiveEditorToolbarToggle()
  261. {
  262. var type = GetType();
  263. this.RegisterValueChangedCallback(
  264. v => CinemachineSceneToolUtility.SetTool(v.newValue, type));
  265. CinemachineSceneToolUtility.RegisterExclusiveToolHandlers(type, isOn => value = isOn,
  266. display => style.display = display ? DisplayStyle.Flex : DisplayStyle.None);
  267. }
  268. }
  269. [EditorToolbarElement(id, typeof(SceneView))]
  270. class FoVTool : CinemachineExclusiveEditorToolbarToggle
  271. {
  272. public const string id = "FoVTool/Toggle";
  273. public FoVTool()
  274. {
  275. icon = AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  276. + "/Editor/EditorResources/Handles/Dark/FOV.png");
  277. tooltip = "Field of View Tool";
  278. }
  279. }
  280. [EditorToolbarElement(id, typeof(SceneView))]
  281. class FarNearClipTool : CinemachineExclusiveEditorToolbarToggle
  282. {
  283. public const string id = "FarNearClipTool/Toggle";
  284. public FarNearClipTool()
  285. {
  286. icon = AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  287. + "/Editor/EditorResources/Handles/Dark/FarNearClip.png");
  288. tooltip = "Far/Near Clip Tool";
  289. }
  290. }
  291. [EditorToolbarElement(id, typeof(SceneView))]
  292. class FollowOffsetTool : CinemachineExclusiveEditorToolbarToggle
  293. {
  294. public const string id = "FollowOffsetTool/Toggle";
  295. public FollowOffsetTool()
  296. {
  297. icon = AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  298. + "/Editor/EditorResources/Handles/Dark/FollowOffset.png");
  299. tooltip = "Follow Offset Tool";
  300. }
  301. }
  302. [EditorToolbarElement(id, typeof(SceneView))]
  303. class TrackedObjectOffsetTool : CinemachineExclusiveEditorToolbarToggle
  304. {
  305. public const string id = "TrackedObjectOffsetTool/Toggle";
  306. public TrackedObjectOffsetTool()
  307. {
  308. icon = AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  309. + "/Editor/EditorResources/Handles/Dark/TrackedObjectOffset.png");
  310. tooltip = "Tracked Object Offset Tool";
  311. }
  312. }
  313. /// <summary>
  314. /// Creates a toggle tool on the Cinemachine toolbar.
  315. /// </summary>
  316. abstract class CinemachineEditorToolbarToggle : EditorToolbarToggle
  317. {
  318. protected CinemachineEditorToolbarToggle()
  319. {
  320. CinemachineSceneToolUtility.RegisterToolHandlers(GetType(), isOn => value = isOn,
  321. display => style.display = display ? DisplayStyle.Flex : DisplayStyle.None);
  322. }
  323. }
  324. #if false
  325. [EditorToolbarElement(id, typeof(SceneView))]
  326. class FreelookRigSelection : EditorToolbarDropdown
  327. {
  328. public const string id = "FreelookRigSelection/Dropdown";
  329. public static int SelectedRig;
  330. Texture2D[] m_Icons;
  331. public FreelookRigSelection()
  332. {
  333. tooltip = "Freelook Rig Selection";
  334. clicked += FreelookRigSelectionMenu;
  335. CinemachineSceneToolUtility.RegisterToolHandlers(GetType(), isOn => {},
  336. display => style.display = display ? DisplayStyle.Flex : DisplayStyle.None);
  337. EditorApplication.update += ShadowSelectedRigName;
  338. m_Icons = new Texture2D[]
  339. {
  340. AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  341. + "/Editor/EditorResources/Handles/FreelookRigTop.png"),
  342. AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  343. + "/Editor/EditorResources/Handles/FreelookRigMiddle.png"),
  344. AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.CinemachineRealativeInstallPath
  345. + "/Editor/EditorResources/Handles/FreelookRigBottom.png"),
  346. };
  347. }
  348. ~FreelookRigSelection()
  349. {
  350. clicked -= FreelookRigSelectionMenu;
  351. EditorApplication.update -= ShadowSelectedRigName;
  352. }
  353. void ShadowSelectedRigName()
  354. {
  355. var index = Mathf.Clamp(SelectedRig, 0, CinemachineFreeLookEditor.RigNames.Length - 1);
  356. icon = m_Icons[index];
  357. text = CinemachineFreeLookEditor.RigNames[index].text;
  358. }
  359. void FreelookRigSelectionMenu()
  360. {
  361. var menu = new GenericMenu();
  362. for (var i = 0; i < CinemachineFreeLookEditor.RigNames.Length; ++i)
  363. {
  364. var rigIndex = i; // vital to capture the index here for the lambda below
  365. menu.AddItem(CinemachineFreeLookEditor.RigNames[i], false, () =>
  366. {
  367. SelectedRig = rigIndex;
  368. var active = Selection.activeObject as GameObject;
  369. if (active != null)
  370. {
  371. var freelook = active.GetComponent<CinemachineFreeLook>();
  372. if (freelook != null)
  373. CinemachineFreeLookEditor.SetSelectedRig(freelook, rigIndex);
  374. }
  375. });
  376. }
  377. menu.DropDown(worldBound);
  378. }
  379. }
  380. #endif
  381. #endif
  382. }
  383. #endif