GameWindow.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using bs;
  4. namespace bs.Editor
  5. {
  6. /** @addtogroup Windows
  7. * @{
  8. */
  9. /// <summary>
  10. /// Displays in-game viewport in the editor.
  11. /// </summary>
  12. public class GameWindow : EditorWindow
  13. {
  14. private const int HeaderHeight = 20;
  15. private static readonly Color BG_COLOR = Color.VeryDarkGray;
  16. private readonly AspectRatio[] aspectRatios =
  17. {
  18. new AspectRatio(16, 9),
  19. new AspectRatio(16, 10),
  20. new AspectRatio(5, 4),
  21. new AspectRatio(4, 3),
  22. new AspectRatio(3, 2)
  23. };
  24. private int selectedAspectRatio = 0;
  25. private GUIRenderTexture renderTextureGUI;
  26. private GUITexture renderTextureBg;
  27. private GUILabel noCameraLabel;
  28. private Camera currentCamera;
  29. private bool onDemandDisabled;
  30. /// <summary>
  31. /// Opens the game window.
  32. /// </summary>
  33. [MenuItem("Windows/Game", ButtonModifier.CtrlAlt, ButtonCode.G, 6000)]
  34. private static void OpenGameWindow()
  35. {
  36. OpenWindow<GameWindow>();
  37. }
  38. /// <summary>
  39. /// Starts execution of the game in the game window.
  40. /// </summary>
  41. [MenuItem("Tools/Play", 9300)]
  42. [ToolbarItem("Play", ToolbarIcon.Play, "Play", 1800, true)]
  43. private static void Play()
  44. {
  45. GameWindow gameWindow = GetWindow<GameWindow>();
  46. SceneWindow sceneWindow = GetWindow<SceneWindow>();
  47. PlayInEditorState state = PlayInEditor.State;
  48. if (state != PlayInEditorState.Playing)
  49. {
  50. gameWindow.Active = true;
  51. gameWindow.HasFocus = true;
  52. }
  53. else
  54. {
  55. sceneWindow.Active = true;
  56. sceneWindow.HasFocus = true;
  57. }
  58. if (state == PlayInEditorState.Paused)
  59. PlayInEditor.State = PlayInEditorState.Playing;
  60. else
  61. {
  62. if(state == PlayInEditorState.Playing)
  63. PlayInEditor.State = PlayInEditorState.Stopped;
  64. else
  65. PlayInEditor.State = PlayInEditorState.Playing;
  66. }
  67. }
  68. /// <summary>
  69. /// Pauses the execution of the game on the current frame.
  70. /// </summary>
  71. [MenuItem("Tools/Pause", 9299)]
  72. [ToolbarItem("Pause", ToolbarIcon.Pause, "Pause", 1799)]
  73. private static void Pause()
  74. {
  75. if (PlayInEditor.State == PlayInEditorState.Paused)
  76. PlayInEditor.State = PlayInEditorState.Playing;
  77. else
  78. PlayInEditor.State = PlayInEditorState.Paused;
  79. }
  80. /// <summary>
  81. /// Moves the execution of the game by one frame forward.
  82. /// </summary>
  83. [MenuItem("Tools/Step", 9298)]
  84. [ToolbarItem("Step", ToolbarIcon.Step, "Frame step", 1798)]
  85. private static void Step()
  86. {
  87. PlayInEditor.FrameStep();
  88. }
  89. /// <inheritdoc/>
  90. protected override LocString GetDisplayName()
  91. {
  92. return new LocEdString("Game");
  93. }
  94. private void OnInitialize()
  95. {
  96. GUILayoutY mainLayout = GUI.AddLayoutY();
  97. string[] aspectRatioTitles = new string[aspectRatios.Length + 1];
  98. aspectRatioTitles[0] = "Free";
  99. for (int i = 0; i < aspectRatios.Length; i++)
  100. aspectRatioTitles[i + 1] = aspectRatios[i].width + ":" + aspectRatios[i].height;
  101. GUIListBoxField aspectField = new GUIListBoxField(aspectRatioTitles, new LocEdString("Aspect ratio"));
  102. aspectField.OnSelectionChanged += OnAspectRatioChanged;
  103. GUILayoutY buttonLayoutVert = mainLayout.AddLayoutY();
  104. GUILayoutX buttonLayout = buttonLayoutVert.AddLayoutX();
  105. buttonLayout.AddElement(aspectField);
  106. buttonLayout.AddFlexibleSpace();
  107. buttonLayoutVert.AddFlexibleSpace();
  108. renderTextureGUI = new GUIRenderTexture(null);
  109. renderTextureBg = new GUITexture(Builtin.WhiteTexture);
  110. renderTextureBg.SetTint(BG_COLOR);
  111. noCameraLabel = new GUILabel(new LocEdString("(No main camera in scene)"));
  112. GUIPanel rtPanel = mainLayout.AddPanel();
  113. rtPanel.AddElement(renderTextureGUI);
  114. GUIPanel bgPanel = rtPanel.AddPanel(1);
  115. bgPanel.AddElement(renderTextureBg);
  116. GUILayoutY alignLayoutY = rtPanel.AddLayoutY();
  117. alignLayoutY.AddFlexibleSpace();
  118. GUILayoutX alignLayoutX = alignLayoutY.AddLayoutX();
  119. alignLayoutX.AddFlexibleSpace();
  120. alignLayoutX.AddElement(noCameraLabel);
  121. alignLayoutX.AddFlexibleSpace();
  122. alignLayoutY.AddFlexibleSpace();
  123. UpdateRenderTexture(Width, Height);
  124. currentCamera = Scene.Camera;
  125. bool hasMainCamera = currentCamera != null;
  126. renderTextureGUI.Active = hasMainCamera;
  127. noCameraLabel.Active = !hasMainCamera;
  128. ToggleOnDemandDrawing(EditorApplication.IsOnDemandDrawingEnabled());
  129. NotifyNeedsRedraw();
  130. }
  131. private void OnEditorUpdate()
  132. {
  133. Camera camera = Scene.Camera;
  134. if (camera != currentCamera)
  135. {
  136. if (currentCamera != null)
  137. currentCamera.Flags &= ~CameraFlag.OnDemand;
  138. if(!onDemandDisabled)
  139. camera.Flags |= CameraFlag.OnDemand;
  140. currentCamera = camera;
  141. }
  142. bool hasMainCamera = camera != null;
  143. renderTextureGUI.Active = hasMainCamera;
  144. noCameraLabel.Active = !hasMainCamera;
  145. }
  146. private void OnDestroy()
  147. {
  148. if (currentCamera != null)
  149. currentCamera.Flags &= ~CameraFlag.OnDemand;
  150. EditorApplication.MainRenderTarget = null;
  151. }
  152. /// <summary>
  153. /// Notifies the system that the 3D viewport should be redrawn.
  154. /// </summary>
  155. internal void NotifyNeedsRedraw()
  156. {
  157. Camera camera = Scene.Camera;
  158. camera?.NotifyNeedsRedraw();
  159. }
  160. /// <summary>
  161. /// Enables or disables on-demand drawing. When enabled the 3D viewport will only be redrawn when
  162. /// <see cref="NotifyNeedsRedraw"/> is called. If disabled the viewport will be redrawn every frame.
  163. /// Normally you always want to keep this disabled unless you know the viewport will require updates
  164. /// every frame (e.g. when a game is running, or when previewing animations).
  165. /// </summary>
  166. /// <param name="enabled">True to enable on-demand drawing, false otherwise.</param>
  167. internal void ToggleOnDemandDrawing(bool enabled)
  168. {
  169. onDemandDisabled = !enabled;
  170. if (currentCamera == null)
  171. return;
  172. if (enabled)
  173. currentCamera.Flags |= CameraFlag.OnDemand;
  174. else
  175. currentCamera.Flags &= ~CameraFlag.OnDemand;
  176. }
  177. /// <summary>
  178. /// Creates or rebuilds the main render texture. Should be called at least once before using the
  179. /// game window. Should be called whenever the window is resized.
  180. /// </summary>
  181. /// <param name="width">Width of the scene render target, in pixels.</param>
  182. /// <param name="height">Height of the scene render target, in pixels.</param>
  183. private void UpdateRenderTexture(int width, int height)
  184. {
  185. height = height - HeaderHeight;
  186. int rtWidth = MathEx.Max(20, width);
  187. int rtHeight = MathEx.Max(20, height);
  188. if (selectedAspectRatio != 0) // 0 is free aspect
  189. {
  190. AspectRatio aspectRatio = aspectRatios[selectedAspectRatio - 1];
  191. int visibleAreaHeight = rtHeight;
  192. float aspectInv = aspectRatio.height/(float)aspectRatio.width;
  193. rtHeight = MathEx.RoundToInt(rtWidth*aspectInv);
  194. if (rtHeight > visibleAreaHeight)
  195. {
  196. rtHeight = visibleAreaHeight;
  197. float aspect = aspectRatio.width / (float)aspectRatio.height;
  198. rtWidth = MathEx.RoundToInt(rtHeight * aspect);
  199. }
  200. }
  201. RenderTexture renderTexture = new RenderTexture(PixelFormat.RGBA8, rtWidth, rtHeight) { Priority = 1};
  202. EditorApplication.MainRenderTarget = renderTexture;
  203. renderTextureGUI.RenderTexture = renderTexture;
  204. int offsetX = (width - rtWidth)/2;
  205. int offsetY = (height - rtHeight)/2;
  206. Rect2I rtBounds = new Rect2I(offsetX, offsetY, rtWidth, rtHeight);
  207. renderTextureGUI.Bounds = rtBounds;
  208. Rect2I bgBounds = new Rect2I(0, 0, width, height);
  209. renderTextureBg.Bounds = bgBounds;
  210. NotifyNeedsRedraw();
  211. }
  212. /// <summary>
  213. /// Triggered when the user selects a new aspect ratio from the drop down box.
  214. /// </summary>
  215. /// <param name="idx">Index of the aspect ratio the user selected.</param>
  216. private void OnAspectRatioChanged(int idx)
  217. {
  218. selectedAspectRatio = idx;
  219. UpdateRenderTexture(Width, Height);
  220. }
  221. /// <inheritdoc/>
  222. protected override void WindowResized(int width, int height)
  223. {
  224. UpdateRenderTexture(width, height);
  225. base.WindowResized(width, height);
  226. }
  227. /// <summary>
  228. /// Camera aspect ratio as numerator and denominator.
  229. /// </summary>
  230. struct AspectRatio
  231. {
  232. /// <summary>
  233. /// Creates a new object that holds the aspect ratio.
  234. /// </summary>
  235. /// <param name="width">Numerator of the aspect ratio.</param>
  236. /// <param name="height">Denominator of the aspect ratio.</param>
  237. public AspectRatio(int width, int height)
  238. {
  239. this.width = width;
  240. this.height = height;
  241. }
  242. public int width;
  243. public int height;
  244. }
  245. }
  246. /** @} */
  247. }