SceneWindow.cs 39 KB

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