SceneWindow.cs 43 KB

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