Refactor Vector3Field and update project structure
Changed Vector3Field.cs to derive from ValueControl<Vector3> and use NumberBox controls for better UI handling. Changed EditorControls.xaml to update resource paths for new controls. Changed InternalControls.xaml to simplify the resource dictionary by removing unnecessary references. Changed IComponentEditor.cs to reflect updates in the component editor's lifecycle methods. Changed project files for Ghost.Editor and Ghost.Core to include new dependencies and project references. Changed FileExtensions.cs and IInspectorService.cs to align with the new namespace structure. Changed Result.cs to enhance error handling and success checking methods. Changed TypeHandle.cs to improve type handling compatibility. Changed AssemblyInfo.cs files to include new assembly visibility attributes for better encapsulation. Added new graphics-related classes and interfaces in the Ghost.Engine project, including IGraphicsDevice and DX12GraphicsDevice. Added a new Mesh class to handle 3D mesh data and provide methods for creating geometric shapes. Added GraphicsPipeline.cs to manage the graphics rendering loop and device initialization. Added ScenePage.xaml and ScenePage.xaml.cs to create a new page for rendering scenes. Updated HierarchyPage.xaml.cs and InspectorPage.xaml.cs to use the new service locator pattern for service retrieval. Updated LandingWindow.xaml.cs and EngineEditorWindow.xaml.cs to utilize the new service locator pattern for better service access. Updated Logger.cs to enhance logging capabilities with optional stack traces and assertion logging. Updated QueryFilter.cs and QueryEnumerable.cs to use the new TypeHandle structure for improved efficiency. Updated WorldNode.cs and WorldNodeSerializer.cs to enhance serialization and management of world nodes. Updated AssetDatabase and related classes to improve asset management and metadata generation. Updated Ghost.UnitTest.csproj to include new project references and package dependencies for unit tests.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Engine.Models;
|
||||
using Ghost.Engine.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class ConsoleViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<LogMessage> Logs
|
||||
{
|
||||
get; set;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowInfo
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowWarning
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowError
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowStackTrace
|
||||
{
|
||||
get; set;
|
||||
} = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LogMessage? SelectedLog
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ConsoleViewModel()
|
||||
{
|
||||
foreach (var log in Logger.Logs)
|
||||
{
|
||||
Logs.Add(log);
|
||||
}
|
||||
|
||||
Logger.OnLogsUpdate += UpdateLogs;
|
||||
}
|
||||
|
||||
~ConsoleViewModel()
|
||||
{
|
||||
Logger.OnLogsUpdate -= UpdateLogs;
|
||||
}
|
||||
|
||||
private void UpdateLogs(LogChangeType updateType)
|
||||
{
|
||||
switch (updateType)
|
||||
{
|
||||
case LogChangeType.LogAdded:
|
||||
Logs.Add(Logger.Logs[^1]);
|
||||
break;
|
||||
case LogChangeType.LogRemoved:
|
||||
if (Logs.Count > 0)
|
||||
{
|
||||
Logs.RemoveAt(0);
|
||||
}
|
||||
break;
|
||||
case LogChangeType.LogsCleared:
|
||||
Logs.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnShowStackTraceChanged(bool value)
|
||||
{
|
||||
Logger.HasStackTrace = value;
|
||||
Logger.LogInfo($"Stack trace visibility set to {value}.");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ClearLogs()
|
||||
{
|
||||
Logger.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.SceneGraph;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class HierarchyViewModel : ObservableObject, INavigationAware
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<WorldNode> SceneList
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new(EditorWorldManager.LoadedWorlds);
|
||||
|
||||
private void OnWorldLoaded(WorldNode node)
|
||||
{
|
||||
SceneList.Add(node);
|
||||
}
|
||||
|
||||
private void OnWorldUnloaded(WorldNode node)
|
||||
{
|
||||
SceneList.Remove(node);
|
||||
}
|
||||
|
||||
public void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
EditorWorldManager.OnWorldLoaded += OnWorldLoaded;
|
||||
EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
EditorWorldManager.OnWorldLoaded -= OnWorldLoaded;
|
||||
EditorWorldManager.OnWorldUnloaded -= OnWorldUnloaded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class InspectorViewModel(IInspectorService inspectorService) : ObservableObject, INavigationAware
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial IInspectable? Inspectable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
inspectorService.OnSelectionChanged += OnSelectionChanged;
|
||||
Inspectable = inspectorService.SelectedInspectable;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
inspectorService.OnSelectionChanged -= OnSelectionChanged;
|
||||
Inspectable = null;
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
Inspectable = inspectorService.SelectedInspectable;
|
||||
}
|
||||
}
|
||||
146
Ghost.Editor/ViewModels/Pages/EngineEditor/ProjectViewModel.cs
Normal file
146
Ghost.Editor/ViewModels/Pages/EngineEditor/ProjectViewModel.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class ProjectViewModel : ObservableObject
|
||||
{
|
||||
public ObservableCollection<ExplorerItem> SubDirectories
|
||||
{
|
||||
get;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ExplorerItem> DirectoryAssets
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ExplorerItem? SelectedDirectory
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ExplorerItem? SelectedAsset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ProjectViewModel()
|
||||
{
|
||||
if (ProjectService.CurrentProject.Metadata == null)
|
||||
{
|
||||
throw new InvalidOperationException("Current project is not set.");
|
||||
}
|
||||
|
||||
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER), true);
|
||||
LoadSubFolderRecursive(ref assetsRootItem);
|
||||
|
||||
SubDirectories.Add(assetsRootItem);
|
||||
}
|
||||
|
||||
private static void LoadSubFolderRecursive(ref ExplorerItem parentItem)
|
||||
{
|
||||
foreach (var directory in Directory.EnumerateDirectories(parentItem.FullName))
|
||||
{
|
||||
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
LoadSubFolderRecursive(ref item);
|
||||
|
||||
parentItem.Children ??= new();
|
||||
parentItem.Children.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<ExplorerItem?> FindNodeIterative(ExplorerItem root, Func<ExplorerItem, bool> predicate)
|
||||
{
|
||||
var stack = new Stack<ExplorerItem>();
|
||||
stack.Push(root);
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var node = stack.Pop();
|
||||
if (predicate(node))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.Children == null || node.Children.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var i = node.Children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
stack.Push(node.Children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void NavigateToDirectory(string? path)
|
||||
{
|
||||
App.Window?.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
DirectoryAssets.Clear();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var directory in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
DirectoryAssets.Add(directoryItem);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(path))
|
||||
{
|
||||
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false);
|
||||
DirectoryAssets.Add(fileItem);
|
||||
}
|
||||
|
||||
SelectedDirectory = await FindNodeIterative(SubDirectories[0], x => x.FullName == path);
|
||||
});
|
||||
}
|
||||
|
||||
public void OpenSelected()
|
||||
{
|
||||
if (SelectedAsset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedAsset.IsDirectory)
|
||||
{
|
||||
NavigateToDirectory(SelectedAsset.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetDatabase.OpenAsset(SelectedAsset.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedDirectoryChanged(ExplorerItem? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DirectoryAssets.Clear();
|
||||
NavigateToDirectory(value.FullName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Utilities;
|
||||
using Ghost.Engine.Resources;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class CreateProjectViewModel(NotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public ObservableCollection<TemplateData> templates = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial TemplateData? SelectedTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectLocation
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
templates.Clear();
|
||||
await foreach (var (path, info) in ProjectService.GetProjectTemplatesAsync())
|
||||
{
|
||||
templates.Add(new(path, info));
|
||||
}
|
||||
|
||||
SelectedTemplate = templates.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SelectionProjectLocation()
|
||||
{
|
||||
var folder = await SystemUtilities.OpenFolderPickerAsync();
|
||||
if (folder != null)
|
||||
{
|
||||
ProjectLocation = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CreateProject()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ProjectName)
|
||||
|| !Directory.Exists(ProjectLocation)
|
||||
|| !SelectedTemplate.HasValue)
|
||||
{
|
||||
notificationService.ShowNotification("Incorrect project info", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.s_engineVersion, SelectedTemplate.Value.directory);
|
||||
if (!result.success)
|
||||
{
|
||||
notificationService.ShowNotification(result.message, MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.value);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Ghost.Editor/ViewModels/Pages/Landing/OpenProjectViewModel.cs
Normal file
106
Ghost.Editor/ViewModels/Pages/Landing/OpenProjectViewModel.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class OpenProjectViewModel(ProjectService projectService, INotificationService _notificationService, AppStateMachine _stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public readonly ObservableCollection<ProjectMetadataInfo> projects = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility EmptyVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility DragVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void UpdateEmptyPlaceHolderVisibility()
|
||||
{
|
||||
EmptyVisibility = projects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
projects.Clear();
|
||||
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
||||
{
|
||||
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
projects.Add(new(projectInfo.MetadataPath, metadata));
|
||||
}
|
||||
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task ContentDrop(DataPackageView dataView)
|
||||
{
|
||||
var errorMessage = string.Empty;
|
||||
if (dataView.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
var items = await dataView.GetStorageItemsAsync();
|
||||
var rootFolder = items.OfType<StorageFolder>().FirstOrDefault();
|
||||
if (rootFolder != null)
|
||||
{
|
||||
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
||||
if (result.success)
|
||||
{
|
||||
projects.Add(result.value);
|
||||
goto CloseDropPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = result.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Unsupported data format. Please drop a folder containing a project.";
|
||||
}
|
||||
|
||||
_notificationService.ShowNotification(errorMessage, MessageType.Error);
|
||||
|
||||
CloseDropPanel:
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
public async Task LoadProject(ProjectMetadataInfo project)
|
||||
{
|
||||
try
|
||||
{
|
||||
project.Metadata.LastOpened = DateTime.Now;
|
||||
await ProjectService.CreateMetadataFileAsync(project.Path, project.Metadata);
|
||||
|
||||
await _stateService.TransitionToAsync(StateKey.EngineEditor, project);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user