using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.IO; using BansheeEngine; namespace BansheeEditor { /// /// Available tools in the scene view. /// public enum SceneViewTool { View, Move, Rotate, Scale } /// /// Pivot mode used by the scene view tools. /// public enum HandlePivotMode { Center, Pivot } /// /// Coordinate mode used by the scene view tools. /// public enum HandleCoordinateMode { Local, World } /// /// Manages various generic and global settings relating to the editor. /// public class EditorApplication { /// /// Determines the active tool shown in the scene view. /// public static SceneViewTool ActiveSceneTool { get { return EditorSettings.ActiveSceneTool; } set { EditorSettings.ActiveSceneTool = value; } } /// /// Determines the coordinate mode used by the tools in the scene view. /// public static HandleCoordinateMode ActiveCoordinateMode { get { return EditorSettings.ActiveCoordinateMode; } set { EditorSettings.ActiveCoordinateMode = value; } } /// /// Determines the pivot mode used by the tools in the scene view. /// public static HandlePivotMode ActivePivotMode { get { return EditorSettings.ActivePivotMode; } set { EditorSettings.ActivePivotMode = value; } } /// /// Camera used for rendering the scene view. /// public static Camera SceneViewCamera { get { return EditorWindow.GetWindow().Camera; } } /// /// Absolute path to the folder containing the currently open project. /// public static string ProjectPath { get { return Internal_GetProjectPath(); } } /// /// Name of the currently open project. /// public static string ProjectName { get { return Internal_GetProjectName(); } } /// /// Checks is any project currently loaded. /// public static bool IsProjectLoaded { get { return Internal_GetProjectLoaded(); } } /// /// Returns the path where the script compiler is located at. /// internal static string CompilerPath { get { return Internal_GetCompilerPath(); } } /// /// Returns the path to the folder where the builtin script assemblies are located at. /// internal static string BuiltinAssemblyPath { get { return Internal_GetBuiltinAssemblyPath(); } } /// /// Returns the path to the folder where the custom script assemblies are located at. /// internal static string ScriptAssemblyPath { get { return Internal_GetScriptAssemblyPath(); } } /// /// Returns the path to the folder where the .NET framework assemblies are located at. /// internal static string FrameworkAssemblyPath { get { return Internal_GetFrameworkAssemblyPath(); } } /// /// Name of the builtin assembly containing engine specific types. /// internal static string EngineAssembly { get { return Internal_GetEngineAssemblyName(); } } /// /// Name of the builtin assembly containing editor specific types. /// internal static string EditorAssembly { get { return Internal_GetEditorAssemblyName(); } } /// /// Name of the custom assembly compiled from non-editor scripts within the project. /// internal static string ScriptGameAssembly { get { return Internal_GetScriptGameAssemblyName(); } } /// /// Name of the custom assembly compiled from editor scripts within the project. /// internal static string ScriptEditorAssembly { get { return Internal_GetScriptEditorAssemblyName(); } } private static EditorApplication instance; private static FolderMonitor monitor; /// /// Constructs a new editor application. Called at editor start-up by the runtime. /// internal EditorApplication() { instance = this; // Register controls InputConfiguration inputConfig = VirtualInput.KeyConfig; inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.W); inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.S); inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.A); inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.D); inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.Up); inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.Down); inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.Left); inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.Right); inputConfig.RegisterButton(SceneCamera.FastMoveBinding, ButtonCode.LeftShift); inputConfig.RegisterButton(SceneCamera.RotateBinding, ButtonCode.MouseRight); inputConfig.RegisterAxis(SceneCamera.HorizontalAxisBinding, InputAxis.MouseX); inputConfig.RegisterAxis(SceneCamera.VerticalAxisBinding, InputAxis.MouseY); inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt); inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q); inputConfig.RegisterButton(SceneWindow.MoveToolBinding, ButtonCode.W); inputConfig.RegisterButton(SceneWindow.RotateToolBinding, ButtonCode.E); inputConfig.RegisterButton(SceneWindow.ScaleToolBinding, ButtonCode.R); inputConfig.RegisterButton(SceneWindow.DuplicateBinding, ButtonCode.D, ButtonModifier.Ctrl); } /// /// Triggered when the folder monitor detects an asset in the monitored folder was modified. /// /// Path to the modified file or folder. private static void OnAssetModified(string path) { ProjectLibrary.Refresh(path); } /// /// Called 60 times per second by the runtime. /// internal void OnEditorUpdate() { ProjectLibrary.Update(); } /// /// Opens a dialog that allows the user to select a new prefab to load as the current scene. If current scene /// is modified the user is offered a chance to save it. /// [MenuItem("File/Open Scene", ButtonModifier.Ctrl, ButtonCode.L, 10050, true)] private static void LoadScene() { string[] scenePaths; if (BrowseDialog.OpenFile(ProjectLibrary.ResourceFolder, "", false, out scenePaths)) { if (scenePaths.Length > 0) LoadScene(scenePaths[0]); } } /// /// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously /// saved it is instead automatically saved at the last location. /// [MenuItem("File/Save Scene", ButtonModifier.Ctrl, ButtonCode.S, 10049)] [ToolbarItem("Save Scene", ToolbarIcon.SaveScene, "", 1998)] private static void SaveScene() { if (!string.IsNullOrEmpty(Scene.ActiveSceneUUID)) { string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID); Internal_SaveScene(scenePath); } else SaveSceneAs(); } /// /// Opens a dialog to allows the user to select a location where to save the current scene. /// [MenuItem("File/Save Scene As", 10048)] private static void SaveSceneAs() { string scenePath = ""; if (BrowseDialog.SaveFile(ProjectLibrary.ResourceFolder, "*.prefab", out scenePath)) { if (!PathEx.IsPartOf(scenePath, ProjectLibrary.ResourceFolder)) DialogBox.Open("Error", "The location must be inside the Resources folder of the project.", DialogBox.Type.OK); else { // TODO - If path points to an existing non-scene asset or folder I should delete it otherwise // Internal_SaveScene will silently fail. Scene.ActiveSceneUUID = Internal_SaveScene(scenePath + ".prefab"); } } } /// /// Loads a prefab as the current scene at the specified path. If current scene is modified the user is offered a /// chance to save it. /// /// Path to a valid prefab, relative to the resource folder. public static void LoadScene(string path) { Action continueLoad = (scenePath) => { Scene.Load(path); ProjectSettings.LastOpenScene = scenePath; ProjectSettings.Save(); }; Action dialogCallback = (result) => { if (result == DialogBox.ResultType.Yes) { SaveScene(); continueLoad(path); } else if (result == DialogBox.ResultType.No) continueLoad(path); }; if (Scene.IsModified()) { DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?", DialogBox.Type.YesNoCancel, dialogCallback); } else continueLoad(path); } /// /// Checks does the folder at the provieded path contain a valid project. /// /// Absolute path to the root project folder. /// True if the folder contains a valid project. public static bool IsValidProject(string path) { return Internal_IsValidProject(path); } /// /// Contains a new project in the provided folder. /// /// Absolute path to the folder to create the project in. Name of this folder will be used as the /// project's name. public static void CreateProject(string path) { Internal_CreateProject(path); } /// /// Opens a Project Window allowing you to browse for or create a project. /// [MenuItem("File/Open Project", 10100)] [ToolbarItem("Open Project", ToolbarIcon.OpenProject, "", 2000)] public static void BrowseForProject() { ProjectWindow.Open(); } /// /// Saves all data in the currently open project. /// [MenuItem("File/Save Project", 10099)] [ToolbarItem("Save Project", ToolbarIcon.SaveProject, "", 1999)] public static void SaveProject() { // TODO - Save dirty resources Internal_SaveProject(); } /// /// Loads the project at the specified path. This method executes asynchronously and will trigger /// when done. /// /// Absolute path to the project's root folder. public static void LoadProject(string path) { if (IsProjectLoaded && path == ProjectPath) return; if (!Internal_IsValidProject(path)) { Debug.LogWarning("Provided path: \"" + path + "\" is not a valid project."); return; } if (IsProjectLoaded) UnloadProject(); Internal_LoadProject(path); // Triggers OnProjectLoaded when done } /// /// Opens a file or a folder in the default external application. /// /// Absolute path to the file or folder to open. public static void OpenExternally(string path) { Internal_OpenExternally(path); } /// /// Marks a resource as dirty so that it may be saved the next time the project is saved. Optionally you may also /// call to save it immediately. /// /// Resource to mark as dirty public static void SetDirty(Resource resource) { // TODO - Not implemented } /// /// Triggered when method completes. /// private static void OnProjectLoaded() { if (!IsProjectLoaded) { ProjectWindow.Open(); return; } string projectPath = ProjectPath; RecentProject[] recentProjects = EditorSettings.RecentProjects; bool foundPath = false; for (int i = 0; i < recentProjects.Length; i++) { if (PathEx.Compare(recentProjects[i].path, projectPath)) { recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks; EditorSettings.RecentProjects = recentProjects; foundPath = true; break; } } if (!foundPath) { List extendedRecentProjects = new List(); extendedRecentProjects.AddRange(recentProjects); RecentProject newProject = new RecentProject(); newProject.path = projectPath; newProject.accessTimestamp = (ulong)DateTime.Now.Ticks; extendedRecentProjects.Add(newProject); EditorSettings.RecentProjects = extendedRecentProjects.ToArray(); } EditorSettings.LastOpenProject = projectPath; EditorSettings.Save(); ProjectLibrary.Refresh(); monitor = new FolderMonitor(ProjectLibrary.ResourceFolder); monitor.OnAdded += OnAssetModified; monitor.OnRemoved += OnAssetModified; monitor.OnModified += OnAssetModified; if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene)) Scene.Load(ProjectSettings.LastOpenScene); } /// /// Unloads the currently loaded project. Offers the user a chance to save the current scene if it is modified. /// Automatically saves all project data before unloading. /// private static void UnloadProject() { Action continueUnload = () => { Scene.Clear(); if (monitor != null) { monitor.Destroy(); monitor = null; } LibraryWindow window = EditorWindow.GetWindow(); if(window != null) window.Reset(); Internal_UnloadProject(); }; Action dialogCallback = (result) => { if (result == DialogBox.ResultType.Yes) SaveScene(); continueUnload(); }; if (Scene.IsModified()) { DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?", DialogBox.Type.YesNoCancel, dialogCallback); } else continueUnload(); } [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetProjectPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetProjectName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_GetProjectLoaded(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetCompilerPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetBuiltinAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetScriptAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetFrameworkAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetEngineAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetEditorAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetScriptGameAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetScriptEditorAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_SaveScene(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_IsValidProject(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SaveProject(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_LoadProject(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_UnloadProject(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_CreateProject(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_OpenExternally(string path); } }