SceneWindow.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using BansheeEngine;
  7. namespace BansheeEditor
  8. {
  9. /// <summary>
  10. /// Displays the scene view camera and various scene controls.
  11. /// </summary>
  12. internal sealed class SceneWindow : EditorWindow
  13. {
  14. internal const string ToggleProfilerOverlayBinding = "ToggleProfilerOverlay";
  15. internal const string ViewToolBinding = "ViewTool";
  16. internal const string MoveToolBinding = "MoveTool";
  17. internal const string RotateToolBinding = "RotateTool";
  18. internal const string ScaleToolBinding = "ScaleTool";
  19. internal const string DuplicateBinding = "Duplicate";
  20. internal const string DeleteBinding = "Delete";
  21. internal const string FrameBinding = "SceneFrame";
  22. private const int HeaderHeight = 20;
  23. private const float DefaultPlacementDepth = 5.0f;
  24. private static readonly Color ClearColor = new Color(83.0f/255.0f, 83.0f/255.0f, 83.0f/255.0f);
  25. private const string ProfilerOverlayActiveKey = "_Internal_ProfilerOverlayActive";
  26. private const int HandleAxesGUISize = 50;
  27. private const int HandleAxesGUIPaddingX = 10;
  28. private const int HandleAxesGUIPaddingY = 5;
  29. private Camera camera;
  30. private SceneCamera cameraController;
  31. private RenderTexture2D renderTexture;
  32. private GUILayoutY mainLayout;
  33. private GUIPanel rtPanel;
  34. private GUIButton focusCatcher;
  35. private GUIRenderTexture renderTextureGUI;
  36. private SceneGrid sceneGrid;
  37. private SceneSelection sceneSelection;
  38. private SceneGizmos sceneGizmos;
  39. private SceneHandles sceneHandles;
  40. private GUIToggle viewButton;
  41. private GUIToggle moveButton;
  42. private GUIToggle rotateButton;
  43. private GUIToggle scaleButton;
  44. private GUIToggle localCoordButton;
  45. private GUIToggle worldCoordButton;
  46. private GUIToggle pivotButton;
  47. private GUIToggle centerButton;
  48. private GUIToggle moveSnapButton;
  49. private GUIFloatField moveSnapInput;
  50. private GUIToggle rotateSnapButton;
  51. private GUIFloatField rotateSnapInput;
  52. private SceneAxesGUI sceneAxesGUI;
  53. private bool hasContentFocus = false;
  54. private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
  55. private int editorSettingsHash = int.MaxValue;
  56. private VirtualButton duplicateKey;
  57. private VirtualButton deleteKey;
  58. private VirtualButton frameKey;
  59. // Tool shortcuts
  60. private VirtualButton viewToolKey;
  61. private VirtualButton moveToolKey;
  62. private VirtualButton rotateToolKey;
  63. private VirtualButton scaleToolKey;
  64. // Profiler overlay
  65. private ProfilerOverlay activeProfilerOverlay;
  66. private Camera profilerCamera;
  67. private VirtualButton toggleProfilerOverlayKey;
  68. // Drag & drop
  69. private bool dragActive;
  70. private SceneObject draggedSO;
  71. /// <summary>
  72. /// Returns the scene camera.
  73. /// </summary>
  74. public Camera Camera
  75. {
  76. get { return camera; }
  77. }
  78. /// <summary>
  79. /// Determines scene camera's projection type.
  80. /// </summary>
  81. internal ProjectionType ProjectionType
  82. {
  83. get { return cameraController.ProjectionType; }
  84. set { cameraController.ProjectionType = value; sceneAxesGUI.ProjectionType = value; }
  85. }
  86. /// <summary>
  87. /// Constructs a new scene window.
  88. /// </summary>
  89. internal SceneWindow()
  90. { }
  91. /// <summary>
  92. /// Opens a scene window if its not open already.
  93. /// </summary>
  94. [MenuItem("Windows/Scene", ButtonModifier.CtrlAlt, ButtonCode.S, 6000)]
  95. private static void OpenSceneWindow()
  96. {
  97. OpenWindow<SceneWindow>();
  98. }
  99. /// <summary>
  100. /// Focuses on the currently selected object.
  101. /// </summary>
  102. [MenuItem("Tools/Frame Selected", ButtonModifier.None, ButtonCode.F, 9275, true)]
  103. private static void OpenSettingsWindow()
  104. {
  105. SceneWindow window = GetWindow<SceneWindow>();
  106. if (window != null)
  107. window.cameraController.FrameSelected();
  108. }
  109. /// <summary>
  110. /// Switches the active tool to the view tool.
  111. /// </summary>
  112. [MenuItem("Tools/View", ButtonModifier.Ctrl, ButtonCode.Q, 9274, true)]
  113. private static void SetViewTool()
  114. {
  115. SceneWindow window = GetWindow<SceneWindow>();
  116. if (window != null)
  117. window.OnSceneToolButtonClicked(SceneViewTool.View);
  118. }
  119. /// <summary>
  120. /// Switches the active tool to the move tool.
  121. /// </summary>
  122. [MenuItem("Tools/Move", ButtonModifier.Ctrl, ButtonCode.W, 9273)]
  123. private static void SetMoveTool()
  124. {
  125. SceneWindow window = GetWindow<SceneWindow>();
  126. if (window != null)
  127. window.OnSceneToolButtonClicked(SceneViewTool.Move);
  128. }
  129. /// <summary>
  130. /// Switches the active tool to the rotate tool.
  131. /// </summary>
  132. [MenuItem("Tools/Rotate", ButtonModifier.Ctrl, ButtonCode.E, 9272)]
  133. private static void SetRotateTool()
  134. {
  135. SceneWindow window = GetWindow<SceneWindow>();
  136. if (window != null)
  137. window.OnSceneToolButtonClicked(SceneViewTool.Rotate);
  138. }
  139. /// <summary>
  140. /// Switches the active tool to the scale tool.
  141. /// </summary>
  142. [MenuItem("Tools/Scale", ButtonModifier.Ctrl, ButtonCode.R, 9271)]
  143. private static void SetScaleTool()
  144. {
  145. SceneWindow window = GetWindow<SceneWindow>();
  146. if (window != null)
  147. window.OnSceneToolButtonClicked(SceneViewTool.Scale);
  148. }
  149. /// <inheritdoc/>
  150. protected override LocString GetDisplayName()
  151. {
  152. return new LocEdString("Scene");
  153. }
  154. private void OnInitialize()
  155. {
  156. mainLayout = GUI.AddLayoutY();
  157. GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View),
  158. new LocEdString("View"));
  159. GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move),
  160. new LocEdString("Move"));
  161. GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate),
  162. new LocEdString("Rotate"));
  163. GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale),
  164. new LocEdString("Scale"));
  165. GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local),
  166. new LocEdString("Local"));
  167. GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World),
  168. new LocEdString("World"));
  169. GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot),
  170. new LocEdString("Pivot"));
  171. GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center),
  172. new LocEdString("Center"));
  173. GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap),
  174. new LocEdString("Move snap"));
  175. GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap),
  176. new LocEdString("Rotate snap"));
  177. GUIToggleGroup handlesTG = new GUIToggleGroup();
  178. viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  179. moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  180. rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  181. scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  182. GUIToggleGroup coordModeTG = new GUIToggleGroup();
  183. localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
  184. worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
  185. GUIToggleGroup pivotModeTG = new GUIToggleGroup();
  186. pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  187. centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  188. moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  189. moveSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
  190. rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  191. rotateSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
  192. viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
  193. moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
  194. rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
  195. scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
  196. localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
  197. worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
  198. pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
  199. centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
  200. moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
  201. moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
  202. rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
  203. rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
  204. GUILayout handlesLayout = mainLayout.AddLayoutX();
  205. handlesLayout.AddElement(viewButton);
  206. handlesLayout.AddElement(moveButton);
  207. handlesLayout.AddElement(rotateButton);
  208. handlesLayout.AddElement(scaleButton);
  209. handlesLayout.AddSpace(10);
  210. handlesLayout.AddElement(localCoordButton);
  211. handlesLayout.AddElement(worldCoordButton);
  212. handlesLayout.AddSpace(10);
  213. handlesLayout.AddElement(pivotButton);
  214. handlesLayout.AddElement(centerButton);
  215. handlesLayout.AddFlexibleSpace();
  216. handlesLayout.AddElement(moveSnapButton);
  217. handlesLayout.AddElement(moveSnapInput);
  218. handlesLayout.AddSpace(10);
  219. handlesLayout.AddElement(rotateSnapButton);
  220. handlesLayout.AddElement(rotateSnapInput);
  221. GUIPanel mainPanel = mainLayout.AddPanel();
  222. rtPanel = mainPanel.AddPanel();
  223. GUIPanel sceneAxesPanel = mainPanel.AddPanel(-1);
  224. sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize, ProjectionType.Perspective);
  225. focusCatcher = new GUIButton("", EditorStyles.Blank);
  226. focusCatcher.OnFocusGained += () => hasContentFocus = true;
  227. focusCatcher.OnFocusLost += () => hasContentFocus = false;
  228. GUIPanel focusPanel = GUI.AddPanel(-2);
  229. focusPanel.AddElement(focusCatcher);
  230. toggleProfilerOverlayKey = new VirtualButton(ToggleProfilerOverlayBinding);
  231. viewToolKey = new VirtualButton(ViewToolBinding);
  232. moveToolKey = new VirtualButton(MoveToolBinding);
  233. rotateToolKey = new VirtualButton(RotateToolBinding);
  234. scaleToolKey = new VirtualButton(ScaleToolBinding);
  235. duplicateKey = new VirtualButton(DuplicateBinding);
  236. deleteKey = new VirtualButton(DeleteBinding);
  237. frameKey = new VirtualButton(FrameBinding);
  238. UpdateRenderTexture(Width, Height - HeaderHeight);
  239. UpdateProfilerOverlay();
  240. }
  241. private void OnDestroy()
  242. {
  243. if (camera != null)
  244. {
  245. camera.SceneObject.Destroy();
  246. camera = null;
  247. }
  248. sceneAxesGUI.Destroy();
  249. sceneAxesGUI = null;
  250. }
  251. /// <summary>
  252. /// Orients the camera so it looks along the provided axis.
  253. /// </summary>
  254. /// <param name="axis">Axis to look along.</param>
  255. internal void LookAlong(Vector3 axis)
  256. {
  257. axis.Normalize();
  258. cameraController.LookAlong(axis);
  259. UpdateGridMode();
  260. }
  261. private void UpdateGridMode()
  262. {
  263. Vector3 forward = camera.SceneObject.Forward;
  264. if (camera.ProjectionType == ProjectionType.Perspective)
  265. sceneGrid.SetMode(GridMode.Perspective);
  266. else
  267. {
  268. float dotX = Vector3.Dot(forward, Vector3.XAxis);
  269. if (dotX >= 0.95f)
  270. sceneGrid.SetMode(GridMode.OrthoX);
  271. else if (dotX <= -0.95f)
  272. sceneGrid.SetMode(GridMode.OrthoNegX);
  273. else
  274. {
  275. float dotY = Vector3.Dot(forward, Vector3.YAxis);
  276. if (dotY >= 0.95f)
  277. sceneGrid.SetMode(GridMode.OrthoY);
  278. else if (dotY <= -0.95f)
  279. sceneGrid.SetMode(GridMode.OrthoNegY);
  280. else
  281. {
  282. float dotZ = Vector3.Dot(forward, Vector3.ZAxis);
  283. if (dotZ >= 0.95f)
  284. sceneGrid.SetMode(GridMode.OrthoZ);
  285. else if (dotZ <= -0.95f)
  286. sceneGrid.SetMode(GridMode.OrthoNegZ);
  287. else
  288. sceneGrid.SetMode(GridMode.Perspective);
  289. }
  290. }
  291. }
  292. }
  293. /// <summary>
  294. /// Converts screen coordinates into coordinates relative to the scene view render texture.
  295. /// </summary>
  296. /// <param name="screenPos">Coordinates relative to the screen.</param>
  297. /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
  298. /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
  299. private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
  300. {
  301. scenePos = screenPos;
  302. Vector2I windowPos = ScreenToWindowPos(screenPos);
  303. Rect2I bounds = GUILayoutUtility.CalculateBounds(renderTextureGUI, GUI);
  304. if (bounds.Contains(windowPos))
  305. {
  306. scenePos.x = windowPos.x - bounds.x;
  307. scenePos.y = windowPos.y - bounds.y;
  308. return true;
  309. }
  310. return false;
  311. }
  312. private void OnEditorUpdate()
  313. {
  314. if (HasFocus)
  315. {
  316. if (!Input.IsPointerButtonHeld(PointerButton.Right))
  317. {
  318. if (VirtualInput.IsButtonDown(toggleProfilerOverlayKey))
  319. EditorSettings.SetBool(ProfilerOverlayActiveKey, !EditorSettings.GetBool(ProfilerOverlayActiveKey));
  320. if (VirtualInput.IsButtonDown(viewToolKey))
  321. EditorApplication.ActiveSceneTool = SceneViewTool.View;
  322. if (VirtualInput.IsButtonDown(moveToolKey))
  323. EditorApplication.ActiveSceneTool = SceneViewTool.Move;
  324. if (VirtualInput.IsButtonDown(rotateToolKey))
  325. EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
  326. if (VirtualInput.IsButtonDown(scaleToolKey))
  327. EditorApplication.ActiveSceneTool = SceneViewTool.Scale;
  328. if (VirtualInput.IsButtonDown(duplicateKey))
  329. {
  330. SceneObject[] selectedObjects = Selection.SceneObjects;
  331. CleanDuplicates(ref selectedObjects);
  332. if (selectedObjects.Length > 0)
  333. {
  334. String message;
  335. if (selectedObjects.Length == 1)
  336. message = "Duplicated " + selectedObjects[0].Name;
  337. else
  338. message = "Duplicated " + selectedObjects.Length + " elements";
  339. UndoRedo.CloneSO(selectedObjects, message);
  340. EditorApplication.SetSceneDirty();
  341. }
  342. }
  343. if (VirtualInput.IsButtonDown(deleteKey))
  344. {
  345. SceneObject[] selectedObjects = Selection.SceneObjects;
  346. CleanDuplicates(ref selectedObjects);
  347. if (selectedObjects.Length > 0)
  348. {
  349. foreach (var so in selectedObjects)
  350. {
  351. string message = "Deleted " + so.Name;
  352. UndoRedo.DeleteSO(so, message);
  353. }
  354. EditorApplication.SetSceneDirty();
  355. }
  356. }
  357. }
  358. }
  359. // Refresh GUI buttons if needed (in case someones changes the values from script)
  360. if (editorSettingsHash != EditorSettings.Hash)
  361. {
  362. UpdateButtonStates();
  363. UpdateProfilerOverlay();
  364. editorSettingsHash = EditorSettings.Hash;
  365. }
  366. // Update scene view handles and selection
  367. sceneGizmos.Draw();
  368. sceneGrid.Draw();
  369. bool handleActive = false;
  370. if (Input.IsPointerButtonUp(PointerButton.Left))
  371. {
  372. if (sceneHandles.IsActive())
  373. {
  374. sceneHandles.ClearSelection();
  375. handleActive = true;
  376. }
  377. if (sceneAxesGUI.IsActive())
  378. {
  379. sceneAxesGUI.ClearSelection();
  380. handleActive = true;
  381. }
  382. }
  383. Vector2I scenePos;
  384. bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
  385. bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
  386. draggedOver &= IsPointerHovering && inBounds && DragDrop.Type == DragDropType.Resource;
  387. if (draggedOver)
  388. {
  389. if (DragDrop.DropInProgress)
  390. {
  391. dragActive = false;
  392. if (draggedSO != null)
  393. {
  394. Selection.SceneObject = draggedSO;
  395. EditorApplication.SetSceneDirty();
  396. }
  397. draggedSO = null;
  398. }
  399. else
  400. {
  401. if (!dragActive)
  402. {
  403. dragActive = true;
  404. ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
  405. string[] draggedPaths = dragData.Paths;
  406. for (int i = 0; i < draggedPaths.Length; i++)
  407. {
  408. ResourceMeta meta = ProjectLibrary.GetMeta(draggedPaths[i]);
  409. if (meta != null)
  410. {
  411. if (meta.ResType == ResourceType.Mesh)
  412. {
  413. if (!string.IsNullOrEmpty(draggedPaths[i]))
  414. {
  415. string meshName = Path.GetFileNameWithoutExtension(draggedPaths[i]);
  416. draggedSO = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\"");
  417. Mesh mesh = ProjectLibrary.Load<Mesh>(draggedPaths[i]);
  418. Renderable renderable = draggedSO.AddComponent<Renderable>();
  419. renderable.Mesh = mesh;
  420. }
  421. break;
  422. }
  423. else if (meta.ResType == ResourceType.Prefab)
  424. {
  425. if (!string.IsNullOrEmpty(draggedPaths[i]))
  426. {
  427. Prefab prefab = ProjectLibrary.Load<Prefab>(draggedPaths[i]);
  428. draggedSO = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name);
  429. }
  430. break;
  431. }
  432. }
  433. }
  434. }
  435. if (draggedSO != null)
  436. {
  437. Ray worldRay = camera.ScreenToWorldRay(scenePos);
  438. draggedSO.Position = worldRay*DefaultPlacementDepth;
  439. }
  440. }
  441. return;
  442. }
  443. else
  444. {
  445. if (dragActive)
  446. {
  447. dragActive = false;
  448. if (draggedSO != null)
  449. {
  450. draggedSO.Destroy();
  451. draggedSO = null;
  452. }
  453. }
  454. }
  455. if (HasContentFocus)
  456. {
  457. cameraController.EnableInput(true);
  458. if (inBounds)
  459. {
  460. if (Input.IsPointerButtonDown(PointerButton.Left))
  461. {
  462. Rect2I sceneAxesGUIBounds = new Rect2I(Width - HandleAxesGUISize - HandleAxesGUIPaddingX,
  463. HandleAxesGUIPaddingY, HandleAxesGUISize, HandleAxesGUISize);
  464. if (sceneAxesGUIBounds.Contains(scenePos))
  465. sceneAxesGUI.TrySelect(scenePos);
  466. else
  467. sceneHandles.TrySelect(scenePos);
  468. }
  469. else if (Input.IsPointerButtonUp(PointerButton.Left))
  470. {
  471. if (!handleActive)
  472. {
  473. bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
  474. Input.IsButtonHeld(ButtonCode.RightControl);
  475. sceneSelection.PickObject(scenePos, ctrlHeld);
  476. }
  477. }
  478. }
  479. }
  480. else
  481. cameraController.EnableInput(false);
  482. SceneHandles.BeginInput();
  483. sceneHandles.UpdateInput(scenePos, Input.PointerDelta);
  484. sceneHandles.Draw();
  485. sceneAxesGUI.UpdateInput(scenePos);
  486. sceneAxesGUI.Draw();
  487. SceneHandles.EndInput();
  488. sceneSelection.Draw();
  489. UpdateGridMode();
  490. if (VirtualInput.IsButtonDown(frameKey))
  491. cameraController.FrameSelected();
  492. }
  493. /// <inheritdoc/>
  494. protected override void WindowResized(int width, int height)
  495. {
  496. UpdateRenderTexture(width, height - HeaderHeight);
  497. base.WindowResized(width, height);
  498. }
  499. /// <inheritdoc/>
  500. protected override void FocusChanged(bool inFocus)
  501. {
  502. if (!inFocus)
  503. {
  504. sceneHandles.ClearSelection();
  505. }
  506. }
  507. /// <summary>
  508. /// Triggered when one of the scene tool buttons is clicked, changing the active scene handle.
  509. /// </summary>
  510. /// <param name="tool">Clicked scene tool to activate.</param>
  511. private void OnSceneToolButtonClicked(SceneViewTool tool)
  512. {
  513. EditorApplication.ActiveSceneTool = tool;
  514. editorSettingsHash = EditorSettings.Hash;
  515. }
  516. /// <summary>
  517. /// Triggered when one of the coordinate mode buttons is clicked, changing the active coordinate mode.
  518. /// </summary>
  519. /// <param name="mode">Clicked coordinate mode to activate.</param>
  520. private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
  521. {
  522. EditorApplication.ActiveCoordinateMode = mode;
  523. editorSettingsHash = EditorSettings.Hash;
  524. }
  525. /// <summary>
  526. /// Triggered when one of the pivot buttons is clicked, changing the active pivot mode.
  527. /// </summary>
  528. /// <param name="mode">Clicked pivot mode to activate.</param>
  529. private void OnPivotModeButtonClicked(HandlePivotMode mode)
  530. {
  531. EditorApplication.ActivePivotMode = mode;
  532. editorSettingsHash = EditorSettings.Hash;
  533. }
  534. /// <summary>
  535. /// Triggered when the move snap button is toggled.
  536. /// </summary>
  537. /// <param name="active">Determins should be move snap be activated or deactivated.</param>
  538. private void OnMoveSnapToggled(bool active)
  539. {
  540. Handles.MoveHandleSnapActive = active;
  541. editorSettingsHash = EditorSettings.Hash;
  542. }
  543. /// <summary>
  544. /// Triggered when the move snap increment value changes.
  545. /// </summary>
  546. /// <param name="value">Value that determines in what increments to perform move snapping.</param>
  547. private void OnMoveSnapValueChanged(float value)
  548. {
  549. Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
  550. editorSettingsHash = EditorSettings.Hash;
  551. }
  552. /// <summary>
  553. /// Triggered when the rotate snap button is toggled.
  554. /// </summary>
  555. /// <param name="active">Determins should be rotate snap be activated or deactivated.</param>
  556. private void OnRotateSnapToggled(bool active)
  557. {
  558. Handles.RotateHandleSnapActive = active;
  559. editorSettingsHash = EditorSettings.Hash;
  560. }
  561. /// <summary>
  562. /// Triggered when the rotate snap increment value changes.
  563. /// </summary>
  564. /// <param name="value">Value that determines in what increments to perform rotate snapping.</param>
  565. private void OnRotateSnapValueChanged(float value)
  566. {
  567. Handles.RotateSnapAmount = (Degree)MathEx.Clamp(value, 0.01f, 360.0f);
  568. editorSettingsHash = EditorSettings.Hash;
  569. }
  570. /// <summary>
  571. /// Updates toggle button states according to current editor options. This is useful if tools, coordinate mode,
  572. /// pivot or other scene view options have been modified externally.
  573. /// </summary>
  574. private void UpdateButtonStates()
  575. {
  576. switch (EditorApplication.ActiveSceneTool)
  577. {
  578. case SceneViewTool.View:
  579. viewButton.Value = true;
  580. break;
  581. case SceneViewTool.Move:
  582. moveButton.Value = true;
  583. break;
  584. case SceneViewTool.Rotate:
  585. rotateButton.Value = true;
  586. break;
  587. case SceneViewTool.Scale:
  588. scaleButton.Value = true;
  589. break;
  590. }
  591. switch (EditorApplication.ActiveCoordinateMode)
  592. {
  593. case HandleCoordinateMode.Local:
  594. localCoordButton.Value = true;
  595. break;
  596. case HandleCoordinateMode.World:
  597. worldCoordButton.Value = true;
  598. break;
  599. }
  600. switch (EditorApplication.ActivePivotMode)
  601. {
  602. case HandlePivotMode.Center:
  603. centerButton.Value = true;
  604. break;
  605. case HandlePivotMode.Pivot:
  606. pivotButton.Value = true;
  607. break;
  608. }
  609. if (Handles.MoveHandleSnapActive)
  610. moveSnapButton.Value = true;
  611. else
  612. moveSnapButton.Value = false;
  613. moveSnapInput.Value = Handles.MoveSnapAmount;
  614. if (Handles.RotateHandleSnapActive)
  615. rotateSnapButton.Value = true;
  616. else
  617. rotateSnapButton.Value = false;
  618. moveSnapInput.Value = Handles.RotateSnapAmount.Degrees;
  619. }
  620. /// <summary>
  621. /// Activates or deactivates the profiler overlay according to current editor settings.
  622. /// </summary>
  623. private void UpdateProfilerOverlay()
  624. {
  625. if (EditorSettings.GetBool(ProfilerOverlayActiveKey))
  626. {
  627. if (activeProfilerOverlay == null)
  628. {
  629. SceneObject profilerSO = new SceneObject("EditorProfilerOverlay");
  630. profilerCamera = profilerSO.AddComponent<Camera>();
  631. profilerCamera.Target = renderTexture;
  632. profilerCamera.ClearFlags = ClearFlags.None;
  633. profilerCamera.Priority = 1;
  634. profilerCamera.Layers = 0;
  635. activeProfilerOverlay = profilerSO.AddComponent<ProfilerOverlay>();
  636. }
  637. }
  638. else
  639. {
  640. if (activeProfilerOverlay != null)
  641. {
  642. activeProfilerOverlay.SceneObject.Destroy();
  643. activeProfilerOverlay = null;
  644. profilerCamera = null;
  645. }
  646. }
  647. }
  648. /// <summary>
  649. /// Creates the scene camera and updates the render texture. Should be called at least once before using the
  650. /// scene view. Should be called whenever the window is resized.
  651. /// </summary>
  652. /// <param name="width">Width of the scene render target, in pixels.</param>
  653. /// <param name="height">Height of the scene render target, in pixels.</param>
  654. private void UpdateRenderTexture(int width, int height)
  655. {
  656. width = MathEx.Max(20, width);
  657. height = MathEx.Max(20, height);
  658. renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height);
  659. renderTexture.Priority = 1;
  660. if (camera == null)
  661. {
  662. SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
  663. camera = sceneCameraSO.AddComponent<Camera>();
  664. camera.Target = renderTexture;
  665. camera.ViewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
  666. sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
  667. sceneCameraSO.LookAt(new Vector3(0, 0.5f, 0));
  668. camera.Priority = 2;
  669. camera.NearClipPlane = 0.05f;
  670. camera.FarClipPlane = 2500.0f;
  671. camera.ClearColor = ClearColor;
  672. camera.Layers = UInt64.MaxValue & ~SceneAxesHandle.LAYER; // Don't draw scene axes in this camera
  673. cameraController = sceneCameraSO.AddComponent<SceneCamera>();
  674. renderTextureGUI = new GUIRenderTexture(renderTexture);
  675. rtPanel.AddElement(renderTextureGUI);
  676. sceneGrid = new SceneGrid(camera);
  677. sceneSelection = new SceneSelection(camera);
  678. sceneGizmos = new SceneGizmos(camera);
  679. sceneHandles = new SceneHandles(this, camera);
  680. }
  681. else
  682. {
  683. camera.Target = renderTexture;
  684. renderTextureGUI.RenderTexture = renderTexture;
  685. }
  686. Rect2I rtBounds = new Rect2I(0, 0, width, height);
  687. renderTextureGUI.Bounds = rtBounds;
  688. focusCatcher.Bounds = GUILayoutUtility.CalculateBounds(rtPanel, GUI);
  689. sceneAxesGUI.SetPosition(width - HandleAxesGUISize - HandleAxesGUIPaddingX, HandleAxesGUIPaddingY);
  690. // TODO - Consider only doing the resize once user stops resizing the widget in order to reduce constant
  691. // render target destroy/create cycle for every single pixel.
  692. camera.AspectRatio = width / (float)height;
  693. if (profilerCamera != null)
  694. profilerCamera.Target = renderTexture;
  695. }
  696. /// <summary>
  697. /// Parses an array of scene objects and removes elements that are children of elements that are also in the array.
  698. /// </summary>
  699. /// <param name="objects">Array containing duplicate objects as input, and array without duplicate objects as
  700. /// output.</param>
  701. private void CleanDuplicates(ref SceneObject[] objects)
  702. {
  703. List<SceneObject> cleanList = new List<SceneObject>();
  704. for (int i = 0; i < objects.Length; i++)
  705. {
  706. bool foundParent = false;
  707. for (int j = 0; j < objects.Length; j++)
  708. {
  709. SceneObject elem = objects[i];
  710. while (elem != null && elem != objects[j])
  711. elem = elem.Parent;
  712. bool isChildOf = elem == objects[j];
  713. if (i != j && isChildOf)
  714. {
  715. foundParent = true;
  716. break;
  717. }
  718. }
  719. if (!foundParent)
  720. cleanList.Add(objects[i]);
  721. }
  722. objects = cleanList.ToArray();
  723. }
  724. }
  725. }