SceneWindow.cs 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using bs;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. namespace bs.Editor
  8. {
  9. /** @addtogroup Scene-Editor
  10. * @{
  11. */
  12. /// <summary>
  13. /// Displays the scene view camera and various scene controls.
  14. /// </summary>
  15. internal sealed class SceneWindow : EditorWindow, IGlobalShortcuts
  16. {
  17. internal const string ToggleProfilerOverlayBinding = "ToggleProfilerOverlay";
  18. internal const string ViewToolBinding = "ViewTool";
  19. internal const string MoveToolBinding = "MoveTool";
  20. internal const string RotateToolBinding = "RotateTool";
  21. internal const string ScaleToolBinding = "ScaleTool";
  22. internal const string FrameBinding = "SceneFrame";
  23. private const int HeaderHeight = 20;
  24. private const float DefaultPlacementDepth = 5.0f;
  25. private static readonly Color ClearColor = new Color(0.0f, 0.3685f, 0.7969f);
  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 RenderTexture renderTexture;
  32. private GUILayoutY mainLayout;
  33. private GUIPanel rtPanel;
  34. private GUIPanel sceneAxesPanel;
  35. private GUIButton focusCatcher;
  36. private GUIRenderTexture renderTextureGUI;
  37. private SceneGrid sceneGrid;
  38. private SceneSelection sceneSelection;
  39. private SceneGizmos sceneGizmos;
  40. private SceneHandles sceneHandles;
  41. private GUIToggle viewButton;
  42. private GUIToggle moveButton;
  43. private GUIToggle rotateButton;
  44. private GUIToggle scaleButton;
  45. private GUIToggle localCoordButton;
  46. private GUIToggle worldCoordButton;
  47. private GUIToggle pivotButton;
  48. private GUIToggle centerButton;
  49. private GUIToggle moveSnapButton;
  50. private GUIFloatField moveSnapInput;
  51. private GUIToggle rotateSnapButton;
  52. private GUIFloatField rotateSnapInput;
  53. private GUIButton cameraOptionsButton;
  54. private GUILayout progressLayout;
  55. private GUIProgressBar loadProgressBar;
  56. private GUILabel loadLabel;
  57. private SceneAxesGUI sceneAxesGUI;
  58. private bool hasContentFocus = false;
  59. private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
  60. private bool loadingProgressShown = false;
  61. private bool AllowViewportInput { get { return !loadingProgressShown; } }
  62. private int editorSettingsHash = int.MaxValue;
  63. private VirtualButton frameKey;
  64. // Tool shortcuts
  65. private VirtualButton viewToolKey;
  66. private VirtualButton moveToolKey;
  67. private VirtualButton rotateToolKey;
  68. private VirtualButton scaleToolKey;
  69. // Drag & drop
  70. private bool dragActive;
  71. private SceneObject draggedSO;
  72. private Vector3 draggedSOOffset;
  73. private GUITexture dragSelection;
  74. private bool isDraggingSelection;
  75. private Vector2I dragSelectionStart;
  76. private Vector2I dragSelectionEnd;
  77. private Vector2I mouseDownPosition;
  78. private GUIPanel selectionPanel;
  79. /// <summary>
  80. /// Returns the scene camera.
  81. /// </summary>
  82. public Camera Camera
  83. {
  84. get { return camera; }
  85. }
  86. /// <summary>
  87. /// Determines scene camera's projection type.
  88. /// </summary>
  89. internal ProjectionType ProjectionType
  90. {
  91. get { return cameraController.ProjectionType; }
  92. set { cameraController.ProjectionType = value; sceneAxesGUI.ProjectionType = value; }
  93. }
  94. /// <summary>
  95. /// Determines scene camera's orthographic size.
  96. /// </summary>
  97. internal float OrthographicSize
  98. {
  99. get { return cameraController.OrthographicSize; }
  100. set { cameraController.OrthographicSize = value; }
  101. }
  102. /// <summary>
  103. /// Determines scene camera's field of view.
  104. /// </summary>
  105. internal Degree FieldOfView
  106. {
  107. get { return cameraController.FieldOfView; }
  108. set { cameraController.FieldOfView = value; }
  109. }
  110. /// <summary>
  111. /// Determines scene camera's near clip plane.
  112. /// </summary>
  113. internal float NearClipPlane
  114. {
  115. get { return cameraController.NearClipPlane; }
  116. set { cameraController.NearClipPlane = value; }
  117. }
  118. /// <summary>
  119. /// Determines scene camera's far clip plane.
  120. /// </summary>
  121. internal float FarClipPlane
  122. {
  123. get { return cameraController.FarClipPlane; }
  124. set { cameraController.FarClipPlane = value; }
  125. }
  126. /// <summary>
  127. /// Determines scene camera's scroll speed.
  128. /// </summary>
  129. internal float ScrollSpeed
  130. {
  131. get { return cameraController.ScrollSpeed; }
  132. set { cameraController.ScrollSpeed = value; }
  133. }
  134. /// <summary>
  135. /// Constructs a new scene window.
  136. /// </summary>
  137. internal SceneWindow()
  138. { }
  139. /// <summary>
  140. /// Opens a scene window if its not open already.
  141. /// </summary>
  142. [MenuItem("Windows/Scene", ButtonModifier.CtrlAlt, ButtonCode.S, 6000)]
  143. private static void OpenSceneWindow()
  144. {
  145. OpenWindow<SceneWindow>();
  146. }
  147. /// <summary>
  148. /// Focuses on the currently selected object.
  149. /// </summary>
  150. [MenuItem("Tools/Frame Selected", ButtonModifier.None, ButtonCode.F, 9275, true)]
  151. private static void OpenSettingsWindow()
  152. {
  153. SceneWindow window = GetWindow<SceneWindow>();
  154. if (window != null)
  155. window.cameraController.FrameSelected();
  156. }
  157. /// <summary>
  158. /// Switches the active tool to the view tool.
  159. /// </summary>
  160. [MenuItem("Tools/View", ButtonModifier.Ctrl, ButtonCode.Q, 9274, true)]
  161. private static void SetViewTool()
  162. {
  163. SceneWindow window = GetWindow<SceneWindow>();
  164. if (window != null)
  165. window.OnSceneToolButtonClicked(SceneViewTool.View);
  166. }
  167. /// <summary>
  168. /// Switches the active tool to the move tool.
  169. /// </summary>
  170. [MenuItem("Tools/Move", ButtonModifier.Ctrl, ButtonCode.W, 9273)]
  171. private static void SetMoveTool()
  172. {
  173. SceneWindow window = GetWindow<SceneWindow>();
  174. if (window != null)
  175. window.OnSceneToolButtonClicked(SceneViewTool.Move);
  176. }
  177. /// <summary>
  178. /// Switches the active tool to the rotate tool.
  179. /// </summary>
  180. [MenuItem("Tools/Rotate", ButtonModifier.Ctrl, ButtonCode.E, 9272)]
  181. private static void SetRotateTool()
  182. {
  183. SceneWindow window = GetWindow<SceneWindow>();
  184. if (window != null)
  185. window.OnSceneToolButtonClicked(SceneViewTool.Rotate);
  186. }
  187. /// <summary>
  188. /// Switches the active tool to the scale tool.
  189. /// </summary>
  190. [MenuItem("Tools/Scale", ButtonModifier.Ctrl, ButtonCode.R, 9271)]
  191. private static void SetScaleTool()
  192. {
  193. SceneWindow window = GetWindow<SceneWindow>();
  194. if (window != null)
  195. window.OnSceneToolButtonClicked(SceneViewTool.Scale);
  196. }
  197. /// <inheritdoc/>
  198. protected override LocString GetDisplayName()
  199. {
  200. return new LocEdString("Scene");
  201. }
  202. private void OnInitialize()
  203. {
  204. mainLayout = GUI.AddLayoutY();
  205. GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View),
  206. new LocEdString("View"));
  207. GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move),
  208. new LocEdString("Move"));
  209. GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate),
  210. new LocEdString("Rotate"));
  211. GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale),
  212. new LocEdString("Scale"));
  213. GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local),
  214. new LocEdString("Local"));
  215. GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World),
  216. new LocEdString("World"));
  217. GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot),
  218. new LocEdString("Pivot"));
  219. GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center),
  220. new LocEdString("Center"));
  221. GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap),
  222. new LocEdString("Move snap"));
  223. GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap),
  224. new LocEdString("Rotate snap"));
  225. GUIToggleGroup handlesTG = new GUIToggleGroup();
  226. viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  227. moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  228. rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  229. scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  230. GUIToggleGroup coordModeTG = new GUIToggleGroup();
  231. localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
  232. worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
  233. GUIToggleGroup pivotModeTG = new GUIToggleGroup();
  234. pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  235. centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  236. moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  237. moveSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
  238. rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
  239. rotateSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
  240. GUIContent cameraOptionsIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.SceneCameraOptions), new LocEdString("Camera options"));
  241. cameraOptionsButton = new GUIButton(cameraOptionsIcon);
  242. viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
  243. moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
  244. rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
  245. scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
  246. localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
  247. worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
  248. pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
  249. centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
  250. moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
  251. moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
  252. rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
  253. rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
  254. cameraOptionsButton.OnClick += () => OnCameraOptionsClicked();
  255. GUILayout handlesLayout = mainLayout.AddLayoutX();
  256. handlesLayout.AddElement(viewButton);
  257. handlesLayout.AddElement(moveButton);
  258. handlesLayout.AddElement(rotateButton);
  259. handlesLayout.AddElement(scaleButton);
  260. handlesLayout.AddSpace(10);
  261. handlesLayout.AddElement(localCoordButton);
  262. handlesLayout.AddElement(worldCoordButton);
  263. handlesLayout.AddSpace(10);
  264. handlesLayout.AddElement(pivotButton);
  265. handlesLayout.AddElement(centerButton);
  266. handlesLayout.AddFlexibleSpace();
  267. handlesLayout.AddElement(moveSnapButton);
  268. handlesLayout.AddElement(moveSnapInput);
  269. handlesLayout.AddSpace(10);
  270. handlesLayout.AddElement(rotateSnapButton);
  271. handlesLayout.AddElement(rotateSnapInput);
  272. handlesLayout.AddSpace(10);
  273. handlesLayout.AddElement(cameraOptionsButton);
  274. handlesLayout.SetHeight(viewButton.Bounds.height);
  275. GUIPanel mainPanel = mainLayout.AddPanel();
  276. rtPanel = mainPanel.AddPanel();
  277. // Loading progress
  278. loadLabel = new GUILabel(new LocEdString("Loading scene..."));
  279. loadProgressBar = new GUIProgressBar("", GUIOption.FixedWidth(200));
  280. progressLayout = mainPanel.AddLayoutY();
  281. progressLayout.AddFlexibleSpace();
  282. GUILayout loadLabelLayout = progressLayout.AddLayoutX();
  283. loadLabelLayout.AddFlexibleSpace();
  284. loadLabelLayout.AddElement(loadLabel);
  285. loadLabelLayout.AddFlexibleSpace();
  286. GUILayout progressBarLayout = progressLayout.AddLayoutX();
  287. progressBarLayout.AddFlexibleSpace();
  288. progressBarLayout.AddElement(loadProgressBar);
  289. progressBarLayout.AddFlexibleSpace();
  290. progressLayout.AddFlexibleSpace();
  291. progressLayout.Active = false;
  292. selectionPanel = mainPanel.AddPanel(-1);
  293. sceneAxesPanel = mainPanel.AddPanel(-1);
  294. sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize, ProjectionType.Perspective);
  295. focusCatcher = new GUIButton("", EditorStyles.Blank);
  296. focusCatcher.OnFocusGained += () => hasContentFocus = true;
  297. focusCatcher.OnFocusLost += () => hasContentFocus = false;
  298. GUIPanel focusPanel = GUI.AddPanel(-2);
  299. focusPanel.AddElement(focusCatcher);
  300. viewToolKey = new VirtualButton(ViewToolBinding);
  301. moveToolKey = new VirtualButton(MoveToolBinding);
  302. rotateToolKey = new VirtualButton(RotateToolBinding);
  303. scaleToolKey = new VirtualButton(ScaleToolBinding);
  304. frameKey = new VirtualButton(FrameBinding);
  305. UpdateRenderTexture(Width, Height - HeaderHeight);
  306. UpdateLoadingProgress();
  307. }
  308. private void OnCameraOptionsClicked()
  309. {
  310. Vector2I openPosition;
  311. Rect2I buttonBounds = GUIUtility.CalculateBounds(cameraOptionsButton, GUI);
  312. openPosition.x = buttonBounds.x + buttonBounds.width / 2;
  313. openPosition.y = buttonBounds.y + buttonBounds.height / 2;
  314. SceneCameraOptionsDropdown cameraOptionsDropdown = DropDownWindow.Open<SceneCameraOptionsDropdown>(GUI, openPosition);
  315. cameraOptionsDropdown.Initialize(this);
  316. }
  317. private void OnDestroy()
  318. {
  319. if (camera != null)
  320. {
  321. camera.SceneObject.Destroy(true);
  322. camera = null;
  323. }
  324. sceneAxesGUI.Destroy();
  325. sceneAxesGUI = null;
  326. }
  327. /// <summary>
  328. /// Deletes all currently selected objects.
  329. /// </summary>
  330. private void DeleteSelection()
  331. {
  332. SceneObject[] selectedObjects = Selection.SceneObjects;
  333. CleanDuplicates(ref selectedObjects);
  334. if (selectedObjects.Length > 0)
  335. {
  336. foreach (var so in selectedObjects)
  337. {
  338. string message = "Deleted " + so.Name;
  339. UndoRedo.DeleteSO(so, message);
  340. }
  341. EditorApplication.SetSceneDirty();
  342. }
  343. }
  344. /// <summary>
  345. /// Duplicates all currently selected objects.
  346. /// </summary>
  347. private void DuplicateSelection()
  348. {
  349. SceneObject[] selectedObjects = Selection.SceneObjects;
  350. CleanDuplicates(ref selectedObjects);
  351. if (selectedObjects.Length > 0)
  352. {
  353. string message;
  354. if (selectedObjects.Length == 1)
  355. message = "Duplicated " + selectedObjects[0].Name;
  356. else
  357. message = "Duplicated " + selectedObjects.Length + " elements";
  358. Transform[] savedTransforms = new Transform[selectedObjects.Length];
  359. for (int i = 0; i < savedTransforms.Length; i++)
  360. savedTransforms[i] = new Transform(selectedObjects[i]);
  361. SceneObject[] clonedObjects = UndoRedo.CloneSO(selectedObjects, message);
  362. // Restore world positions
  363. for (int i = 0; i < savedTransforms.Length; i++)
  364. savedTransforms[i].Apply(clonedObjects[i]);
  365. EditorApplication.SetSceneDirty();
  366. }
  367. }
  368. /// <inheritdoc/>
  369. void IGlobalShortcuts.OnRenamePressed()
  370. {
  371. // Do nothing
  372. }
  373. /// <inheritdoc/>
  374. void IGlobalShortcuts.OnDuplicatePressed()
  375. {
  376. DuplicateSelection();
  377. }
  378. /// <inheritdoc/>
  379. void IGlobalShortcuts.OnDeletePressed()
  380. {
  381. DeleteSelection();
  382. }
  383. /// <inheritdoc/>
  384. void IGlobalShortcuts.OnCopyPressed()
  385. {
  386. // Do nothing
  387. }
  388. /// <inheritdoc/>
  389. void IGlobalShortcuts.OnCutPressed()
  390. {
  391. // Do nothing
  392. }
  393. /// <inheritdoc/>
  394. void IGlobalShortcuts.OnPastePressed()
  395. {
  396. // Do nothing
  397. }
  398. /// <summary>
  399. /// Orients the camera so it looks along the provided axis.
  400. /// </summary>
  401. /// <param name="axis">Axis to look along.</param>
  402. internal void LookAlong(Vector3 axis)
  403. {
  404. axis.Normalize();
  405. cameraController.LookAlong(axis);
  406. UpdateGridMode();
  407. }
  408. private void UpdateGridMode()
  409. {
  410. Vector3 forward = camera.SceneObject.Forward;
  411. if (camera.ProjectionType == ProjectionType.Perspective)
  412. sceneGrid.SetMode(GridMode.Perspective);
  413. else
  414. {
  415. float dotX = Vector3.Dot(forward, Vector3.XAxis);
  416. if (dotX >= 0.95f)
  417. sceneGrid.SetMode(GridMode.OrthoX);
  418. else if (dotX <= -0.95f)
  419. sceneGrid.SetMode(GridMode.OrthoNegX);
  420. else
  421. {
  422. float dotY = Vector3.Dot(forward, Vector3.YAxis);
  423. if (dotY >= 0.95f)
  424. sceneGrid.SetMode(GridMode.OrthoY);
  425. else if (dotY <= -0.95f)
  426. sceneGrid.SetMode(GridMode.OrthoNegY);
  427. else
  428. {
  429. float dotZ = Vector3.Dot(forward, Vector3.ZAxis);
  430. if (dotZ >= 0.95f)
  431. sceneGrid.SetMode(GridMode.OrthoZ);
  432. else if (dotZ <= -0.95f)
  433. sceneGrid.SetMode(GridMode.OrthoNegZ);
  434. else
  435. sceneGrid.SetMode(GridMode.Perspective);
  436. }
  437. }
  438. }
  439. }
  440. /// <summary>
  441. /// Converts screen coordinates into coordinates relative to the scene view render texture.
  442. /// </summary>
  443. /// <param name="screenPos">Coordinates relative to the screen.</param>
  444. /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
  445. /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
  446. private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
  447. {
  448. scenePos = screenPos;
  449. Vector2I windowPos = ScreenToWindowPos(screenPos);
  450. Rect2I bounds = GUIUtility.CalculateBounds(renderTextureGUI, GUI);
  451. if (bounds.Contains(windowPos))
  452. {
  453. scenePos.x = windowPos.x - bounds.x;
  454. scenePos.y = windowPos.y - bounds.y;
  455. return true;
  456. }
  457. return false;
  458. }
  459. private void OnEditorUpdate()
  460. {
  461. UpdateLoadingProgress();
  462. if (HasFocus)
  463. {
  464. if (!Input.IsPointerButtonHeld(PointerButton.Right))
  465. {
  466. if (VirtualInput.IsButtonDown(EditorApplication.DuplicateKey))
  467. DuplicateSelection();
  468. else if (VirtualInput.IsButtonDown(EditorApplication.DeleteKey))
  469. DeleteSelection();
  470. else if (VirtualInput.IsButtonDown(viewToolKey))
  471. EditorApplication.ActiveSceneTool = SceneViewTool.View;
  472. else if (VirtualInput.IsButtonDown(moveToolKey))
  473. EditorApplication.ActiveSceneTool = SceneViewTool.Move;
  474. else if (VirtualInput.IsButtonDown(rotateToolKey))
  475. EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
  476. else if (VirtualInput.IsButtonDown(scaleToolKey))
  477. EditorApplication.ActiveSceneTool = SceneViewTool.Scale;
  478. }
  479. }
  480. // Refresh GUI buttons if needed (in case someones changes the values from script)
  481. if (editorSettingsHash != EditorSettings.Hash)
  482. {
  483. UpdateButtonStates();
  484. editorSettingsHash = EditorSettings.Hash;
  485. }
  486. // Update scene view handles and selection
  487. sceneGrid.Draw();
  488. bool handleActive = sceneHandles.IsActive() || sceneAxesGUI.IsActive();
  489. Vector2I scenePos;
  490. bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
  491. bool clearSelection = false;
  492. if (AllowViewportInput)
  493. {
  494. if (Input.IsPointerButtonUp(PointerButton.Left))
  495. clearSelection = true;
  496. else if (Input.IsPointerButtonDown(PointerButton.Left))
  497. mouseDownPosition = scenePos;
  498. }
  499. else
  500. {
  501. clearSelection = true;
  502. inBounds = false;
  503. }
  504. bool dragResult = false;
  505. if (clearSelection)
  506. {
  507. dragResult = EndDragSelection();
  508. if (sceneHandles.IsActive())
  509. sceneHandles.ClearSelection();
  510. if (sceneAxesGUI.IsActive())
  511. sceneAxesGUI.ClearSelection();
  512. }
  513. bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
  514. draggedOver &= IsPointerHovering && inBounds && DragDrop.Type == DragDropType.Resource;
  515. if (draggedOver)
  516. {
  517. if (DragDrop.DropInProgress)
  518. {
  519. dragActive = false;
  520. if (draggedSO != null)
  521. {
  522. Selection.SceneObject = draggedSO;
  523. EditorApplication.SetSceneDirty();
  524. }
  525. draggedSO = null;
  526. }
  527. else
  528. {
  529. if (!dragActive)
  530. {
  531. dragActive = true;
  532. ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
  533. string[] draggedPaths = dragData.Paths;
  534. for (int i = 0; i < draggedPaths.Length; i++)
  535. {
  536. ResourceMeta meta = ProjectLibrary.GetMeta(draggedPaths[i]);
  537. if (meta != null)
  538. {
  539. if (meta.ResType == ResourceType.Mesh)
  540. {
  541. if (!string.IsNullOrEmpty(draggedPaths[i]))
  542. {
  543. string meshName = Path.GetFileNameWithoutExtension(draggedPaths[i]);
  544. draggedSO = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\"");
  545. Mesh mesh = ProjectLibrary.Load<Mesh>(draggedPaths[i]);
  546. Renderable renderable = draggedSO.AddComponent<Renderable>();
  547. renderable.Mesh = mesh;
  548. if (mesh != null)
  549. draggedSOOffset = mesh.Bounds.Box.Center;
  550. else
  551. draggedSOOffset = Vector3.Zero;
  552. }
  553. break;
  554. }
  555. else if (meta.ResType == ResourceType.Prefab)
  556. {
  557. if (!string.IsNullOrEmpty(draggedPaths[i]))
  558. {
  559. Prefab prefab = ProjectLibrary.Load<Prefab>(draggedPaths[i]);
  560. draggedSO = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name);
  561. if (draggedSO != null)
  562. {
  563. AABox draggedObjBounds = EditorUtility.CalculateBounds(draggedSO);
  564. draggedSOOffset = draggedObjBounds.Center;
  565. }
  566. else
  567. draggedSOOffset = Vector3.Zero;
  568. }
  569. break;
  570. }
  571. }
  572. }
  573. }
  574. if (draggedSO != null)
  575. {
  576. if (Input.IsButtonHeld(ButtonCode.Space))
  577. {
  578. SnapData snapData;
  579. sceneSelection.Snap(scenePos, out snapData, new SceneObject[] { draggedSO });
  580. Quaternion q = Quaternion.FromToRotation(Vector3.YAxis, snapData.normal);
  581. draggedSO.Position = snapData.position;
  582. draggedSO.Rotation = q;
  583. }
  584. else
  585. {
  586. Ray worldRay = camera.ScreenPointToRay(scenePos);
  587. draggedSO.Position = worldRay * DefaultPlacementDepth - draggedSOOffset;
  588. }
  589. }
  590. }
  591. return;
  592. }
  593. else
  594. {
  595. if (dragActive)
  596. {
  597. dragActive = false;
  598. if (draggedSO != null)
  599. {
  600. draggedSO.Destroy();
  601. draggedSO = null;
  602. }
  603. }
  604. }
  605. if ((HasContentFocus || IsPointerHovering) && AllowViewportInput)
  606. {
  607. cameraController.EnableInput(true);
  608. if (inBounds && HasContentFocus)
  609. {
  610. if (Input.IsPointerButtonDown(PointerButton.Left))
  611. {
  612. Rect2I sceneAxesGUIBounds = new Rect2I(Width - HandleAxesGUISize - HandleAxesGUIPaddingX,
  613. HandleAxesGUIPaddingY, HandleAxesGUISize, HandleAxesGUISize);
  614. if (sceneAxesGUIBounds.Contains(scenePos))
  615. sceneAxesGUI.TrySelect(scenePos);
  616. else
  617. sceneHandles.TrySelect(scenePos);
  618. }
  619. else if (Input.IsPointerButtonHeld(PointerButton.Left) && !handleActive && !dragActive &&
  620. draggedSO == null && scenePos != mouseDownPosition)
  621. {
  622. if (isDraggingSelection)
  623. UpdateDragSelection(scenePos);
  624. else
  625. StartDragSelection(scenePos);
  626. }
  627. else if (Input.IsPointerButtonUp(PointerButton.Left))
  628. {
  629. if (!handleActive && !dragActive && !dragResult)
  630. {
  631. bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
  632. Input.IsButtonHeld(ButtonCode.RightControl);
  633. sceneSelection.PickObject(scenePos, ctrlHeld, new SceneObject[] { draggedSO });
  634. }
  635. }
  636. }
  637. }
  638. else
  639. cameraController.EnableInput(false);
  640. if (AllowViewportInput)
  641. {
  642. SceneHandles.BeginInput();
  643. sceneHandles.UpdateInput(scenePos, Input.PointerDelta);
  644. sceneAxesGUI.UpdateInput(scenePos);
  645. SceneHandles.EndInput();
  646. }
  647. sceneHandles.Draw();
  648. sceneAxesGUI.Draw();
  649. // Must be done after handle input is processed, in order to reflect most recent transform
  650. sceneGizmos.Draw();
  651. sceneSelection.Draw();
  652. UpdateGridMode();
  653. if (VirtualInput.IsButtonDown(frameKey))
  654. cameraController.FrameSelected();
  655. }
  656. /// <inheritdoc/>
  657. protected override void WindowResized(int width, int height)
  658. {
  659. UpdateRenderTexture(width, height - HeaderHeight);
  660. base.WindowResized(width, height);
  661. }
  662. /// <inheritdoc/>
  663. protected override void FocusChanged(bool inFocus)
  664. {
  665. if (!inFocus)
  666. {
  667. sceneHandles.ClearSelection();
  668. }
  669. }
  670. /// <summary>
  671. /// Triggered when one of the scene tool buttons is clicked, changing the active scene handle.
  672. /// </summary>
  673. /// <param name="tool">Clicked scene tool to activate.</param>
  674. private void OnSceneToolButtonClicked(SceneViewTool tool)
  675. {
  676. EditorApplication.ActiveSceneTool = tool;
  677. editorSettingsHash = EditorSettings.Hash;
  678. }
  679. /// <summary>
  680. /// Triggered when one of the coordinate mode buttons is clicked, changing the active coordinate mode.
  681. /// </summary>
  682. /// <param name="mode">Clicked coordinate mode to activate.</param>
  683. private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
  684. {
  685. EditorApplication.ActiveCoordinateMode = mode;
  686. editorSettingsHash = EditorSettings.Hash;
  687. }
  688. /// <summary>
  689. /// Triggered when one of the pivot buttons is clicked, changing the active pivot mode.
  690. /// </summary>
  691. /// <param name="mode">Clicked pivot mode to activate.</param>
  692. private void OnPivotModeButtonClicked(HandlePivotMode mode)
  693. {
  694. EditorApplication.ActivePivotMode = mode;
  695. editorSettingsHash = EditorSettings.Hash;
  696. }
  697. /// <summary>
  698. /// Triggered when the move snap button is toggled.
  699. /// </summary>
  700. /// <param name="active">Determins should be move snap be activated or deactivated.</param>
  701. private void OnMoveSnapToggled(bool active)
  702. {
  703. Handles.MoveHandleSnapActive = active;
  704. editorSettingsHash = EditorSettings.Hash;
  705. }
  706. /// <summary>
  707. /// Triggered when the move snap increment value changes.
  708. /// </summary>
  709. /// <param name="value">Value that determines in what increments to perform move snapping.</param>
  710. private void OnMoveSnapValueChanged(float value)
  711. {
  712. Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
  713. editorSettingsHash = EditorSettings.Hash;
  714. }
  715. /// <summary>
  716. /// Triggered when the rotate snap button is toggled.
  717. /// </summary>
  718. /// <param name="active">Determins should be rotate snap be activated or deactivated.</param>
  719. private void OnRotateSnapToggled(bool active)
  720. {
  721. Handles.RotateHandleSnapActive = active;
  722. editorSettingsHash = EditorSettings.Hash;
  723. }
  724. /// <summary>
  725. /// Triggered when the rotate snap increment value changes.
  726. /// </summary>
  727. /// <param name="value">Value that determines in what increments to perform rotate snapping.</param>
  728. private void OnRotateSnapValueChanged(float value)
  729. {
  730. Handles.RotateSnapAmount = (Degree)MathEx.Clamp(value, 0.01f, 360.0f);
  731. editorSettingsHash = EditorSettings.Hash;
  732. }
  733. /// <summary>
  734. /// Updates toggle button states according to current editor options. This is useful if tools, coordinate mode,
  735. /// pivot or other scene view options have been modified externally.
  736. /// </summary>
  737. private void UpdateButtonStates()
  738. {
  739. switch (EditorApplication.ActiveSceneTool)
  740. {
  741. case SceneViewTool.View:
  742. viewButton.Value = true;
  743. break;
  744. case SceneViewTool.Move:
  745. moveButton.Value = true;
  746. break;
  747. case SceneViewTool.Rotate:
  748. rotateButton.Value = true;
  749. break;
  750. case SceneViewTool.Scale:
  751. scaleButton.Value = true;
  752. break;
  753. }
  754. switch (EditorApplication.ActiveCoordinateMode)
  755. {
  756. case HandleCoordinateMode.Local:
  757. localCoordButton.Value = true;
  758. break;
  759. case HandleCoordinateMode.World:
  760. worldCoordButton.Value = true;
  761. break;
  762. }
  763. switch (EditorApplication.ActivePivotMode)
  764. {
  765. case HandlePivotMode.Center:
  766. centerButton.Value = true;
  767. break;
  768. case HandlePivotMode.Pivot:
  769. pivotButton.Value = true;
  770. break;
  771. }
  772. if (Handles.MoveHandleSnapActive)
  773. moveSnapButton.Value = true;
  774. else
  775. moveSnapButton.Value = false;
  776. moveSnapInput.Value = Handles.MoveSnapAmount;
  777. if (Handles.RotateHandleSnapActive)
  778. rotateSnapButton.Value = true;
  779. else
  780. rotateSnapButton.Value = false;
  781. moveSnapInput.Value = Handles.RotateSnapAmount.Degrees;
  782. }
  783. /// <summary>
  784. /// Creates the scene camera and updates the render texture. Should be called at least once before using the
  785. /// scene view. Should be called whenever the window is resized.
  786. /// </summary>
  787. /// <param name="width">Width of the scene render target, in pixels.</param>
  788. /// <param name="height">Height of the scene render target, in pixels.</param>
  789. private void UpdateRenderTexture(int width, int height)
  790. {
  791. width = MathEx.Max(20, width);
  792. height = MathEx.Max(20, height);
  793. // Note: Depth buffer and readable flags are required because ScenePicking uses it
  794. Texture colorTex = Texture.Create2D(width, height, PixelFormat.RGBA8, TextureUsage.Render | TextureUsage.CPUReadable);
  795. Texture depthTex = Texture.Create2D(width, height, PixelFormat.D32_S8X24, TextureUsage.DepthStencil | TextureUsage.CPUReadable);
  796. renderTexture = new RenderTexture(colorTex, depthTex);
  797. renderTexture.Priority = 1;
  798. if (camera == null)
  799. {
  800. SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
  801. camera = sceneCameraSO.AddComponent<Camera>();
  802. camera.Viewport.Target = renderTexture;
  803. camera.Viewport.Area = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
  804. sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
  805. sceneCameraSO.LookAt(new Vector3(0, 0.5f, 0));
  806. camera.Priority = 2;
  807. camera.Viewport.ClearColor = ClearColor;
  808. camera.Layers = UInt64.MaxValue & ~SceneAxesHandle.LAYER; // Don't draw scene axes in this camera
  809. cameraController = sceneCameraSO.AddComponent<SceneCamera>();
  810. cameraController.Initialize();
  811. renderTextureGUI = new GUIRenderTexture(renderTexture);
  812. rtPanel.AddElement(renderTextureGUI);
  813. sceneGrid = new SceneGrid(camera);
  814. sceneSelection = new SceneSelection(camera);
  815. sceneGizmos = new SceneGizmos(camera);
  816. sceneHandles = new SceneHandles(this, camera);
  817. }
  818. else
  819. {
  820. camera.Viewport.Target = renderTexture;
  821. renderTextureGUI.RenderTexture = renderTexture;
  822. }
  823. Rect2I rtBounds = new Rect2I(0, 0, width, height);
  824. renderTextureGUI.Bounds = rtBounds;
  825. focusCatcher.Bounds = GUIUtility.CalculateBounds(rtPanel, GUI);
  826. sceneAxesGUI.SetPosition(width - HandleAxesGUISize - HandleAxesGUIPaddingX, HandleAxesGUIPaddingY);
  827. // TODO - Consider only doing the resize once user stops resizing the widget in order to reduce constant
  828. // render target destroy/create cycle for every single pixel.
  829. camera.AspectRatio = width / (float)height;
  830. }
  831. /// <summary>
  832. /// Parses an array of scene objects and removes elements that are children of elements that are also in the array.
  833. /// </summary>
  834. /// <param name="objects">Array containing duplicate objects as input, and array without duplicate objects as
  835. /// output.</param>
  836. private void CleanDuplicates(ref SceneObject[] objects)
  837. {
  838. List<SceneObject> cleanList = new List<SceneObject>();
  839. for (int i = 0; i < objects.Length; i++)
  840. {
  841. bool foundParent = false;
  842. for (int j = 0; j < objects.Length; j++)
  843. {
  844. SceneObject elem = objects[i];
  845. while (elem != null && elem != objects[j])
  846. elem = elem.Parent;
  847. bool isChildOf = elem == objects[j];
  848. if (i != j && isChildOf)
  849. {
  850. foundParent = true;
  851. break;
  852. }
  853. }
  854. if (!foundParent)
  855. cleanList.Add(objects[i]);
  856. }
  857. objects = cleanList.ToArray();
  858. }
  859. /// <summary>
  860. /// Checks if the load progress bar needs to be shown, shows/hides it and updates the progress accordingly.
  861. /// </summary>
  862. private void UpdateLoadingProgress()
  863. {
  864. bool needsProgress = EditorApplication.IsSceneLoading;
  865. if (needsProgress && !loadingProgressShown)
  866. {
  867. progressLayout.Active = true;
  868. rtPanel.Active = false;
  869. sceneAxesPanel.Active = false;
  870. loadingProgressShown = true;
  871. }
  872. else if(!needsProgress && loadingProgressShown)
  873. {
  874. progressLayout.Active = false;
  875. rtPanel.Active = true;
  876. sceneAxesPanel.Active = true;
  877. loadingProgressShown = false;
  878. }
  879. if (needsProgress)
  880. loadProgressBar.Percent = EditorApplication.SceneLoadProgress;
  881. }
  882. /// <summary>
  883. /// Starts a drag operation that displays a selection outline allowing the user to select multiple entries at once.
  884. /// </summary>
  885. /// <param name="scenePos">Coordinates relative to the scene where the drag originated.</param>
  886. private void StartDragSelection(Vector2I scenePos)
  887. {
  888. isDraggingSelection = true;
  889. dragSelectionStart = scenePos;
  890. dragSelectionEnd = dragSelectionStart;
  891. }
  892. /// <summary>
  893. /// Updates a selection outline drag operation by expanding the outline to the new location. Elements in the outline
  894. /// are selected.
  895. /// </summary>
  896. /// <param name="scenePos">Coordinates of the pointer relative to the scene.</param>
  897. /// <returns>True if the selection outline drag is valid and was updated, false otherwise.</returns>
  898. private bool UpdateDragSelection(Vector2I scenePos)
  899. {
  900. if (!isDraggingSelection)
  901. return false;
  902. if (dragSelection == null)
  903. {
  904. dragSelection = new GUITexture(null, true, EditorStylesInternal.SelectionArea);
  905. selectionPanel.AddElement(dragSelection);
  906. }
  907. dragSelectionEnd = scenePos;
  908. Rect2I selectionArea = new Rect2I();
  909. Vector2I min = new Vector2I(Math.Min(dragSelectionStart.x, dragSelectionEnd.x), Math.Min(dragSelectionStart.y, dragSelectionEnd.y));
  910. Vector2I max = new Vector2I(Math.Max(dragSelectionStart.x, dragSelectionEnd.x), Math.Max(dragSelectionStart.y, dragSelectionEnd.y));
  911. selectionArea.x = min.x;
  912. selectionArea.y = min.y;
  913. selectionArea.width = Math.Max(max.x - min.x, 1);
  914. selectionArea.height = Math.Max(max.y - min.y, 1);
  915. dragSelection.Bounds = selectionArea;
  916. return true;
  917. }
  918. /// <summary>
  919. /// Ends the selection outline drag operation. Elements in the outline are selected.
  920. /// </summary>
  921. /// <returns>True if the selection outline drag is valid and was ended, false otherwise.</returns>
  922. private bool EndDragSelection()
  923. {
  924. if (!isDraggingSelection)
  925. return false;
  926. if (dragSelection != null)
  927. {
  928. dragSelection.Destroy();
  929. dragSelection = null;
  930. }
  931. if ((dragSelectionEnd - dragSelectionStart).Length < 1)
  932. {
  933. isDraggingSelection = false;
  934. return false;
  935. }
  936. Vector2I min = new Vector2I(Math.Min(dragSelectionStart.x, dragSelectionEnd.x),
  937. Math.Min(dragSelectionStart.y, dragSelectionEnd.y));
  938. Vector2I max = new Vector2I(Math.Max(dragSelectionStart.x, dragSelectionEnd.x),
  939. Math.Max(dragSelectionStart.y, dragSelectionEnd.y));
  940. sceneSelection.PickObjects(min, max - min,
  941. Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl));
  942. isDraggingSelection = false;
  943. return true;
  944. }
  945. /// <summary>
  946. /// Contains information about world transform of a single scene object.
  947. /// </summary>
  948. struct Transform
  949. {
  950. public Transform(SceneObject so)
  951. {
  952. position = so.Position;
  953. rotation = so.Rotation;
  954. scale = so.Scale;
  955. }
  956. /// <summary>
  957. /// Applies the saved transform to the specified scene object. The transform is assumed to be in world space.
  958. /// </summary>
  959. /// <param name="so">Scene object to apply the transform to.</param>
  960. public void Apply(SceneObject so)
  961. {
  962. so.Position = position;
  963. so.Rotation = rotation;
  964. so.LocalScale = scale;
  965. }
  966. public Vector3 position;
  967. public Quaternion rotation;
  968. public Vector3 scale;
  969. }
  970. }
  971. /** @} */
  972. }