CinemachineSceneTools.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. #if UNITY_2021_2_OR_NEWER
  2. using System;
  3. using System.Collections.Generic;
  4. using Cinemachine.Utility;
  5. using UnityEditor;
  6. using UnityEditor.EditorTools;
  7. using UnityEngine;
  8. namespace Cinemachine.Editor
  9. {
  10. /// <summary>
  11. /// Static class that manages Cinemachine Tools. It knows which tool is active,
  12. /// and ensures that exclusive tools are not active at the same time.
  13. /// The tools and editors requiring tools register/unregister themselves here.
  14. /// </summary>
  15. static class CinemachineSceneToolUtility
  16. {
  17. #if UNITY_2022_1_OR_NEWER
  18. /// <summary>
  19. /// Checks whether tool is the currently active exclusive tool.
  20. /// </summary>
  21. /// <param name="tool">Tool to check.</param>
  22. /// <returns>True, when the tool is the active exclusive tool. False, otherwise.</returns>
  23. public static bool IsToolActive(Type tool)
  24. {
  25. return s_ActiveExclusiveTool == tool;
  26. }
  27. static Type s_ActiveExclusiveTool;
  28. /// <summary>
  29. /// Register your Type from the editor script's OnEnable function.
  30. /// This way CinemachineTools will know which tools to display.
  31. /// </summary>
  32. /// <param name="tool">Tool to register</param>
  33. public static void RegisterTool(Type tool)
  34. {
  35. if (s_RequiredTools.ContainsKey(tool))
  36. {
  37. s_RequiredTools[tool]++;
  38. }
  39. else
  40. {
  41. s_RequiredTools.Add(tool, 1);
  42. }
  43. s_TriggerToolBarRefresh = true;
  44. }
  45. /// <summary>
  46. /// Unregister your Type from the editor script's OnDisable function.
  47. /// This way CinemachineTools will know which tools to display.
  48. /// </summary>
  49. /// <param name="tool">Tool to register</param>
  50. public static void UnregisterTool(Type tool)
  51. {
  52. if (s_RequiredTools.ContainsKey(tool))
  53. {
  54. s_RequiredTools[tool]--;
  55. if (s_RequiredTools[tool] <= 0)
  56. {
  57. s_RequiredTools.Remove(tool);
  58. }
  59. }
  60. s_TriggerToolBarRefresh = true;
  61. }
  62. internal static bool IsToolRequired(Type tool)
  63. {
  64. return s_RequiredTools.ContainsKey(tool);
  65. }
  66. static Dictionary<Type, int> s_RequiredTools;
  67. internal static void SetTool(bool active, Type tool)
  68. {
  69. if (active)
  70. {
  71. s_ActiveExclusiveTool = tool;
  72. }
  73. else
  74. {
  75. s_ActiveExclusiveTool = s_ActiveExclusiveTool == tool ? null : s_ActiveExclusiveTool;
  76. }
  77. s_TriggerToolBarRefresh = true;
  78. }
  79. static CinemachineSceneToolUtility()
  80. {
  81. s_RequiredTools = new Dictionary<Type, int>();
  82. EditorApplication.update += RefreshToolbar;
  83. }
  84. static bool s_TriggerToolBarRefresh;
  85. static void RefreshToolbar()
  86. {
  87. if (s_TriggerToolBarRefresh)
  88. {
  89. #if UNITY_2022_2_OR_NEWER
  90. ToolManager.RefreshAvailableTools();
  91. #else
  92. // The following is a hack to refresh the toolbars
  93. foreach (var scene in SceneView.sceneViews)
  94. {
  95. if (((SceneView)scene).TryGetOverlay("unity-transform-toolbar", out var tools))
  96. {
  97. if (tools.displayed)
  98. {
  99. tools.displayed = false;
  100. tools.displayed = true;
  101. break;
  102. }
  103. }
  104. }
  105. #endif
  106. s_TriggerToolBarRefresh = false;
  107. }
  108. }
  109. #else
  110. /// <summary>
  111. /// Checks whether tool is the currently active exclusive tool.
  112. /// </summary>
  113. /// <param name="tool">Tool to check.</param>
  114. /// <returns>True, when the tool is the active exclusive tool. False, otherwise.</returns>
  115. public static bool IsToolActive(Type tool)
  116. {
  117. return s_ActiveExclusiveTool == tool;
  118. }
  119. static Type s_ActiveExclusiveTool;
  120. /// <summary>
  121. /// Register your Type from the editor script's OnEnable function.
  122. /// This way CinemachineTools will know which tools to display.
  123. /// </summary>
  124. /// <param name="tool">Tool to register</param>
  125. public static void RegisterTool(Type tool)
  126. {
  127. if (s_RequiredTools.ContainsKey(tool))
  128. {
  129. s_RequiredTools[tool]++;
  130. }
  131. else
  132. {
  133. s_RequiredTools.Add(tool, 1);
  134. }
  135. }
  136. /// <summary>
  137. /// Unregister your Type from the editor script's OnDisable function.
  138. /// This way CinemachineTools will know which tools to display.
  139. /// </summary>
  140. /// <param name="tool">Tool to register</param>
  141. public static void UnregisterTool(Type tool)
  142. {
  143. if (s_RequiredTools.ContainsKey(tool))
  144. {
  145. s_RequiredTools[tool]--;
  146. if (s_RequiredTools[tool] <= 0)
  147. {
  148. s_RequiredTools.Remove(tool);
  149. }
  150. }
  151. }
  152. static Dictionary<Type, int> s_RequiredTools;
  153. public delegate void ToolHandler(bool v);
  154. struct CinemachineSceneToolDelegates
  155. {
  156. public ToolHandler ToggleSetter;
  157. public ToolHandler IsDisplayedSetter;
  158. }
  159. static Dictionary<Type, CinemachineSceneToolDelegates> s_ExclusiveTools; // tools that can't be active at the same time
  160. static Dictionary<Type, CinemachineSceneToolDelegates> s_Tools; // tools without restrictions
  161. /// <summary>
  162. /// Use for registering tool handlers for tools that are exclusive with each other,
  163. /// meaning they cannot be active at the same time.
  164. /// </summary>
  165. /// <param name="tool">The tool to register.</param>
  166. /// <param name="toggleSetter">The tool's toggle value setter.</param>
  167. /// <param name="isDisplayedSetter">The tool's isDisplayed setter.</param>
  168. internal static void RegisterExclusiveToolHandlers(Type tool, ToolHandler toggleSetter, ToolHandler isDisplayedSetter)
  169. {
  170. RegisterToolHandlers(ref s_ExclusiveTools, tool, toggleSetter, isDisplayedSetter);
  171. }
  172. /// <summary>
  173. /// Use for registering tool handlers for tools that can be active anytime
  174. /// without taking other tools into consideration.
  175. /// </summary>
  176. /// <param name="tool">The tool to register.</param>
  177. /// <param name="toggleSetter">The tool's toggle value setter.</param>
  178. /// <param name="isDisplayedSetter">The tool's isDisplayed setter.</param>
  179. internal static void RegisterToolHandlers(Type tool, ToolHandler toggleSetter, ToolHandler isDisplayedSetter)
  180. {
  181. RegisterToolHandlers(ref s_Tools, tool, toggleSetter, isDisplayedSetter);
  182. }
  183. static void RegisterToolHandlers(ref Dictionary<Type, CinemachineSceneToolDelegates> tools,
  184. Type tool, ToolHandler toggleSetter, ToolHandler isDisplayedSetter)
  185. {
  186. if (tools.ContainsKey(tool))
  187. {
  188. tools.Remove(tool);
  189. }
  190. tools.Add(tool, new CinemachineSceneToolDelegates
  191. {
  192. ToggleSetter = toggleSetter,
  193. IsDisplayedSetter = isDisplayedSetter,
  194. });
  195. }
  196. internal delegate bool ToolbarHandler();
  197. static ToolbarHandler s_ToolBarIsDisplayed;
  198. internal static void RegisterToolbarIsDisplayedHandler(ToolbarHandler handler)
  199. {
  200. s_ToolBarIsDisplayed = handler;
  201. }
  202. static bool s_ToolbarTurnOffByMe;
  203. internal delegate bool ToolbarTurnOnOffHandler(bool on);
  204. static ToolbarTurnOnOffHandler s_ToolBarDisplay;
  205. internal static void RegisterToolbarDisplayHandler(ToolbarTurnOnOffHandler handler)
  206. {
  207. s_ToolBarDisplay = handler;
  208. }
  209. internal static void SetTool(bool active, Type tool)
  210. {
  211. if (active)
  212. {
  213. s_ActiveExclusiveTool = tool;
  214. EnsureCinemachineToolsAreExclusiveWithUnityTools();
  215. }
  216. else
  217. {
  218. s_ActiveExclusiveTool = s_ActiveExclusiveTool == tool ? null : s_ActiveExclusiveTool;
  219. }
  220. }
  221. static void EnsureCinemachineToolsAreExclusiveWithUnityTools()
  222. {
  223. foreach (var toolHandle in s_ExclusiveTools)
  224. {
  225. toolHandle.Value.ToggleSetter(toolHandle.Key == s_ActiveExclusiveTool);
  226. }
  227. if (s_ActiveExclusiveTool != null)
  228. {
  229. Tools.current = Tool.None; // Cinemachine tools are exclusive with unity tools
  230. }
  231. }
  232. static CinemachineSceneToolUtility()
  233. {
  234. s_ExclusiveTools = new Dictionary<Type, CinemachineSceneToolDelegates>();
  235. s_Tools = new Dictionary<Type, CinemachineSceneToolDelegates>();
  236. s_RequiredTools = new Dictionary<Type, int>();
  237. EditorApplication.update += () =>
  238. {
  239. if (s_RequiredTools.Count <= 0)
  240. {
  241. s_ToolbarTurnOffByMe |= s_ToolBarDisplay(false);
  242. }
  243. else if (s_ToolbarTurnOffByMe)
  244. {
  245. s_ToolBarDisplay(true);
  246. s_ToolbarTurnOffByMe = false;
  247. }
  248. var cmToolbarIsHidden = !s_ToolBarIsDisplayed();
  249. // if a unity tool is selected or cmToolbar is hidden, unselect our tools.
  250. if (s_ActiveExclusiveTool != null && (Tools.current != Tool.None || cmToolbarIsHidden))
  251. {
  252. SetTool(true, null);
  253. }
  254. if (!cmToolbarIsHidden)
  255. {
  256. // only display cm tools that are relevant for the current selection
  257. foreach (var toolHandle in s_ExclusiveTools)
  258. {
  259. toolHandle.Value.IsDisplayedSetter(s_RequiredTools.ContainsKey(toolHandle.Key));
  260. }
  261. foreach (var toolHandle in s_Tools)
  262. {
  263. toolHandle.Value.IsDisplayedSetter(s_RequiredTools.ContainsKey(toolHandle.Key));
  264. }
  265. }
  266. };
  267. }
  268. #endif
  269. }
  270. static class CinemachineSceneToolHelpers
  271. {
  272. public const float LineThickness = 2f;
  273. public static readonly Color HelperLineDefaultColor = new Color(255, 255, 255, 25);
  274. const float k_DottedLineSpacing = 4f;
  275. static GUIStyle s_LabelStyle = new GUIStyle
  276. {
  277. normal =
  278. {
  279. background = AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.
  280. CinemachineRealativeInstallPath + "/Editor/EditorResources/SceneToolsLabelBackground.png"),
  281. textColor = Handles.selectedColor,
  282. },
  283. fontStyle = FontStyle.Bold,
  284. padding = new RectOffset(5, 0, 5, 0)
  285. };
  286. public static float SliderHandleDelta(Vector3 newPos, Vector3 oldPos, Vector3 forward)
  287. {
  288. var delta = newPos - oldPos;
  289. return Mathf.Sign(Vector3.Dot(delta, forward)) * delta.magnitude;
  290. }
  291. /// <summary>
  292. /// Calculate delta and discard imprecision.
  293. /// </summary>
  294. public static Vector3 PositionHandleDelta(Quaternion rot, Vector3 newPos, Vector3 oldPos)
  295. {
  296. var delta =
  297. Quaternion.Inverse(rot) * (newPos - oldPos);
  298. delta = new Vector3(
  299. Mathf.Abs(delta.x) < UnityVectorExtensions.Epsilon ? 0 : delta.x,
  300. Mathf.Abs(delta.y) < UnityVectorExtensions.Epsilon ? 0 : delta.y,
  301. Mathf.Abs(delta.z) < UnityVectorExtensions.Epsilon ? 0 : delta.z);
  302. return delta;
  303. }
  304. public static void DrawLabel(Vector3 position, string text)
  305. {
  306. var labelOffset = HandleUtility.GetHandleSize(position) / 5f;
  307. Handles.Label(position + new Vector3(0, -labelOffset, 0), text, s_LabelStyle);
  308. }
  309. public static float CubeHandleCapSize(Vector3 position) => HandleUtility.GetHandleSize(position) / 10f;
  310. static int s_ScaleSliderHash = "ScaleSliderHash".GetHashCode();
  311. static float s_FOVAfterLastToolModification;
  312. public static void FovToolHandle(CinemachineVirtualCameraBase vcam, SerializedProperty lensProperty,
  313. in LensSettings lens, bool isLensHorizontal)
  314. {
  315. var orthographic = lens.Orthographic;
  316. if (GUIUtility.hotControl == 0)
  317. {
  318. s_FOVAfterLastToolModification = orthographic ? lens.OrthographicSize : lens.FieldOfView;
  319. }
  320. var originalColor = Handles.color;
  321. Handles.color = Handles.preselectionColor;
  322. var camPos = vcam.State.FinalPosition;
  323. var camRot = vcam.State.FinalOrientation;
  324. var camForward = camRot * Vector3.forward;
  325. EditorGUI.BeginChangeCheck();
  326. #if UNITY_2022_2_OR_NEWER
  327. var fovHandleId = GUIUtility.GetControlID(s_ScaleSliderHash, FocusType.Passive);
  328. var newFov = Handles.ScaleSlider(fovHandleId, s_FOVAfterLastToolModification,
  329. camPos, camForward, camRot, HandleUtility.GetHandleSize(camPos), 0.1f);
  330. #else
  331. var fovHandleId = GUIUtility.GetControlID(s_ScaleSliderHash, FocusType.Passive) + 1;
  332. var newFov = Handles.ScaleSlider(
  333. s_FOVAfterLastToolModification, camPos, camForward, camRot, HandleUtility.GetHandleSize(camPos), 0.1f);
  334. #endif
  335. if (EditorGUI.EndChangeCheck())
  336. {
  337. if (orthographic)
  338. {
  339. lensProperty.FindPropertyRelative("OrthographicSize").floatValue +=
  340. (s_FOVAfterLastToolModification - newFov);
  341. }
  342. else
  343. {
  344. lensProperty.FindPropertyRelative("FieldOfView").floatValue +=
  345. (s_FOVAfterLastToolModification - newFov);
  346. lensProperty.FindPropertyRelative("FieldOfView").floatValue =
  347. Mathf.Clamp(lensProperty.FindPropertyRelative("FieldOfView").floatValue, 1f, 179f);
  348. }
  349. lensProperty.serializedObject.ApplyModifiedProperties();
  350. }
  351. s_FOVAfterLastToolModification = newFov;
  352. var fovHandleDraggedOrHovered =
  353. GUIUtility.hotControl == fovHandleId || HandleUtility.nearestControl == fovHandleId;
  354. if (fovHandleDraggedOrHovered)
  355. {
  356. var labelPos = camPos + camForward * HandleUtility.GetHandleSize(camPos);
  357. if (lens.IsPhysicalCamera)
  358. {
  359. DrawLabel(labelPos, "Focal Length (" +
  360. Camera.FieldOfViewToFocalLength(lens.FieldOfView, lens.SensorSize.y).ToString("F1") + ")");
  361. }
  362. else if (orthographic)
  363. {
  364. DrawLabel(labelPos, "Orthographic Size (" +
  365. lens.OrthographicSize.ToString("F1") + ")");
  366. }
  367. else if (isLensHorizontal)
  368. {
  369. DrawLabel(labelPos, "Horizontal FOV (" +
  370. Camera.VerticalToHorizontalFieldOfView(lens.FieldOfView, lens.Aspect).ToString("F1") + ")");
  371. }
  372. else
  373. {
  374. DrawLabel(labelPos, "Vertical FOV (" +
  375. lens.FieldOfView.ToString("F1") + ")");
  376. }
  377. }
  378. Handles.color = fovHandleDraggedOrHovered ? Handles.selectedColor : HelperLineDefaultColor;
  379. var vcamLocalToWorld = Matrix4x4.TRS(camPos, camRot, Vector3.one);
  380. DrawFrustum(vcamLocalToWorld, lens);
  381. SoloOnDrag(GUIUtility.hotControl == fovHandleId, vcam, fovHandleId);
  382. Handles.color = originalColor;
  383. }
  384. public static void NearFarClipHandle(CinemachineVirtualCameraBase vcam, SerializedProperty lens)
  385. {
  386. var originalColor = Handles.color;
  387. Handles.color = Handles.preselectionColor;
  388. var vcamState = vcam.State;
  389. var camPos = vcamState.FinalPosition;
  390. var camRot = vcamState.FinalOrientation;
  391. var camForward = camRot * Vector3.forward;
  392. var nearClipPlane = lens.FindPropertyRelative("NearClipPlane");
  393. var farClipPlane = lens.FindPropertyRelative("FarClipPlane");
  394. var nearClipPos = camPos + camForward * nearClipPlane.floatValue;
  395. var farClipPos = camPos + camForward * farClipPlane.floatValue;
  396. var vcamLens = vcamState.Lens;
  397. EditorGUI.BeginChangeCheck();
  398. var ncHandleId = GUIUtility.GetControlID(FocusType.Passive);
  399. var newNearClipPos = Handles.Slider(ncHandleId, nearClipPos, camForward,
  400. CubeHandleCapSize(nearClipPos), Handles.CubeHandleCap, 0.5f);
  401. var fcHandleId = GUIUtility.GetControlID(FocusType.Passive);
  402. var newFarClipPos = Handles.Slider(fcHandleId, farClipPos, camForward,
  403. CubeHandleCapSize(farClipPos), Handles.CubeHandleCap, 0.5f);
  404. if (EditorGUI.EndChangeCheck())
  405. {
  406. nearClipPlane.floatValue +=
  407. SliderHandleDelta(newNearClipPos, nearClipPos, camForward);
  408. if (!vcamLens.Orthographic)
  409. {
  410. nearClipPlane.floatValue = Mathf.Max(0.01f, nearClipPlane.floatValue);
  411. }
  412. farClipPlane.floatValue +=
  413. SliderHandleDelta(newFarClipPos, farClipPos, camForward);
  414. lens.serializedObject.ApplyModifiedProperties();
  415. }
  416. var vcamLocalToWorld = Matrix4x4.TRS(camPos, camRot, Vector3.one);
  417. Handles.color = HelperLineDefaultColor;
  418. DrawFrustum(vcamLocalToWorld, vcamLens);
  419. if (GUIUtility.hotControl == ncHandleId || HandleUtility.nearestControl == ncHandleId)
  420. {
  421. DrawPreFrustum(vcamLocalToWorld, vcamLens);
  422. DrawLabel(nearClipPos, "Near Clip Plane (" + nearClipPlane.floatValue.ToString("F1") + ")");
  423. }
  424. if (GUIUtility.hotControl == fcHandleId || HandleUtility.nearestControl == fcHandleId)
  425. {
  426. DrawPreFrustum(vcamLocalToWorld, vcamLens);
  427. DrawLabel(farClipPos, "Far Clip Plane (" + farClipPlane.floatValue.ToString("F1") + ")");
  428. }
  429. SoloOnDrag(GUIUtility.hotControl == ncHandleId || GUIUtility.hotControl == fcHandleId,
  430. vcam, Mathf.Min(ncHandleId, fcHandleId));
  431. Handles.color = originalColor;
  432. }
  433. static void DrawPreFrustum(Matrix4x4 transform, LensSettings lens)
  434. {
  435. if (!lens.Orthographic && lens.NearClipPlane >= 0)
  436. {
  437. DrawPerspectiveFrustum(transform, lens.FieldOfView,
  438. lens.NearClipPlane, 0, lens.Aspect, true);
  439. }
  440. }
  441. static void DrawFrustum(Matrix4x4 transform, LensSettings lens)
  442. {
  443. if (lens.Orthographic)
  444. {
  445. DrawOrthographicFrustum(transform, lens.OrthographicSize,
  446. lens.FarClipPlane, lens.NearClipPlane, lens.Aspect);
  447. }
  448. else
  449. {
  450. DrawPerspectiveFrustum(transform, lens.FieldOfView,
  451. lens.FarClipPlane, lens.NearClipPlane, lens.Aspect, false);
  452. }
  453. }
  454. static void DrawOrthographicFrustum(Matrix4x4 transform,
  455. float orthographicSize, float farClipPlane, float nearClipRange, float aspect)
  456. {
  457. var originalMatrix = Handles.matrix;
  458. Handles.matrix = transform;
  459. var size = new Vector3(aspect * orthographicSize * 2,
  460. orthographicSize * 2, farClipPlane - nearClipRange);
  461. Handles.DrawWireCube(new Vector3(0, 0, (size.z / 2) + nearClipRange), size);
  462. Handles.matrix = originalMatrix;
  463. }
  464. static void DrawPerspectiveFrustum(Matrix4x4 transform,
  465. float fov, float farClipPlane, float nearClipRange, float aspect, bool dottedLine)
  466. {
  467. var originalMatrix = Handles.matrix;
  468. Handles.matrix = transform;
  469. fov = fov * 0.5f * Mathf.Deg2Rad;
  470. var tanfov = Mathf.Tan(fov);
  471. var farEnd = new Vector3(0, 0, farClipPlane);
  472. var endSizeX = new Vector3(farClipPlane * tanfov * aspect, 0, 0);
  473. var endSizeY = new Vector3(0, farClipPlane * tanfov, 0);
  474. Vector3 s1, s2, s3, s4;
  475. var e1 = farEnd + endSizeX + endSizeY;
  476. var e2 = farEnd - endSizeX + endSizeY;
  477. var e3 = farEnd - endSizeX - endSizeY;
  478. var e4 = farEnd + endSizeX - endSizeY;
  479. if (nearClipRange <= 0.0f)
  480. {
  481. s1 = s2 = s3 = s4 = Vector3.zero;
  482. }
  483. else
  484. {
  485. var startSizeX = new Vector3(nearClipRange * tanfov * aspect, 0, 0);
  486. var startSizeY = new Vector3(0, nearClipRange * tanfov, 0);
  487. var startPoint = new Vector3(0, 0, nearClipRange);
  488. s1 = startPoint + startSizeX + startSizeY;
  489. s2 = startPoint - startSizeX + startSizeY;
  490. s3 = startPoint - startSizeX - startSizeY;
  491. s4 = startPoint + startSizeX - startSizeY;
  492. if (dottedLine)
  493. {
  494. Handles.DrawDottedLine(s1, s2, k_DottedLineSpacing);
  495. Handles.DrawDottedLine(s2, s3, k_DottedLineSpacing);
  496. Handles.DrawDottedLine(s3, s4, k_DottedLineSpacing);
  497. Handles.DrawDottedLine(s4, s1, k_DottedLineSpacing);
  498. }
  499. else
  500. {
  501. Handles.DrawLine(s1, s2);
  502. Handles.DrawLine(s2, s3);
  503. Handles.DrawLine(s3, s4);
  504. Handles.DrawLine(s4, s1);
  505. }
  506. }
  507. if (dottedLine)
  508. {
  509. Handles.DrawDottedLine(e1, e2, k_DottedLineSpacing);
  510. Handles.DrawDottedLine(e2, e3, k_DottedLineSpacing);
  511. Handles.DrawDottedLine(e3, e4, k_DottedLineSpacing);
  512. Handles.DrawDottedLine(e4, e1, k_DottedLineSpacing);
  513. Handles.DrawDottedLine(s1, e1, k_DottedLineSpacing);
  514. Handles.DrawDottedLine(s2, e2, k_DottedLineSpacing);
  515. Handles.DrawDottedLine(s3, e3, k_DottedLineSpacing);
  516. Handles.DrawDottedLine(s4, e4, k_DottedLineSpacing);
  517. }
  518. else
  519. {
  520. Handles.DrawLine(e1, e2);
  521. Handles.DrawLine(e2, e3);
  522. Handles.DrawLine(e3, e4);
  523. Handles.DrawLine(e4, e1);
  524. Handles.DrawLine(s1, e1);
  525. Handles.DrawLine(s2, e2);
  526. Handles.DrawLine(s3, e3);
  527. Handles.DrawLine(s4, e4);
  528. }
  529. Handles.matrix = originalMatrix;
  530. }
  531. public static void TrackedObjectOffsetTool(
  532. CinemachineComponentBase cmComponent, SerializedProperty trackedObjectOffset)
  533. {
  534. var originalColor = Handles.color;
  535. var lookAtPos = cmComponent.LookAtTargetPosition;
  536. var lookAtRot = cmComponent.LookAtTargetRotation;
  537. var trackedObjectPos = lookAtPos + lookAtRot * trackedObjectOffset.vector3Value;
  538. EditorGUI.BeginChangeCheck();
  539. #if UNITY_2022_2_OR_NEWER
  540. var tooHandleIds = Handles.PositionHandleIds.@default;
  541. var newTrackedObjectPos = Handles.PositionHandle(tooHandleIds, trackedObjectPos, lookAtRot);
  542. var tooHandleMinId = tooHandleIds.x - 1;
  543. var tooHandleMaxId = tooHandleIds.xyz + 1;
  544. #else
  545. var tooHandleMinId = GUIUtility.GetControlID(FocusType.Passive);
  546. var newTrackedObjectPos = Handles.PositionHandle(trackedObjectPos, lookAtRot);
  547. var tooHandleMaxId = GUIUtility.GetControlID(FocusType.Passive);
  548. #endif
  549. if (EditorGUI.EndChangeCheck())
  550. {
  551. trackedObjectOffset.vector3Value +=
  552. PositionHandleDelta(lookAtRot, newTrackedObjectPos, trackedObjectPos);
  553. trackedObjectOffset.serializedObject.ApplyModifiedProperties();
  554. }
  555. var trackedObjectOffsetHandleIsDragged =
  556. tooHandleMinId < GUIUtility.hotControl && GUIUtility.hotControl < tooHandleMaxId;
  557. var trackedObjectOffsetHandleIsUsedOrHovered = trackedObjectOffsetHandleIsDragged ||
  558. tooHandleMinId < HandleUtility.nearestControl && HandleUtility.nearestControl < tooHandleMaxId;
  559. if (trackedObjectOffsetHandleIsUsedOrHovered)
  560. {
  561. DrawLabel(trackedObjectPos, "(" + cmComponent.Stage + ") Tracked Object Offset "
  562. + trackedObjectOffset.vector3Value.ToString("F1"));
  563. }
  564. Handles.color = trackedObjectOffsetHandleIsUsedOrHovered ?
  565. Handles.selectedColor : HelperLineDefaultColor;
  566. Handles.DrawDottedLine(lookAtPos, trackedObjectPos, k_DottedLineSpacing);
  567. Handles.DrawLine(trackedObjectPos, cmComponent.VcamState.FinalPosition);
  568. SoloOnDrag(trackedObjectOffsetHandleIsDragged, cmComponent.VirtualCamera, tooHandleMaxId);
  569. Handles.color = originalColor;
  570. }
  571. public static void TransposerFollowOffsetTool(CinemachineTransposer cmComponent)
  572. {
  573. var originalColor = Handles.color;
  574. var brain = CinemachineCore.Instance.FindPotentialTargetBrain(cmComponent.VirtualCamera);
  575. var up = brain != null ? brain.DefaultWorldUp : Vector3.up;
  576. var camPos = cmComponent.GetTargetCameraPosition(up);
  577. var camRot = cmComponent.GetReferenceOrientation(up);
  578. EditorGUI.BeginChangeCheck();
  579. #if UNITY_2022_2_OR_NEWER
  580. var foHandleIds = Handles.PositionHandleIds.@default;
  581. var newPos = Handles.PositionHandle(foHandleIds, camPos, camRot);
  582. var foHandleMinId = foHandleIds.x - 1;
  583. var foHandleMaxId = foHandleIds.xyz + 1;
  584. #else
  585. var foHandleMinId = GUIUtility.GetControlID(FocusType.Passive);
  586. var newPos = Handles.PositionHandle(camPos, camRot);
  587. var foHandleMaxId = GUIUtility.GetControlID(FocusType.Passive);
  588. #endif
  589. if (EditorGUI.EndChangeCheck())
  590. {
  591. var so = new SerializedObject(cmComponent);
  592. var followOffset = so.FindProperty(() => cmComponent.m_FollowOffset);
  593. followOffset.vector3Value += PositionHandleDelta(camRot, newPos, camPos);
  594. so.ApplyModifiedProperties();
  595. followOffset.vector3Value = cmComponent.EffectiveOffset;
  596. so.ApplyModifiedProperties();
  597. }
  598. var followOffsetHandleIsDragged =
  599. foHandleMinId < GUIUtility.hotControl && GUIUtility.hotControl < foHandleMaxId;
  600. var followOffsetHandleIsDraggedOrHovered = followOffsetHandleIsDragged ||
  601. foHandleMinId < HandleUtility.nearestControl && HandleUtility.nearestControl < foHandleMaxId;
  602. if (followOffsetHandleIsDraggedOrHovered)
  603. {
  604. DrawLabel(camPos, "Follow offset " + cmComponent.m_FollowOffset.ToString("F1"));
  605. }
  606. Handles.color = followOffsetHandleIsDraggedOrHovered ? Handles.selectedColor : HelperLineDefaultColor;
  607. Handles.DrawDottedLine(cmComponent.FollowTargetPosition, camPos, k_DottedLineSpacing);
  608. SoloOnDrag(followOffsetHandleIsDragged, cmComponent.VirtualCamera, foHandleMaxId);
  609. Handles.color = originalColor;
  610. }
  611. /// <summary>
  612. /// Draws Orbit handles (e.g. for freelook)
  613. /// </summary>
  614. /// <returns>Index of the rig being edited, or -1 if none</returns>
  615. public static int OrbitControlHandle(
  616. CinemachineVirtualCameraBase vcam, SerializedProperty orbits)
  617. {
  618. var originalColor = Handles.color;
  619. var followPos = vcam.Follow.position;
  620. var draggedRig = -1;
  621. var minIndex = 1;
  622. for (var rigIndex = 0; rigIndex < orbits.arraySize; ++rigIndex)
  623. {
  624. var orbit = orbits.GetArrayElementAtIndex(rigIndex);
  625. var orbitHeight = orbit.FindPropertyRelative("m_Height");
  626. var orbitRadius = orbit.FindPropertyRelative("m_Radius");
  627. Handles.color = Handles.preselectionColor;
  628. EditorGUI.BeginChangeCheck();
  629. var heightHandleId = GUIUtility.GetControlID(FocusType.Passive);
  630. var heightHandlePos = followPos + Vector3.up * orbitHeight.floatValue;
  631. var newHeightHandlePos = Handles.Slider(heightHandleId, heightHandlePos, Vector3.up,
  632. CubeHandleCapSize(heightHandlePos), Handles.CubeHandleCap, 0.5f);
  633. var radiusHandleOffset = Vector3.right;
  634. var radiusHandleId = GUIUtility.GetControlID(FocusType.Passive);
  635. var radiusHandlePos = followPos + Vector3.up * orbitHeight.floatValue
  636. + radiusHandleOffset * orbitRadius.floatValue;
  637. var newRadiusHandlePos = Handles.Slider(radiusHandleId, radiusHandlePos, radiusHandleOffset,
  638. CubeHandleCapSize(radiusHandlePos), Handles.CubeHandleCap, 0.5f);
  639. if (EditorGUI.EndChangeCheck())
  640. {
  641. orbitHeight.floatValue +=
  642. SliderHandleDelta(newHeightHandlePos, heightHandlePos, Vector3.up);
  643. orbitRadius.floatValue +=
  644. SliderHandleDelta(newRadiusHandlePos, radiusHandlePos, radiusHandleOffset);
  645. orbits.serializedObject.ApplyModifiedProperties();
  646. }
  647. var isDragged = GUIUtility.hotControl == heightHandleId || GUIUtility.hotControl == radiusHandleId;
  648. Handles.color = isDragged || HandleUtility.nearestControl == heightHandleId ||
  649. HandleUtility.nearestControl == radiusHandleId ? Handles.selectedColor : HelperLineDefaultColor;
  650. if (GUIUtility.hotControl == heightHandleId || HandleUtility.nearestControl == heightHandleId)
  651. {
  652. DrawLabel(heightHandlePos, "Height: " + orbitHeight.floatValue);
  653. }
  654. if (GUIUtility.hotControl == radiusHandleId || HandleUtility.nearestControl == radiusHandleId)
  655. {
  656. DrawLabel(radiusHandlePos, "Radius: " + orbitRadius.floatValue);
  657. }
  658. Handles.DrawWireDisc(newHeightHandlePos, Vector3.up, orbitRadius.floatValue);
  659. if (isDragged)
  660. {
  661. draggedRig = rigIndex;
  662. minIndex = Mathf.Min(Mathf.Min(heightHandleId), radiusHandleId);
  663. }
  664. }
  665. SoloOnDrag(draggedRig != -1, vcam, minIndex);
  666. Handles.color = originalColor;
  667. return draggedRig;
  668. }
  669. static bool s_IsDragging;
  670. static ICinemachineCamera s_UserSolo;
  671. public static void SoloOnDrag(bool isDragged, ICinemachineCamera vcam, int handleMaxId)
  672. {
  673. if (isDragged)
  674. {
  675. if (!s_IsDragging)
  676. {
  677. s_UserSolo = CinemachineBrain.SoloCamera;
  678. s_IsDragging = true;
  679. }
  680. CinemachineBrain.SoloCamera = vcam;
  681. }
  682. else if (s_IsDragging && handleMaxId != -1) // Handles sometimes return -1 as id, ignore those frames
  683. {
  684. CinemachineBrain.SoloCamera = s_UserSolo;
  685. InspectorUtility.RepaintGameView();
  686. s_IsDragging = false;
  687. s_UserSolo = null;
  688. }
  689. }
  690. }
  691. }
  692. #endif