CinemachineScreenComposerGuides.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. using UnityEngine;
  2. using UnityEditor;
  3. using Cinemachine.Utility;
  4. #if UNITY_2019_2_OR_NEWER
  5. using UnityEngine.UIElements;
  6. #endif
  7. namespace Cinemachine.Editor
  8. {
  9. [InitializeOnLoad]
  10. static class CinemachineScreenComposerGuidesGlobalDraggable
  11. {
  12. static CinemachineScreenComposerGuidesGlobalDraggable()
  13. {
  14. CinemachineScreenComposerGuides.sDraggableGameWindowGuides = Enabled;
  15. }
  16. public static string kEnabledKey = "DraggableScreenComposerGuides_Enabled";
  17. public static bool Enabled
  18. {
  19. get => EditorPrefs.GetBool(kEnabledKey, true);
  20. set
  21. {
  22. if (value != CinemachineScreenComposerGuides.sDraggableGameWindowGuides)
  23. {
  24. EditorPrefs.SetBool(kEnabledKey, value);
  25. CinemachineScreenComposerGuides.sDraggableGameWindowGuides = value;
  26. }
  27. }
  28. }
  29. }
  30. #if !UNITY_2019_2_OR_NEWER
  31. internal class GameViewEventCatcher
  32. {
  33. public void OnEnable() {}
  34. public void OnDisable() {}
  35. }
  36. #else
  37. // This is necessary because in 2019.3 we don't get mouse events in the game view in Edit mode
  38. internal class GameViewEventCatcher
  39. {
  40. class Dragger
  41. {
  42. bool mActive;
  43. VisualElement mRoot;
  44. void OnMouseDown(MouseDownEvent e) { if (mRoot.panel != null) mActive = true; }
  45. void OnMouseUp(MouseUpEvent e) { mActive = false; }
  46. void OnMouseMove(MouseMoveEvent e)
  47. {
  48. if (mActive && mRoot.panel != null)
  49. {
  50. if (!Application.isPlaying
  51. && CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides
  52. && CinemachineBrain.SoloCamera == null)
  53. {
  54. InspectorUtility.RepaintGameView();
  55. }
  56. }
  57. }
  58. public Dragger(VisualElement root)
  59. {
  60. mRoot = root;
  61. if (mRoot == null || mRoot.panel == null || mRoot.panel.visualTree == null)
  62. return;
  63. mRoot.panel.visualTree.RegisterCallback<MouseDownEvent>(OnMouseDown, TrickleDown.TrickleDown);
  64. mRoot.panel.visualTree.RegisterCallback<MouseUpEvent>(OnMouseUp, TrickleDown.TrickleDown);
  65. mRoot.panel.visualTree.RegisterCallback<MouseMoveEvent>(OnMouseMove, TrickleDown.TrickleDown);
  66. }
  67. public void Unregister()
  68. {
  69. if (mRoot == null || mRoot.panel == null || mRoot.panel.visualTree == null)
  70. return;
  71. mRoot.panel.visualTree.UnregisterCallback<MouseDownEvent>(OnMouseDown, TrickleDown.TrickleDown);
  72. mRoot.panel.visualTree.UnregisterCallback<MouseUpEvent>(OnMouseUp, TrickleDown.TrickleDown);
  73. mRoot.panel.visualTree.UnregisterCallback<MouseMoveEvent>(OnMouseMove, TrickleDown.TrickleDown);
  74. }
  75. }
  76. Dragger[] mDraggers;
  77. // Create manipulator in each game view
  78. public void OnEnable()
  79. {
  80. System.Reflection.Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly;
  81. System.Type type = assembly.GetType( "UnityEditor.GameView" );
  82. var gameViews = UnityEngine.Resources.FindObjectsOfTypeAll(type);
  83. mDraggers = new Dragger[gameViews.Length];
  84. for (int i = 0; i < gameViews.Length; ++i)
  85. {
  86. var gameViewRoot = (gameViews[i] as UnityEditor.EditorWindow).rootVisualElement;
  87. mDraggers[i] = new Dragger(gameViewRoot);
  88. }
  89. }
  90. public void OnDisable()
  91. {
  92. for (int i = 0; mDraggers != null && i < mDraggers.Length; ++i)
  93. {
  94. var dragger = mDraggers[i];
  95. if (dragger != null)
  96. dragger.Unregister();
  97. }
  98. mDraggers = null;
  99. }
  100. }
  101. #endif
  102. /// <summary>
  103. /// Use an instance of this class to draw screen composer guides in the game view.
  104. /// This is an internal class, and is not meant to be called outside of Cinemachine.
  105. /// </summary>
  106. public class CinemachineScreenComposerGuides
  107. {
  108. /// <summary>Delegate for getting the hard/soft guide rects</summary>
  109. /// <returns>The Hard/Soft guide rect</returns>
  110. public delegate Rect RectGetter();
  111. /// <summary>Delegate for setting the hard/soft guide rects</summary>
  112. /// <param name="rcam">The value to set</param>
  113. public delegate void RectSetter(Rect r);
  114. /// <summary>Delegate to get the current object whose guides are being drawn</summary>
  115. /// <returns>The target object whose guides are being drawn</returns>
  116. public delegate SerializedObject ObjectGetter();
  117. /// <summary>Get the Hard Guide. Client must implement this</summary>
  118. public RectGetter GetHardGuide;
  119. /// <summary>Get the Soft Guide. Client must implement this</summary>
  120. public RectGetter GetSoftGuide;
  121. /// <summary>Set the Hard Guide. Client must implement this</summary>
  122. public RectSetter SetHardGuide;
  123. /// <summary>Get the Soft Guide. Client must implement this</summary>
  124. public RectSetter SetSoftGuide;
  125. /// <summary>Get the target object whose guides are being drawn. Client must implement this</summary>
  126. public ObjectGetter Target;
  127. /// <summary>Width of the draggable guide bar in the game view</summary>
  128. public const float kGuideBarWidthPx = 3f;
  129. /// <summary>If true, then allows game window guides to be edited in play mode.</summary>
  130. public static bool sDraggableGameWindowGuides = true;
  131. /// <summary>
  132. /// Helper to set the appropriate new rects in the target object, is something changed.
  133. /// </summary>
  134. /// <param name="oldHard">Current hard guide</param>
  135. /// <param name="oldSoft">Current soft guide</param>
  136. /// <param name="newHard">New hard guide</param>
  137. /// <param name="newSoft">New soft guide</param>
  138. public void SetNewBounds(Rect oldHard, Rect oldSoft, Rect newHard, Rect newSoft)
  139. {
  140. if ((oldSoft != newSoft) || (oldHard != newHard))
  141. {
  142. Undo.RecordObject(Target().targetObject, "Composer Bounds");
  143. if (oldSoft != newSoft)
  144. SetSoftGuide(newSoft);
  145. if (oldHard != newHard)
  146. SetHardGuide(newHard);
  147. Target().ApplyModifiedProperties();
  148. }
  149. }
  150. Rect GetCameraRect(Camera outputCamera, LensSettings lens)
  151. {
  152. Rect cameraRect = outputCamera.pixelRect;
  153. float screenHeight = cameraRect.height;
  154. float screenWidth = cameraRect.width;
  155. float screenAspect = screenWidth / screenHeight;
  156. switch (outputCamera.gateFit)
  157. {
  158. case Camera.GateFitMode.Vertical:
  159. screenWidth = screenHeight * lens.Aspect;
  160. cameraRect.position += new Vector2((cameraRect.width - screenWidth) * 0.5f, 0);
  161. break;
  162. case Camera.GateFitMode.Horizontal:
  163. screenHeight = screenWidth / lens.Aspect;
  164. cameraRect.position += new Vector2(0, (cameraRect.height - screenHeight) * 0.5f);
  165. break;
  166. case Camera.GateFitMode.Overscan:
  167. if (screenAspect < lens.Aspect)
  168. {
  169. screenHeight = screenWidth / lens.Aspect;
  170. cameraRect.position += new Vector2(0, (cameraRect.height - screenHeight) * 0.5f);
  171. }
  172. else
  173. {
  174. screenWidth = screenHeight * lens.Aspect;
  175. cameraRect.position += new Vector2((cameraRect.width - screenWidth) * 0.5f, 0);
  176. }
  177. break;
  178. case Camera.GateFitMode.Fill:
  179. if (screenAspect > lens.Aspect)
  180. {
  181. screenHeight = screenWidth / lens.Aspect;
  182. cameraRect.position += new Vector2(0, (cameraRect.height - screenHeight) * 0.5f);
  183. }
  184. else
  185. {
  186. screenWidth = screenHeight * lens.Aspect;
  187. cameraRect.position += new Vector2((cameraRect.width - screenWidth) * 0.5f, 0);
  188. }
  189. break;
  190. case Camera.GateFitMode.None:
  191. break;
  192. }
  193. cameraRect = new Rect(cameraRect.position, new Vector2(screenWidth, screenHeight));
  194. // Invert Y
  195. float h = cameraRect.height;
  196. cameraRect.yMax = Screen.height - cameraRect.yMin;
  197. cameraRect.yMin = cameraRect.yMax - h;
  198. // Shift the guides along with the lens
  199. cameraRect.position += new Vector2(
  200. -screenWidth * lens.LensShift.x, screenHeight * lens.LensShift.y);
  201. return cameraRect;
  202. }
  203. /// <summary>
  204. /// Call this from the inspector's OnGUI. Draws the guides and manages dragging.
  205. /// </summary>
  206. /// <param name="isLive">Is the target live</param>
  207. /// <param name="outputCamera">Destination camera</param>
  208. /// <param name="lens">Current lens settings</param>
  209. /// <param name="showHardGuides">True if hard guides should be shown</param>
  210. public void OnGUI_DrawGuides(bool isLive, Camera outputCamera, LensSettings lens, bool showHardGuides)
  211. {
  212. Rect cameraRect = GetCameraRect(outputCamera, lens);
  213. float screenWidth = cameraRect.width;
  214. float screenHeight = cameraRect.height;
  215. // Rotate the guides along with the dutch
  216. Matrix4x4 oldMatrix = GUI.matrix;
  217. GUI.matrix = Matrix4x4.Translate(cameraRect.min);
  218. GUIUtility.RotateAroundPivot(lens.Dutch, cameraRect.center);
  219. Color hardBarsColour = CinemachineSettings.ComposerSettings.HardBoundsOverlayColour;
  220. Color softBarsColour = CinemachineSettings.ComposerSettings.SoftBoundsOverlayColour;
  221. float overlayOpacity = CinemachineSettings.ComposerSettings.OverlayOpacity;
  222. if (!isLive)
  223. {
  224. softBarsColour = CinemachineSettings.CinemachineCoreSettings.InactiveGizmoColour;
  225. hardBarsColour = Color.Lerp(softBarsColour, Color.black, 0.5f);
  226. overlayOpacity /= 2;
  227. }
  228. hardBarsColour.a *= overlayOpacity;
  229. softBarsColour.a *= overlayOpacity;
  230. Rect r = showHardGuides ? GetHardGuide() : new Rect(-2, -2, 4, 4);
  231. float hardEdgeLeft = r.xMin * screenWidth;
  232. float hardEdgeTop = r.yMin * screenHeight;
  233. float hardEdgeRight = r.xMax * screenWidth;
  234. float hardEdgeBottom = r.yMax * screenHeight;
  235. mDragBars[(int)DragBar.HardBarLineLeft] = new Rect(hardEdgeLeft - kGuideBarWidthPx / 2f, 0f, kGuideBarWidthPx, screenHeight);
  236. mDragBars[(int)DragBar.HardBarLineTop] = new Rect(0f, hardEdgeTop - kGuideBarWidthPx / 2f, screenWidth, kGuideBarWidthPx);
  237. mDragBars[(int)DragBar.HardBarLineRight] = new Rect(hardEdgeRight - kGuideBarWidthPx / 2f, 0f, kGuideBarWidthPx, screenHeight);
  238. mDragBars[(int)DragBar.HardBarLineBottom] = new Rect(0f, hardEdgeBottom - kGuideBarWidthPx / 2f, screenWidth, kGuideBarWidthPx);
  239. r = GetSoftGuide();
  240. float softEdgeLeft = r.xMin * screenWidth;
  241. float softEdgeTop = r.yMin * screenHeight;
  242. float softEdgeRight = r.xMax * screenWidth;
  243. float softEdgeBottom = r.yMax * screenHeight;
  244. mDragBars[(int)DragBar.SoftBarLineLeft] = new Rect(softEdgeLeft - kGuideBarWidthPx / 2f, 0f, kGuideBarWidthPx, screenHeight);
  245. mDragBars[(int)DragBar.SoftBarLineTop] = new Rect(0f, softEdgeTop - kGuideBarWidthPx / 2f, screenWidth, kGuideBarWidthPx);
  246. mDragBars[(int)DragBar.SoftBarLineRight] = new Rect(softEdgeRight - kGuideBarWidthPx / 2f, 0f, kGuideBarWidthPx, screenHeight);
  247. mDragBars[(int)DragBar.SoftBarLineBottom] = new Rect(0f, softEdgeBottom - kGuideBarWidthPx / 2f, screenWidth, kGuideBarWidthPx);
  248. mDragBars[(int)DragBar.Center] = new Rect(softEdgeLeft, softEdgeTop, softEdgeRight - softEdgeLeft, softEdgeBottom - softEdgeTop);
  249. // Handle dragging bars
  250. if (sDraggableGameWindowGuides && isLive)
  251. OnGuiHandleBarDragging(screenWidth, screenHeight);
  252. // Draw the masks
  253. GUI.color = hardBarsColour;
  254. Rect hardBarLeft = new Rect(0, hardEdgeTop, Mathf.Max(0, hardEdgeLeft), hardEdgeBottom - hardEdgeTop);
  255. Rect hardBarRight = new Rect(hardEdgeRight, hardEdgeTop,
  256. Mathf.Max(0, screenWidth - hardEdgeRight), hardEdgeBottom - hardEdgeTop);
  257. Rect hardBarTop = new Rect(Mathf.Min(0, hardEdgeLeft), 0,
  258. Mathf.Max(screenWidth, hardEdgeRight) - Mathf.Min(0, hardEdgeLeft), Mathf.Max(0, hardEdgeTop));
  259. Rect hardBarBottom = new Rect(Mathf.Min(0, hardEdgeLeft), hardEdgeBottom,
  260. Mathf.Max(screenWidth, hardEdgeRight) - Mathf.Min(0, hardEdgeLeft),
  261. Mathf.Max(0, screenHeight - hardEdgeBottom));
  262. GUI.DrawTexture(hardBarLeft, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  263. GUI.DrawTexture(hardBarTop, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  264. GUI.DrawTexture(hardBarRight, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  265. GUI.DrawTexture(hardBarBottom, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  266. GUI.color = softBarsColour;
  267. Rect softBarLeft = new Rect(hardEdgeLeft, softEdgeTop, softEdgeLeft - hardEdgeLeft, softEdgeBottom - softEdgeTop);
  268. Rect softBarTop = new Rect(hardEdgeLeft, hardEdgeTop, hardEdgeRight - hardEdgeLeft, softEdgeTop - hardEdgeTop);
  269. Rect softBarRight = new Rect(softEdgeRight, softEdgeTop, hardEdgeRight - softEdgeRight, softEdgeBottom - softEdgeTop);
  270. Rect softBarBottom = new Rect(hardEdgeLeft, softEdgeBottom, hardEdgeRight - hardEdgeLeft, hardEdgeBottom - softEdgeBottom);
  271. GUI.DrawTexture(softBarLeft, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  272. GUI.DrawTexture(softBarTop, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  273. GUI.DrawTexture(softBarRight, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  274. GUI.DrawTexture(softBarBottom, Texture2D.whiteTexture, ScaleMode.StretchToFill);
  275. // Draw the drag bars
  276. GUI.DrawTexture(mDragBars[(int)DragBar.SoftBarLineLeft], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  277. GUI.DrawTexture(mDragBars[(int)DragBar.SoftBarLineTop], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  278. GUI.DrawTexture(mDragBars[(int)DragBar.SoftBarLineRight], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  279. GUI.DrawTexture(mDragBars[(int)DragBar.SoftBarLineBottom], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  280. GUI.color = hardBarsColour;
  281. GUI.DrawTexture(mDragBars[(int)DragBar.HardBarLineLeft], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  282. GUI.DrawTexture(mDragBars[(int)DragBar.HardBarLineTop], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  283. GUI.DrawTexture(mDragBars[(int)DragBar.HardBarLineRight], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  284. GUI.DrawTexture(mDragBars[(int)DragBar.HardBarLineBottom], Texture2D.whiteTexture, ScaleMode.StretchToFill);
  285. GUI.matrix = oldMatrix;
  286. }
  287. // For dragging the bars - order defines precedence
  288. private enum DragBar
  289. {
  290. Center,
  291. SoftBarLineLeft, SoftBarLineTop, SoftBarLineRight, SoftBarLineBottom,
  292. HardBarLineLeft, HardBarLineTop, HardBarLineRight, HardBarLineBottom,
  293. NONE
  294. };
  295. private DragBar mDragging = DragBar.NONE;
  296. private Rect[] mDragBars = new Rect[9];
  297. private void OnGuiHandleBarDragging(float screenWidth, float screenHeight)
  298. {
  299. if (Event.current.type == EventType.MouseUp)
  300. mDragging = DragBar.NONE;
  301. if (Event.current.type == EventType.MouseDown)
  302. {
  303. mDragging = DragBar.NONE;
  304. for (DragBar i = DragBar.Center; i < DragBar.NONE && mDragging == DragBar.NONE; ++i)
  305. {
  306. Vector2 slop = new Vector2(5f, 5f);
  307. if (i == DragBar.Center)
  308. {
  309. if (mDragBars[(int)i].width > 3f * slop.x)
  310. slop.x = -slop.x;
  311. if (mDragBars[(int)i].height > 3f * slop.y)
  312. slop.y = -slop.y;
  313. }
  314. Rect r = mDragBars[(int)i].Inflated(slop);
  315. if (r.Contains(Event.current.mousePosition))
  316. mDragging = i;
  317. }
  318. }
  319. if (mDragging != DragBar.NONE && Event.current.type == EventType.MouseDrag)
  320. {
  321. Vector2 d = new Vector2(
  322. Event.current.delta.x / screenWidth,
  323. Event.current.delta.y / screenHeight);
  324. // First snapshot some settings
  325. Rect newHard = GetHardGuide();
  326. Rect newSoft = GetSoftGuide();
  327. Vector2 changed = Vector2.zero;
  328. switch (mDragging)
  329. {
  330. case DragBar.Center: newSoft.position += d; break;
  331. case DragBar.SoftBarLineLeft: newSoft = newSoft.Inflated(new Vector2(-d.x, 0)); break;
  332. case DragBar.SoftBarLineRight: newSoft = newSoft.Inflated(new Vector2(d.x, 0)); break;
  333. case DragBar.SoftBarLineTop: newSoft = newSoft.Inflated(new Vector2(0, -d.y)); break;
  334. case DragBar.SoftBarLineBottom: newSoft = newSoft.Inflated(new Vector2(0, d.y)); break;
  335. case DragBar.HardBarLineLeft: newHard = newHard.Inflated(new Vector2(-d.x, 0)); break;
  336. case DragBar.HardBarLineRight: newHard = newHard.Inflated(new Vector2(d.x, 0)); break;
  337. case DragBar.HardBarLineBottom: newHard = newHard.Inflated(new Vector2(0, d.y)); break;
  338. case DragBar.HardBarLineTop: newHard = newHard.Inflated(new Vector2(0, -d.y)); break;
  339. }
  340. // Apply the changes, enforcing the bounds
  341. SetNewBounds(GetHardGuide(), GetSoftGuide(), newHard, newSoft);
  342. Event.current.Use();
  343. }
  344. }
  345. }
  346. }