SceneWindow.cs 41 KB

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