SceneWindow.cs 53 KB

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