From 4110c166cfe09119a196bda62c04fc1e5fd2c832 Mon Sep 17 00:00:00 2001 From: Misaki Date: Fri, 27 Jun 2025 20:02:02 +0900 Subject: [PATCH] Refactor Vector3Field and update project structure Changed Vector3Field.cs to derive from ValueControl 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. --- Ghost.App/Controls/BasicInput/Vector3Field.cs | 96 ------- Ghost.App/Controls/EditorControls.xaml | 9 - .../Controls/Internal/InternalControls.xaml | 7 - Ghost.App/Core/AssetHandle/Asset.cs | 24 -- Ghost.App/Core/Inspector/IComponentEditor.cs | 25 -- Ghost.App/Ghost.Editor.csproj | 203 --------------- Ghost.App/Models/MessageType.cs | 9 - Ghost.App/Resources/FileExtensions.cs | 11 - .../Services/Contracts/IInspectorService.cs | 14 -- .../Contracts/INotificationService.cs | 8 - .../Services/Contracts/IProgressService.cs | 9 - Ghost.Core/Ghost.Core.csproj | 9 + {Ghost.Data/Models => Ghost.Core}/Result.cs | 28 ++- .../Utilities => Ghost.Core}/TypeHandle.cs | 41 +-- Ghost.Data/AssemblyInfo.cs | 3 +- Ghost.Data/Ghost.Data.csproj | 4 + Ghost.Data/Services/ProjectService.cs | 7 +- .../AppState/AppStateMachine.cs | 0 .../AppState/IAppState.cs | 0 .../AppState/StateKey.cs | 0 .../AssemblyInfo.cs | 1 + .../AssetHandle/AssetDatabase.Meta.cs | 158 ++++++++++++ .../AssetHandle/AssetDatabase.Open.cs | 14 +- .../AssetHandle/AssetDatabase.cs | 35 +++ .../AssetHandle/AssetImporterAttribute.cs | 15 ++ Ghost.Editor.Core/AssetHandle/AssetMeta.cs | 16 ++ .../AssetHandle/AssetOpenHandlerAttribute .cs | 0 .../AssetHandle/ImporterSettings.cs | 5 + .../Contracts/INavigationAware.cs | 2 +- .../Controls/BasicInput/PropertyField.cs | 0 .../Controls/BasicInput/PropertyField.xaml | 0 .../Controls/BasicInput/Vector3Field.cs | 70 ++++++ .../Controls/BasicInput/Vector3Field.xaml | 6 +- .../Controls/ControlsDictionary.cs | 13 + .../Controls/ControlsDictionary.xaml | 10 + .../Controls/Internal/ComponentDataView.cs | 48 ++-- .../Controls/Internal/ComponentDataView.xaml | 0 .../Controls/Internal/NavigationTabView.cs | 7 +- .../Controls/Internal/NavigationTabView.xaml | 0 Ghost.Editor.Core/Controls/ValueControl.cs | 70 ++++++ .../Event/ValueChangedEventHandler.cs | 2 +- Ghost.Editor.Core/Ghost.Editor.Core.csproj | 48 ++++ .../Inspector/ComponentEditor.cs | 40 +++ .../Inspector/ComponentObject.cs | 0 .../Inspector/CustomEditorAttribute.cs | 0 .../Inspector/IInspectable.cs | 0 .../Inspector}/IInspectorService.cs | 4 +- .../Inspector}/InspectorService.cs | 5 +- .../Notifications}/INotificationService.cs | 5 +- .../Notifications}/MessageType.cs | 2 +- .../Notifications}/NotificationService.cs | 5 +- .../Progress}/IProgressService.cs | 2 +- .../Progress}/ProgressService.cs | 3 +- .../Resources/EditorIconSource.cs | 2 +- .../Resources/FileExtensions.cs | 2 +- .../Resources/StaticResource.cs | 2 +- .../SceneGraph/EditorWorldManager.cs | 5 +- .../SceneGraph/EntityNode.cs | 27 +- .../SceneGraph/SceneGraphHelpers.cs | 0 .../SceneGraph/SceneGraphNode.cs | 0 .../SceneGraph/WorldNode.cs | 2 +- .../Serializer/WorldNodeSerializer.cs | 3 +- .../Utilities/EditorApplication.cs | 26 ++ .../Utilities/TypeCache.cs | 4 +- .../ActivationHandler.cs | 6 +- {Ghost.App => Ghost.Editor}/App.xaml | 5 +- {Ghost.App => Ghost.Editor}/App.xaml.cs | 17 +- Ghost.Editor/AssemblyInfo.cs | 2 +- Ghost.Editor/AssetHandle/Asset.cs | 24 -- Ghost.Editor/AssetHandle/AssetDatabase.cs | 83 ------ .../AssetHandle/AssetOpenHandlerAttribute .cs | 29 --- ...on.altform-lightunplated_targetsize-16.png | Bin ...on.altform-lightunplated_targetsize-24.png | Bin ...n.altform-lightunplated_targetsize-256.png | Bin ...on.altform-lightunplated_targetsize-32.png | Bin ...on.altform-lightunplated_targetsize-48.png | Bin .../Icon.altform-unplated_targetsize-16.png | Bin .../Icon.altform-unplated_targetsize-24.png | Bin .../Icon.altform-unplated_targetsize-256.png | Bin .../Icon.altform-unplated_targetsize-32.png | Bin .../Icon.altform-unplated_targetsize-48.png | Bin .../Assets/Icon.scale-100.png | Bin .../Assets/Icon.scale-125.png | Bin .../Assets/Icon.scale-150.png | Bin .../Assets/Icon.scale-200.png | Bin .../Assets/Icon.scale-400.png | Bin .../Assets/Icon.targetsize-16.png | Bin ...on.targetsize-16_altform-lightunplated.png | Bin .../Icon.targetsize-16_altform-unplated.png | Bin .../Assets/Icon.targetsize-24.png | Bin ...on.targetsize-24_altform-lightunplated.png | Bin .../Icon.targetsize-24_altform-unplated.png | Bin .../Assets/Icon.targetsize-256.png | Bin ...n.targetsize-256_altform-lightunplated.png | Bin .../Icon.targetsize-256_altform-unplated.png | Bin .../Assets/Icon.targetsize-32.png | Bin ...on.targetsize-32_altform-lightunplated.png | Bin .../Icon.targetsize-32_altform-unplated.png | Bin .../Assets/Icon.targetsize-48.png | Bin ...on.targetsize-48_altform-lightunplated.png | Bin .../Icon.targetsize-48_altform-unplated.png | Bin .../Assets/LockScreenLogo.scale-200.png | Bin .../Assets/SplashScreen.scale-200.png | Bin .../Assets/Square150x150Logo.scale-200.png | Bin .../Assets/StoreLogo.png | Bin .../Assets/Wide310x150Logo.scale-200.png | Bin .../Assets/icon-256.ico | Bin .../Assets/icon-256.png | Bin .../Components/HierarchyEditor.cs | 2 +- .../Components/LocalToWorldEditor.cs | 39 ++- Ghost.Editor/Contracts/IInspectable.cs | 8 - .../Controls/ViewModelPage.cs | 2 +- .../Core/AppState/EditorState.cs | 25 +- .../Core/AppState/LandingState.cs | 12 +- Ghost.Editor/EditorApplication.cs | 29 --- Ghost.Editor/Ghost.Editor.csproj | 164 +++++++++++- .../Models/AssetItem.cs | 0 .../Models/ExplorerItem.cs | 0 Ghost.Editor/Models/WorldAsset.cs | 14 -- .../Package.appxmanifest | 0 .../Properties/launchSettings.json | 0 Ghost.Editor/SceneGraph/EditorWorldManager.cs | 53 ---- Ghost.Editor/SceneGraph/EntityNode.cs | 21 -- Ghost.Editor/SceneGraph/SceneGraphHelpers.cs | 112 --------- Ghost.Editor/SceneGraph/SceneGraphNode.cs | 54 ---- Ghost.Editor/SceneGraph/WorldNode.cs | 179 ------------- .../Serializer/WorldNodeSerializer.cs | 131 ---------- .../Themes/Override.xaml | 0 .../Converters/AssetPathToGlyphConverter.cs | 0 .../Converters/GetDirectoryNameConverter .cs | 0 .../Vector3ToQuaternionConverter.cs | 0 .../Utilities/HostHelpers.Page.cs | 2 + .../Utilities/ReflectionBinding.cs | 0 .../Utilities/SystemUtility.cs | 10 +- .../View/Pages/EngineEditor/ConsolePage.xaml | 0 .../Pages/EngineEditor/ConsolePage.xaml.cs | 2 +- .../Pages/EngineEditor/HierarchyPage.xaml | 0 .../Pages/EngineEditor/HierarchyPage.xaml.cs | 11 +- .../Pages/EngineEditor/InspectorPage.xaml | 0 .../Pages/EngineEditor/InspectorPage.xaml.cs | 9 +- .../View/Pages/EngineEditor/ProjectPage.xaml | 0 .../Pages/EngineEditor/ProjectPage.xaml.cs | 2 +- .../View/Pages/EngineEditor/ScenePage.xaml | 18 ++ .../View/Pages/EngineEditor/ScenePage.xaml.cs | 54 ++++ .../View/Pages/Landing/CreateProjectPage.xaml | 0 .../Pages/Landing/CreateProjectPage.xaml.cs | 2 +- .../View/Pages/Landing/OpenProjectPage.xaml | 0 .../Pages/Landing/OpenProjectPage.xaml.cs | 2 +- .../View/Windows/EngineEditorWindow.xaml | 24 +- .../View/Windows/EngineEditorWindow.xaml.cs | 12 +- .../View/Windows/LandingWindow.xaml | 0 .../View/Windows/LandingWindow.xaml.cs | 11 +- .../Pages/EngineEditor/ConsoleViewModel.cs | 0 .../Pages/EngineEditor/HierarchyViewModel.cs | 2 +- .../Pages/EngineEditor/InspectorViewModel.cs | 3 +- .../Pages/EngineEditor/ProjectViewModel.cs | 2 +- .../Pages/Landing/CreateProjectViewModel.cs | 10 +- .../Pages/Landing/OpenProjectViewModel.cs | 7 +- .../Windows/EngineEditorViewModel.cs | 0 {Ghost.App => Ghost.Editor}/app.manifest | 0 Ghost.Engine/EngineCore.cs | 16 +- Ghost.Engine/Ghost.Engine.csproj | 7 + Ghost.Engine/Models/LogMessage.cs | 4 +- Ghost.Engine/Services/Logger.cs | 18 +- Ghost.Entities/AssemblyInfo.cs | 2 +- Ghost.Entities/Components/ComponentStorage.cs | 18 +- Ghost.Entities/EntityManager.cs | 9 +- Ghost.Entities/Ghost.Entities.csproj | 4 + Ghost.Entities/Query/QueryFilter.cs | 35 +-- Ghost.Entities/Template/QueryEnumerable.cs | 26 +- Ghost.Entities/Template/QueryEnumerable.tt | 5 +- Ghost.Entities/World.cs | 17 ++ Ghost.Graphics/AssemblyInfo.cs | 6 + Ghost.Graphics/Class1.cs | 6 - Ghost.Graphics/Contracts/IDebugLayer.cs | 5 + Ghost.Graphics/Contracts/IGraphicsDevice.cs | 11 + Ghost.Graphics/Contracts/IRenderView.cs | 11 + Ghost.Graphics/DX12/DX12DebugLayer.cs | 36 +++ Ghost.Graphics/DX12/DX12GraphicsDevice.cs | 117 +++++++++ Ghost.Graphics/DX12/DX12RenderView.cs | 219 ++++++++++++++++ .../Utilities/D3D12DescriptorAllocator.cs | 238 ++++++++++++++++++ Ghost.Graphics/Data/Camera.cs | 5 + Ghost.Graphics/Data/Color32.cs | 27 ++ Ghost.Graphics/Data/GraphicsAPI.cs | 6 + Ghost.Graphics/Data/Mesh.cs | 234 +++++++++++++++++ Ghost.Graphics/Data/SwapChainSurface.cs | 53 ++++ Ghost.Graphics/Ghost.Graphics.csproj | 12 + Ghost.Graphics/GraphicsPipeline.cs | 77 ++++++ Ghost.Graphics/Utilities/MeshBuilder.cs | 126 ++++++++++ Ghost.UnitTest/Ghost.UnitTest.csproj | 8 +- GhostEngine.sln | 30 ++- 191 files changed, 2286 insertions(+), 1462 deletions(-) delete mode 100644 Ghost.App/Controls/BasicInput/Vector3Field.cs delete mode 100644 Ghost.App/Controls/EditorControls.xaml delete mode 100644 Ghost.App/Controls/Internal/InternalControls.xaml delete mode 100644 Ghost.App/Core/AssetHandle/Asset.cs delete mode 100644 Ghost.App/Core/Inspector/IComponentEditor.cs delete mode 100644 Ghost.App/Ghost.Editor.csproj delete mode 100644 Ghost.App/Models/MessageType.cs delete mode 100644 Ghost.App/Resources/FileExtensions.cs delete mode 100644 Ghost.App/Services/Contracts/IInspectorService.cs delete mode 100644 Ghost.App/Services/Contracts/INotificationService.cs delete mode 100644 Ghost.App/Services/Contracts/IProgressService.cs create mode 100644 Ghost.Core/Ghost.Core.csproj rename {Ghost.Data/Models => Ghost.Core}/Result.cs (57%) rename {Ghost.Entities/Utilities => Ghost.Core}/TypeHandle.cs (55%) rename {Ghost.App/Core => Ghost.Editor.Core}/AppState/AppStateMachine.cs (100%) rename {Ghost.App/Core => Ghost.Editor.Core}/AppState/IAppState.cs (100%) rename {Ghost.App/Core => Ghost.Editor.Core}/AppState/StateKey.cs (100%) rename {Ghost.App => Ghost.Editor.Core}/AssemblyInfo.cs (66%) create mode 100644 Ghost.Editor.Core/AssetHandle/AssetDatabase.Meta.cs rename Ghost.App/Core/AssetHandle/AssetDatabase.cs => Ghost.Editor.Core/AssetHandle/AssetDatabase.Open.cs (87%) create mode 100644 Ghost.Editor.Core/AssetHandle/AssetDatabase.cs create mode 100644 Ghost.Editor.Core/AssetHandle/AssetImporterAttribute.cs create mode 100644 Ghost.Editor.Core/AssetHandle/AssetMeta.cs rename {Ghost.App/Core => Ghost.Editor.Core}/AssetHandle/AssetOpenHandlerAttribute .cs (100%) create mode 100644 Ghost.Editor.Core/AssetHandle/ImporterSettings.cs rename {Ghost.App => Ghost.Editor.Core}/Contracts/INavigationAware.cs (73%) rename {Ghost.App => Ghost.Editor.Core}/Controls/BasicInput/PropertyField.cs (100%) rename {Ghost.App => Ghost.Editor.Core}/Controls/BasicInput/PropertyField.xaml (100%) create mode 100644 Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs rename {Ghost.App => Ghost.Editor.Core}/Controls/BasicInput/Vector3Field.xaml (82%) create mode 100644 Ghost.Editor.Core/Controls/ControlsDictionary.cs create mode 100644 Ghost.Editor.Core/Controls/ControlsDictionary.xaml rename {Ghost.App => Ghost.Editor.Core}/Controls/Internal/ComponentDataView.cs (72%) rename {Ghost.App => Ghost.Editor.Core}/Controls/Internal/ComponentDataView.xaml (100%) rename {Ghost.App => Ghost.Editor.Core}/Controls/Internal/NavigationTabView.cs (77%) rename {Ghost.App => Ghost.Editor.Core}/Controls/Internal/NavigationTabView.xaml (100%) create mode 100644 Ghost.Editor.Core/Controls/ValueControl.cs rename {Ghost.App => Ghost.Editor.Core}/Event/ValueChangedEventHandler.cs (90%) create mode 100644 Ghost.Editor.Core/Ghost.Editor.Core.csproj create mode 100644 Ghost.Editor.Core/Inspector/ComponentEditor.cs rename {Ghost.App/Core => Ghost.Editor.Core}/Inspector/ComponentObject.cs (100%) rename {Ghost.App/Core => Ghost.Editor.Core}/Inspector/CustomEditorAttribute.cs (100%) rename {Ghost.App/Core => Ghost.Editor.Core}/Inspector/IInspectable.cs (100%) rename {Ghost.Editor/Services/Contracts => Ghost.Editor.Core/Inspector}/IInspectorService.cs (68%) rename {Ghost.App/Services => Ghost.Editor.Core/Inspector}/InspectorService.cs (75%) rename {Ghost.Editor/Services/Contracts => Ghost.Editor.Core/Notifications}/INotificationService.cs (51%) rename {Ghost.Editor/Models => Ghost.Editor.Core/Notifications}/MessageType.cs (63%) rename {Ghost.App/Services => Ghost.Editor.Core/Notifications}/NotificationService.cs (91%) rename {Ghost.Editor/Services/Contracts => Ghost.Editor.Core/Progress}/IProgressService.cs (83%) rename {Ghost.App/Services => Ghost.Editor.Core/Progress}/ProgressService.cs (96%) rename {Ghost.App => Ghost.Editor.Core}/Resources/EditorIconSource.cs (89%) rename {Ghost.Editor => Ghost.Editor.Core}/Resources/FileExtensions.cs (90%) rename {Ghost.App => Ghost.Editor.Core}/Resources/StaticResource.cs (82%) rename {Ghost.App/Core => Ghost.Editor.Core}/SceneGraph/EditorWorldManager.cs (93%) rename {Ghost.App/Core => Ghost.Editor.Core}/SceneGraph/EntityNode.cs (82%) rename {Ghost.App/Core => Ghost.Editor.Core}/SceneGraph/SceneGraphHelpers.cs (100%) rename {Ghost.App/Core => Ghost.Editor.Core}/SceneGraph/SceneGraphNode.cs (100%) rename {Ghost.App/Core => Ghost.Editor.Core}/SceneGraph/WorldNode.cs (99%) rename {Ghost.App/Core => Ghost.Editor.Core}/Serializer/WorldNodeSerializer.cs (97%) create mode 100644 Ghost.Editor.Core/Utilities/EditorApplication.cs rename {Ghost.App => Ghost.Editor.Core}/Utilities/TypeCache.cs (86%) rename {Ghost.App => Ghost.Editor}/ActivationHandler.cs (85%) rename {Ghost.App => Ghost.Editor}/App.xaml (82%) rename {Ghost.App => Ghost.Editor}/App.xaml.cs (86%) delete mode 100644 Ghost.Editor/AssetHandle/Asset.cs delete mode 100644 Ghost.Editor/AssetHandle/AssetDatabase.cs delete mode 100644 Ghost.Editor/AssetHandle/AssetOpenHandlerAttribute .cs rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-lightunplated_targetsize-16.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-lightunplated_targetsize-24.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-lightunplated_targetsize-256.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-lightunplated_targetsize-32.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-lightunplated_targetsize-48.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-unplated_targetsize-16.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-unplated_targetsize-24.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-unplated_targetsize-256.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-unplated_targetsize-32.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.altform-unplated_targetsize-48.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.scale-100.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.scale-125.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.scale-150.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.scale-200.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.scale-400.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-16.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-16_altform-lightunplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-16_altform-unplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-24.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-24_altform-lightunplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-24_altform-unplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-256.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-256_altform-lightunplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-256_altform-unplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-32.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-32_altform-lightunplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-32_altform-unplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-48.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-48_altform-lightunplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Icon.targetsize-48_altform-unplated.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/LockScreenLogo.scale-200.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/SplashScreen.scale-200.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Square150x150Logo.scale-200.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/StoreLogo.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/Wide310x150Logo.scale-200.png (100%) rename {Ghost.App => Ghost.Editor}/Assets/icon-256.ico (100%) rename {Ghost.App => Ghost.Editor}/Assets/icon-256.png (100%) rename {Ghost.App => Ghost.Editor}/Components/HierarchyEditor.cs (88%) rename {Ghost.App => Ghost.Editor}/Components/LocalToWorldEditor.cs (57%) delete mode 100644 Ghost.Editor/Contracts/IInspectable.cs rename {Ghost.App => Ghost.Editor}/Controls/ViewModelPage.cs (95%) rename {Ghost.App => Ghost.Editor}/Core/AppState/EditorState.cs (69%) rename {Ghost.App => Ghost.Editor}/Core/AppState/LandingState.cs (69%) delete mode 100644 Ghost.Editor/EditorApplication.cs rename {Ghost.App => Ghost.Editor}/Models/AssetItem.cs (100%) rename {Ghost.App => Ghost.Editor}/Models/ExplorerItem.cs (100%) delete mode 100644 Ghost.Editor/Models/WorldAsset.cs rename {Ghost.App => Ghost.Editor}/Package.appxmanifest (100%) rename {Ghost.App => Ghost.Editor}/Properties/launchSettings.json (100%) delete mode 100644 Ghost.Editor/SceneGraph/EditorWorldManager.cs delete mode 100644 Ghost.Editor/SceneGraph/EntityNode.cs delete mode 100644 Ghost.Editor/SceneGraph/SceneGraphHelpers.cs delete mode 100644 Ghost.Editor/SceneGraph/SceneGraphNode.cs delete mode 100644 Ghost.Editor/SceneGraph/WorldNode.cs delete mode 100644 Ghost.Editor/Serializer/WorldNodeSerializer.cs rename {Ghost.App => Ghost.Editor}/Themes/Override.xaml (100%) rename {Ghost.App => Ghost.Editor}/Utilities/Converters/AssetPathToGlyphConverter.cs (100%) rename {Ghost.App => Ghost.Editor}/Utilities/Converters/GetDirectoryNameConverter .cs (100%) rename {Ghost.App => Ghost.Editor}/Utilities/Converters/Vector3ToQuaternionConverter.cs (100%) rename {Ghost.App => Ghost.Editor}/Utilities/HostHelpers.Page.cs (97%) rename {Ghost.App => Ghost.Editor}/Utilities/ReflectionBinding.cs (100%) rename {Ghost.App => Ghost.Editor}/Utilities/SystemUtility.cs (82%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/ConsolePage.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/ConsolePage.xaml.cs (81%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/HierarchyPage.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/HierarchyPage.xaml.cs (85%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/InspectorPage.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/InspectorPage.xaml.cs (70%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/ProjectPage.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Pages/EngineEditor/ProjectPage.xaml.cs (87%) create mode 100644 Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml create mode 100644 Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml.cs rename {Ghost.App => Ghost.Editor}/View/Pages/Landing/CreateProjectPage.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Pages/Landing/CreateProjectPage.xaml.cs (87%) rename {Ghost.App => Ghost.Editor}/View/Pages/Landing/OpenProjectPage.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Pages/Landing/OpenProjectPage.xaml.cs (96%) rename {Ghost.App => Ghost.Editor}/View/Windows/EngineEditorWindow.xaml (92%) rename {Ghost.App => Ghost.Editor}/View/Windows/EngineEditorWindow.xaml.cs (79%) rename {Ghost.App => Ghost.Editor}/View/Windows/LandingWindow.xaml (100%) rename {Ghost.App => Ghost.Editor}/View/Windows/LandingWindow.xaml.cs (86%) rename {Ghost.App => Ghost.Editor}/ViewModels/Pages/EngineEditor/ConsoleViewModel.cs (100%) rename {Ghost.App => Ghost.Editor}/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs (96%) rename {Ghost.App => Ghost.Editor}/ViewModels/Pages/EngineEditor/InspectorViewModel.cs (92%) rename {Ghost.App => Ghost.Editor}/ViewModels/Pages/EngineEditor/ProjectViewModel.cs (98%) rename {Ghost.App => Ghost.Editor}/ViewModels/Pages/Landing/CreateProjectViewModel.cs (93%) rename {Ghost.App => Ghost.Editor}/ViewModels/Pages/Landing/OpenProjectViewModel.cs (95%) rename {Ghost.App => Ghost.Editor}/ViewModels/Windows/EngineEditorViewModel.cs (100%) rename {Ghost.App => Ghost.Editor}/app.manifest (100%) create mode 100644 Ghost.Graphics/AssemblyInfo.cs delete mode 100644 Ghost.Graphics/Class1.cs create mode 100644 Ghost.Graphics/Contracts/IDebugLayer.cs create mode 100644 Ghost.Graphics/Contracts/IGraphicsDevice.cs create mode 100644 Ghost.Graphics/Contracts/IRenderView.cs create mode 100644 Ghost.Graphics/DX12/DX12DebugLayer.cs create mode 100644 Ghost.Graphics/DX12/DX12GraphicsDevice.cs create mode 100644 Ghost.Graphics/DX12/DX12RenderView.cs create mode 100644 Ghost.Graphics/DX12/Utilities/D3D12DescriptorAllocator.cs create mode 100644 Ghost.Graphics/Data/Camera.cs create mode 100644 Ghost.Graphics/Data/Color32.cs create mode 100644 Ghost.Graphics/Data/GraphicsAPI.cs create mode 100644 Ghost.Graphics/Data/Mesh.cs create mode 100644 Ghost.Graphics/Data/SwapChainSurface.cs create mode 100644 Ghost.Graphics/GraphicsPipeline.cs create mode 100644 Ghost.Graphics/Utilities/MeshBuilder.cs diff --git a/Ghost.App/Controls/BasicInput/Vector3Field.cs b/Ghost.App/Controls/BasicInput/Vector3Field.cs deleted file mode 100644 index fa31410..0000000 --- a/Ghost.App/Controls/BasicInput/Vector3Field.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Ghost.Editor.Event; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using System.Numerics; - -namespace Ghost.Editor.Controls; - -// TODO: value update event -public sealed partial class Vector3Field : Control -{ - private bool _suppressCallback; - - public Vector3 Value - { - get => new((float)X, (float)Y, (float)Z); - set - { - if (value == Value) - { - return; - } - - _suppressCallback = true; - - X = value.X; - Y = value.Y; - Z = value.Z; - - _suppressCallback = false; - } - } - - public double X - { - get => (double)GetValue(XProperty); - set => SetValue(XProperty, value); - } - - public static readonly DependencyProperty XProperty = - DependencyProperty.Register(nameof(X), typeof(double), typeof(Vector3Field), new PropertyMetadata(0.0, ValueChanged)); - - public double Y - { - get => (double)GetValue(YProperty); - set => SetValue(YProperty, value); - } - - public static readonly DependencyProperty YProperty = - DependencyProperty.Register(nameof(Y), typeof(double), typeof(Vector3Field), new PropertyMetadata(0.0, ValueChanged)); - - public double Z - { - get => (double)GetValue(ZProperty); - set => SetValue(ZProperty, value); - } - - public static readonly DependencyProperty ZProperty = - DependencyProperty.Register(nameof(Z), typeof(double), typeof(Vector3Field), new PropertyMetadata(0.0, ValueChanged)); - - public event ValueChangedEventHandler? OnValueChanged; - - private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is Vector3Field vector3Field) - { - if (vector3Field._suppressCallback) - { - return; - } - - var oldValue = vector3Field.Value; - if (e.Property == XProperty) - { - var f = (float)(double)e.OldValue; - oldValue.X = f; - } - else if (e.Property == YProperty) - { - var f = (float)(double)e.OldValue; - oldValue.Y = f; - } - else if (e.Property == ZProperty) - { - var f = (float)(double)e.OldValue; - oldValue.Z = f; - } - - vector3Field.OnValueChanged?.Invoke(vector3Field, new ValueChangedEventArgs(oldValue, vector3Field.Value)); - } - } - - public Vector3Field() - { - DefaultStyleKey = typeof(Vector3Field); - } -} \ No newline at end of file diff --git a/Ghost.App/Controls/EditorControls.xaml b/Ghost.App/Controls/EditorControls.xaml deleted file mode 100644 index b2f05a0..0000000 --- a/Ghost.App/Controls/EditorControls.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Ghost.App/Controls/Internal/InternalControls.xaml b/Ghost.App/Controls/Internal/InternalControls.xaml deleted file mode 100644 index b543b4e..0000000 --- a/Ghost.App/Controls/Internal/InternalControls.xaml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Ghost.App/Core/AssetHandle/Asset.cs b/Ghost.App/Core/AssetHandle/Asset.cs deleted file mode 100644 index b29a045..0000000 --- a/Ghost.App/Core/AssetHandle/Asset.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Ghost.Editor.Core.AssetHandle; - -public abstract class Asset -{ - /// - /// Get the Guid of the asset. - /// - public Guid GUID - { - get; - } = Guid.NewGuid(); - - /// - /// True if the asset is a folder, false if it is a file. - /// - public bool IsFolder - { - get; - } - - internal void GenerateMetadata() - { - } -} \ No newline at end of file diff --git a/Ghost.App/Core/Inspector/IComponentEditor.cs b/Ghost.App/Core/Inspector/IComponentEditor.cs deleted file mode 100644 index 9ce7849..0000000 --- a/Ghost.App/Core/Inspector/IComponentEditor.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.UI.Xaml.Controls; - -namespace Ghost.Editor.Core.Inspector; - -interface IComponentEditor -{ - /// - /// Called when the component editor is created. - /// - /// The component data to edit. - /// The container to add the editor controls to. - public void Create(ComponentObject componentObject, StackPanel container); - - /// - /// Called when the component editor needs to update its UI based on the current state of the component data. - /// - /// The component data to edit. - public void Update(ComponentObject componentObject); - - /// - /// Called when the component editor is destroyed. - /// - /// The component data to edit. - public void Destroy(ComponentObject componentObject); -} \ No newline at end of file diff --git a/Ghost.App/Ghost.Editor.csproj b/Ghost.App/Ghost.Editor.csproj deleted file mode 100644 index 3e316a6..0000000 --- a/Ghost.App/Ghost.Editor.csproj +++ /dev/null @@ -1,203 +0,0 @@ - - - WinExe - net9.0-windows10.0.22621.0 - 10.0.17763.0 - x86;x64;ARM64 - win-x86;win-x64;win-arm64 - win-$(Platform).pubxml - true - true - preview - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - - - true - - - - - False - True - True - enable - 10.0.20348.0 - app.manifest - False - False - Ghost.Editor - enable - - \ No newline at end of file diff --git a/Ghost.App/Models/MessageType.cs b/Ghost.App/Models/MessageType.cs deleted file mode 100644 index c559200..0000000 --- a/Ghost.App/Models/MessageType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ghost.Editor.Models; - -public enum MessageType -{ - Informational, - Success, - Warning, - Error -} \ No newline at end of file diff --git a/Ghost.App/Resources/FileExtensions.cs b/Ghost.App/Resources/FileExtensions.cs deleted file mode 100644 index 9b38981..0000000 --- a/Ghost.App/Resources/FileExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ghost.Editor.Resources; - -internal static class FileExtensions -{ - public const string PROJECT_FILE_EXTENSION = ".ghostproj"; - public const string TEMPLATE_FILE_EXTENSION = ".ghosttemplate"; - public const string SCENE_FILE_EXTENSION = ".ghostscene"; - public const string ASSET_FILE_EXTENSION = ".ghostasset"; - public const string SHADER_FILE_EXTENSION = ".ghostshader"; - public const string MATERIAL_FILE_EXTENSION = ".ghostmaterial"; -} \ No newline at end of file diff --git a/Ghost.App/Services/Contracts/IInspectorService.cs b/Ghost.App/Services/Contracts/IInspectorService.cs deleted file mode 100644 index 8dccc97..0000000 --- a/Ghost.App/Services/Contracts/IInspectorService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Ghost.Editor.Core.Inspector; - -namespace Ghost.Editor.Services.Contracts; - -internal interface IInspectorService -{ - public IInspectable? SelectedInspectable - { - get; - set; - } - - public event Action? OnSelectionChanged; -} \ No newline at end of file diff --git a/Ghost.App/Services/Contracts/INotificationService.cs b/Ghost.App/Services/Contracts/INotificationService.cs deleted file mode 100644 index cef71e7..0000000 --- a/Ghost.App/Services/Contracts/INotificationService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Ghost.Editor.Models; - -namespace Ghost.Editor.Services.Contracts; - -public interface INotificationService -{ - public void ShowNotification(string? message, MessageType type, int duration = 5, string? title = null); -} diff --git a/Ghost.App/Services/Contracts/IProgressService.cs b/Ghost.App/Services/Contracts/IProgressService.cs deleted file mode 100644 index fce0331..0000000 --- a/Ghost.App/Services/Contracts/IProgressService.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ghost.Editor.Services.Contracts; - -public interface IProgressService -{ - public void ShowProgress(string message, double progress = 0.0); - public void ShowIndeterminateProgress(string message); - public void SetProgress(double progress); - public void HideProgress(); -} \ No newline at end of file diff --git a/Ghost.Core/Ghost.Core.csproj b/Ghost.Core/Ghost.Core.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/Ghost.Core/Ghost.Core.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/Ghost.Data/Models/Result.cs b/Ghost.Core/Result.cs similarity index 57% rename from Ghost.Data/Models/Result.cs rename to Ghost.Core/Result.cs index 134cca9..d4c8d55 100644 --- a/Ghost.Data/Models/Result.cs +++ b/Ghost.Core/Result.cs @@ -1,4 +1,4 @@ -namespace Ghost.Data.Models; +namespace Ghost.Core; public readonly struct Result { @@ -22,20 +22,28 @@ public readonly struct Result return new Result(false, message); } + public void CheckSuccess() + { + if (!success) + { + throw new InvalidOperationException($"Operation failed: {message}"); + } + } + public override string ToString() => success ? "OK" : $"Error: {message}"; } public readonly struct Result { public readonly bool success; - public readonly T? data; + public readonly T value; public readonly string? message; - public Result(bool success, T? data, string? message = null) + public Result(bool success, T data, string? message = null) { this.success = success; - this.data = data; + this.value = data; this.message = message; } @@ -46,8 +54,16 @@ public readonly struct Result public static Result Error(string? message) { - return new Result(false, default, message); + return new Result(false, default!, message); } - public override string ToString() => success ? $"OK: {data}" : $"Error: {message}"; + public void CheckSuccess() + { + if (!success) + { + throw new InvalidOperationException($"Operation failed: {message}"); + } + } + + public override string ToString() => success ? $"OK: {value}" : $"Error: {message}"; } diff --git a/Ghost.Entities/Utilities/TypeHandle.cs b/Ghost.Core/TypeHandle.cs similarity index 55% rename from Ghost.Entities/Utilities/TypeHandle.cs rename to Ghost.Core/TypeHandle.cs index 2332f89..02b1050 100644 --- a/Ghost.Entities/Utilities/TypeHandle.cs +++ b/Ghost.Core/TypeHandle.cs @@ -1,18 +1,17 @@ using System.Runtime.CompilerServices; -namespace Ghost.Entities.Utilities; +namespace Ghost.Core; -internal static class TypeHandle +public readonly struct TypeHandle { - /// - /// Gets the type handle for the specified type. - /// - /// The type to get the handle for. - /// The type handle as a nint. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint Get() + public readonly IntPtr Value { - return typeof(T).TypeHandle.Value; + get; + } + + private TypeHandle(IntPtr value) + { + Value = value; } /// @@ -21,13 +20,23 @@ internal static class TypeHandle /// The type to get the handle for. /// The type handle as a nint. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint Get(Type type) - { - return type.TypeHandle.Value; - } + public static TypeHandle Get(Type type) => new TypeHandle(type.TypeHandle.Value); - public static Type? ToType(nint handle) + /// + /// Gets the type handle for the specified type. + /// + /// The type to get the handle for. + /// The type handle as a nint. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TypeHandle Get() => Get(typeof(T)); + + /// + /// Converts a TypeHandle to a Type. + /// + /// The TypeHandle to convert. + /// The corresponding Type. + public Type? ToType() { - return Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(handle)); + return Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(Value)); } } \ No newline at end of file diff --git a/Ghost.Data/AssemblyInfo.cs b/Ghost.Data/AssemblyInfo.cs index ef8099a..af1d141 100644 --- a/Ghost.Data/AssemblyInfo.cs +++ b/Ghost.Data/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Ghost.Editor")] \ No newline at end of file +[assembly: InternalsVisibleTo("Ghost.Editor")] +[assembly: InternalsVisibleTo("Ghost.Editor.Core")] \ No newline at end of file diff --git a/Ghost.Data/Ghost.Data.csproj b/Ghost.Data/Ghost.Data.csproj index d2c294e..e09919d 100644 --- a/Ghost.Data/Ghost.Data.csproj +++ b/Ghost.Data/Ghost.Data.csproj @@ -26,4 +26,8 @@ + + + + diff --git a/Ghost.Data/Services/ProjectService.cs b/Ghost.Data/Services/ProjectService.cs index fdb5bd7..11ccaa3 100644 --- a/Ghost.Data/Services/ProjectService.cs +++ b/Ghost.Data/Services/ProjectService.cs @@ -1,4 +1,5 @@ -using Ghost.Data.Models; +using Ghost.Core; +using Ghost.Data.Models; using Ghost.Data.Repository; using Ghost.Data.Resources; using System.IO.Compression; @@ -197,12 +198,12 @@ internal partial class ProjectService return result; } - if (await HasProjectAsync(result.data.Path)) + if (await HasProjectAsync(result.value.Path)) { return Result.Error("Project already exists."); } - await AddProjectAsync(result.data.Metadata.Name, result.data.Path); + await AddProjectAsync(result.value.Metadata.Name, result.value.Path); return result; } diff --git a/Ghost.App/Core/AppState/AppStateMachine.cs b/Ghost.Editor.Core/AppState/AppStateMachine.cs similarity index 100% rename from Ghost.App/Core/AppState/AppStateMachine.cs rename to Ghost.Editor.Core/AppState/AppStateMachine.cs diff --git a/Ghost.App/Core/AppState/IAppState.cs b/Ghost.Editor.Core/AppState/IAppState.cs similarity index 100% rename from Ghost.App/Core/AppState/IAppState.cs rename to Ghost.Editor.Core/AppState/IAppState.cs diff --git a/Ghost.App/Core/AppState/StateKey.cs b/Ghost.Editor.Core/AppState/StateKey.cs similarity index 100% rename from Ghost.App/Core/AppState/StateKey.cs rename to Ghost.Editor.Core/AppState/StateKey.cs diff --git a/Ghost.App/AssemblyInfo.cs b/Ghost.Editor.Core/AssemblyInfo.cs similarity index 66% rename from Ghost.App/AssemblyInfo.cs rename to Ghost.Editor.Core/AssemblyInfo.cs index d3b1fb4..c790e7f 100644 --- a/Ghost.App/AssemblyInfo.cs +++ b/Ghost.Editor.Core/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Ghost.UnitTest")] +[assembly: InternalsVisibleTo("Ghost.Editor")] \ No newline at end of file diff --git a/Ghost.Editor.Core/AssetHandle/AssetDatabase.Meta.cs b/Ghost.Editor.Core/AssetHandle/AssetDatabase.Meta.cs new file mode 100644 index 0000000..a28eb0a --- /dev/null +++ b/Ghost.Editor.Core/AssetHandle/AssetDatabase.Meta.cs @@ -0,0 +1,158 @@ +using Ghost.Core; +using Ghost.Editor.Core.Utilities; +using Ghost.Engine.Services; +using System.Reflection; +using System.Text.Json; + +namespace Ghost.Editor.Core.AssetHandle; +public static partial class AssetDatabase +{ + private static readonly Dictionary _importerTypeLookup = new(); + + private static void InitializeMetaData() + { + if (_watcher == null) + { + throw new InvalidOperationException("AssetDatabase is not initialized. Ensure that Initialize() is called before registering asset importers."); + } + + var importerTypes = TypeCache.GetTypes().Where(t => t.GetCustomAttribute() != null); + foreach (var type in importerTypes) + { + var attribute = type.GetCustomAttribute()!; + foreach (var extension in attribute.SupportedExtensions) + { + _importerTypeLookup[extension] = type; + } + } + + _watcher.Created += OnAssetCreated; + _watcher.Deleted += OnAssetDeleted; + _watcher.Renamed += OnAssetRenamed; + } + + private static Result GetMetaFilePath(string assetPath) + { + if (Directory.Exists(assetPath)) + { + return Result.Error("Folder does not have meta data"); + } + + if (Path.GetExtension(assetPath).Equals(".meta", StringComparison.OrdinalIgnoreCase)) + { + return Result.Error("Asset path cannot be a meta file"); + } + + return Result.OK(assetPath + ".meta"); + } + + private static ImporterSettings? GetDefaultSettingsForAsset(string assetPath) + { + var extension = Path.GetExtension(assetPath); + + if (_importerTypeLookup.TryGetValue(extension, out var importerType)) + { + var settingsType = importerType.BaseType?.GetGenericArguments()[0]; + if (settingsType == null || !typeof(ImporterSettings).IsAssignableFrom(settingsType)) + { + return null; + } + + return (ImporterSettings?)Activator.CreateInstance(settingsType); + } + + return null; + } + + private static void WriteMetaFile(string metaFilePath, AssetMeta metaData) + { + using var fileStream = File.Create(metaFilePath); + + try + { + JsonSerializer.Serialize(fileStream, metaData); + } + catch (Exception ex) + { + Logger.LogError(ex); + } + } + + internal static void GenerateMetaFile(string assetPath) + { + var metaFileResult = GetMetaFilePath(assetPath); + if (!metaFileResult.success) + { + Logger.LogError(metaFileResult.message); + return; + } + + if (File.Exists(metaFileResult.value)) + { + var existingMeta = JsonSerializer.Deserialize(File.ReadAllText(metaFileResult.value)); + if (existingMeta != null && _assetPathLookup.TryGetValue(existingMeta.Guid, out var path)) + { + if (assetPath != path) + { + existingMeta.Guid = Guid.NewGuid(); + WriteMetaFile(metaFileResult.value, existingMeta); + } + } + + return; + } + + var defaultSettings = GetDefaultSettingsForAsset(assetPath); + var metaData = new AssetMeta + { + Guid = Guid.NewGuid(), + Settings = defaultSettings + }; + + WriteMetaFile(metaFileResult.value, metaData); + } + + private static void OnAssetCreated(object sender, FileSystemEventArgs e) + { + GenerateMetaFile(e.FullPath); + } + + private static void OnAssetDeleted(object sender, FileSystemEventArgs e) + { + var metaFileResult = GetMetaFilePath(e.FullPath); + if (metaFileResult.success && File.Exists(metaFileResult.value)) + { + try + { + var meta = JsonSerializer.Deserialize(File.ReadAllText(metaFileResult.value)); + if (meta != null + && _assetPathLookup.TryGetValue(meta.Guid, out var path) + && path == e.FullPath) + { + _assetPathLookup.Remove(meta.Guid); + } + + File.Delete(metaFileResult.value); + } + catch (Exception ex) + { + Logger.LogError(ex); + } + } + } + + private static void OnAssetRenamed(object sender, RenamedEventArgs e) + { + var oldMetaPath = e.OldFullPath + ".meta"; + var newMetaPath = e.FullPath + ".meta"; + + if (File.Exists(oldMetaPath)) + { + File.Move(oldMetaPath, newMetaPath); + } + else + { + GenerateMetaFile(e.FullPath); + } + } +} \ No newline at end of file diff --git a/Ghost.App/Core/AssetHandle/AssetDatabase.cs b/Ghost.Editor.Core/AssetHandle/AssetDatabase.Open.cs similarity index 87% rename from Ghost.App/Core/AssetHandle/AssetDatabase.cs rename to Ghost.Editor.Core/AssetHandle/AssetDatabase.Open.cs index 3714b67..8d180a6 100644 --- a/Ghost.App/Core/AssetHandle/AssetDatabase.cs +++ b/Ghost.Editor.Core/AssetHandle/AssetDatabase.Open.cs @@ -3,21 +3,11 @@ using System.Reflection; namespace Ghost.Editor.Core.AssetHandle; -public static class AssetDatabase +public static partial class AssetDatabase { private static readonly Dictionary> _assetOpenHandlers = new(StringComparer.OrdinalIgnoreCase); - static AssetDatabase() - { - Initialize(); - } - - internal static void Initialize() - { - RegisterAssetHandles(); - } - - private static void RegisterAssetHandles() + private static void InitializeAssetHandle() { var methods = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) diff --git a/Ghost.Editor.Core/AssetHandle/AssetDatabase.cs b/Ghost.Editor.Core/AssetHandle/AssetDatabase.cs new file mode 100644 index 0000000..1eb5502 --- /dev/null +++ b/Ghost.Editor.Core/AssetHandle/AssetDatabase.cs @@ -0,0 +1,35 @@ +using Ghost.Data.Services; + +namespace Ghost.Editor.Core.AssetHandle; + +public static partial class AssetDatabase +{ + private static FileSystemWatcher? _watcher; + + private static readonly Dictionary _assetPathLookup = new(); + + public static DirectoryInfo? AssetsDirectory + { + get; + private set; + } + + internal static void Initialize() + { + if (ProjectService.CurrentProject.Metadata == null) + { + throw new InvalidOperationException("Project metadata is not initialized. Ensure that the project is loaded before accessing the AssetDatabase."); + } + + AssetsDirectory = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER)); + _watcher = new FileSystemWatcher + { + Path = AssetsDirectory.FullName, + IncludeSubdirectories = true, + EnableRaisingEvents = true + }; + + InitializeAssetHandle(); + InitializeMetaData(); + } +} \ No newline at end of file diff --git a/Ghost.Editor.Core/AssetHandle/AssetImporterAttribute.cs b/Ghost.Editor.Core/AssetHandle/AssetImporterAttribute.cs new file mode 100644 index 0000000..7324c13 --- /dev/null +++ b/Ghost.Editor.Core/AssetHandle/AssetImporterAttribute.cs @@ -0,0 +1,15 @@ +namespace Ghost.Editor.Core.AssetHandle; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public class AssetImporterAttribute : Attribute +{ + public string[] SupportedExtensions + { + get; + } + + public AssetImporterAttribute(params string[] supportedExtensions) + { + SupportedExtensions = supportedExtensions; + } +} \ No newline at end of file diff --git a/Ghost.Editor.Core/AssetHandle/AssetMeta.cs b/Ghost.Editor.Core/AssetHandle/AssetMeta.cs new file mode 100644 index 0000000..0dec40e --- /dev/null +++ b/Ghost.Editor.Core/AssetHandle/AssetMeta.cs @@ -0,0 +1,16 @@ +namespace Ghost.Editor.Core.AssetHandle; + +internal class AssetMeta +{ + public Guid Guid + { + get; + internal set; + } + + public ImporterSettings? Settings + { + get; + set; + } +} \ No newline at end of file diff --git a/Ghost.App/Core/AssetHandle/AssetOpenHandlerAttribute .cs b/Ghost.Editor.Core/AssetHandle/AssetOpenHandlerAttribute .cs similarity index 100% rename from Ghost.App/Core/AssetHandle/AssetOpenHandlerAttribute .cs rename to Ghost.Editor.Core/AssetHandle/AssetOpenHandlerAttribute .cs diff --git a/Ghost.Editor.Core/AssetHandle/ImporterSettings.cs b/Ghost.Editor.Core/AssetHandle/ImporterSettings.cs new file mode 100644 index 0000000..93a0480 --- /dev/null +++ b/Ghost.Editor.Core/AssetHandle/ImporterSettings.cs @@ -0,0 +1,5 @@ +namespace Ghost.Editor.Core.AssetHandle; + +public abstract class ImporterSettings +{ +} \ No newline at end of file diff --git a/Ghost.App/Contracts/INavigationAware.cs b/Ghost.Editor.Core/Contracts/INavigationAware.cs similarity index 73% rename from Ghost.App/Contracts/INavigationAware.cs rename to Ghost.Editor.Core/Contracts/INavigationAware.cs index 4dcd428..76ab1b4 100644 --- a/Ghost.App/Contracts/INavigationAware.cs +++ b/Ghost.Editor.Core/Contracts/INavigationAware.cs @@ -1,4 +1,4 @@ -namespace Ghost.Editor.Contracts; +namespace Ghost.Editor.Core.Contracts; public interface INavigationAware { diff --git a/Ghost.App/Controls/BasicInput/PropertyField.cs b/Ghost.Editor.Core/Controls/BasicInput/PropertyField.cs similarity index 100% rename from Ghost.App/Controls/BasicInput/PropertyField.cs rename to Ghost.Editor.Core/Controls/BasicInput/PropertyField.cs diff --git a/Ghost.App/Controls/BasicInput/PropertyField.xaml b/Ghost.Editor.Core/Controls/BasicInput/PropertyField.xaml similarity index 100% rename from Ghost.App/Controls/BasicInput/PropertyField.xaml rename to Ghost.Editor.Core/Controls/BasicInput/PropertyField.xaml diff --git a/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs b/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs new file mode 100644 index 0000000..1845095 --- /dev/null +++ b/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs @@ -0,0 +1,70 @@ +using Ghost.Editor.Core.Controls; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System.Numerics; + +namespace Ghost.Editor.Controls; + +[TemplatePart(Name = "XComponent", Type = typeof(NumberBox))] +[TemplatePart(Name = "YComponent", Type = typeof(NumberBox))] +[TemplatePart(Name = "ZComponent", Type = typeof(NumberBox))] +public sealed partial class Vector3Field : ValueControl +{ + private NumberBox? _xComponent; + private NumberBox? _yComponent; + private NumberBox? _zComponent; + + public Vector3Field() + { + DefaultStyleKey = typeof(Vector3Field); + } + + protected override void ValueChanged(Vector3 oldValue, Vector3 newValue) + { + SyncFromValue(); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _xComponent?.ValueChanged -= OnComponentChanged; + _yComponent?.ValueChanged -= OnComponentChanged; + _zComponent?.ValueChanged -= OnComponentChanged; + + _xComponent = GetTemplateChild("XComponent") as NumberBox; + _yComponent = GetTemplateChild("YComponent") as NumberBox; + _zComponent = GetTemplateChild("ZComponent") as NumberBox; + + SyncFromValue(); + + _xComponent?.ValueChanged += OnComponentChanged; + _yComponent?.ValueChanged += OnComponentChanged; + _zComponent?.ValueChanged += OnComponentChanged; + } + + private void SyncFromValue() + { + SuppressChangedEvent = true; + _xComponent?.Value = Value.X; + _yComponent?.Value = Value.Y; + _zComponent?.Value = Value.Z; + SuppressChangedEvent = false; + } + + private void OnComponentChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) + { + if (SuppressChangedEvent) + { + return; + } + + var newValue = new Vector3( + (float)(_xComponent?.Value ?? 0), + (float)(_yComponent?.Value ?? 0), + (float)(_zComponent?.Value ?? 0)); + + RiseChangedEvent(Value, newValue); + Value = newValue; + } +} \ No newline at end of file diff --git a/Ghost.App/Controls/BasicInput/Vector3Field.xaml b/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.xaml similarity index 82% rename from Ghost.App/Controls/BasicInput/Vector3Field.xaml rename to Ghost.Editor.Core/Controls/BasicInput/Vector3Field.xaml index f41c3be..4a4226c 100644 --- a/Ghost.App/Controls/BasicInput/Vector3Field.xaml +++ b/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.xaml @@ -22,17 +22,17 @@ Grid.Column="0" VerticalAlignment="Center" Text="X" /> - + - + - + diff --git a/Ghost.Editor.Core/Controls/ControlsDictionary.cs b/Ghost.Editor.Core/Controls/ControlsDictionary.cs new file mode 100644 index 0000000..5a89f08 --- /dev/null +++ b/Ghost.Editor.Core/Controls/ControlsDictionary.cs @@ -0,0 +1,13 @@ +using Microsoft.UI.Xaml; + +namespace Ghost.Editor.Core.Controls; + +public partial class ControlsDictionary : ResourceDictionary +{ + private const string _DICTIONARY_PATH = "ms-appx:///Ghost.Editor.Core/Controls/ControlsDictionary.xaml"; + + public ControlsDictionary() + { + Source = new Uri(_DICTIONARY_PATH, UriKind.Absolute); + } +} \ No newline at end of file diff --git a/Ghost.Editor.Core/Controls/ControlsDictionary.xaml b/Ghost.Editor.Core/Controls/ControlsDictionary.xaml new file mode 100644 index 0000000..7bc6fe2 --- /dev/null +++ b/Ghost.Editor.Core/Controls/ControlsDictionary.xaml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Ghost.App/Controls/Internal/ComponentDataView.cs b/Ghost.Editor.Core/Controls/Internal/ComponentDataView.cs similarity index 72% rename from Ghost.App/Controls/Internal/ComponentDataView.cs rename to Ghost.Editor.Core/Controls/Internal/ComponentDataView.cs index bf96072..353019e 100644 --- a/Ghost.App/Controls/Internal/ComponentDataView.cs +++ b/Ghost.Editor.Core/Controls/Internal/ComponentDataView.cs @@ -1,25 +1,27 @@ +using Ghost.Core; using Ghost.Editor.Core.Inspector; -using Ghost.Editor.Resources; -using Ghost.Editor.Utilities; +using Ghost.Editor.Core.Resources; +using Ghost.Editor.Core.Utilities; using Ghost.Entities; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; using System.Reflection; namespace Ghost.Editor.Controls.Internal; internal unsafe sealed partial class ComponentDataView : Control { + private delegate void EditorUpdate(); + private StackPanel? _contentContainer; private readonly World? _world; private readonly Entity _entity = Entity.Invalid; private readonly Type? _componentType; - private EventHandler? _updateHandler; - private IComponentEditor? _customEditor; + private ComponentEditor? _customEditor; private PropertyField[]? _propertyFields; + private EditorUpdate? _editorUpdate; public string HeaderText { @@ -36,7 +38,8 @@ internal unsafe sealed partial class ComponentDataView : Control Unloaded += (s, e) => { - CompositionTarget.Rendering -= _updateHandler; + _customEditor?.Destroy(); + _contentContainer = null; _customEditor = null; _propertyFields = null; @@ -59,7 +62,7 @@ internal unsafe sealed partial class ComponentDataView : Control ReBuild(); } - private void ReflectionUpdate(object? sender, object e) + private void ReflectionUpdate() { if (_propertyFields == null) { @@ -72,9 +75,9 @@ internal unsafe sealed partial class ComponentDataView : Control } } - private void CustomEditorUpdate(object? sender, object e) + private void CustomEditorUpdate() { - _customEditor!.Update(new ComponentObject(_world!, _entity)); + _customEditor!.Update(); } public void ReBuild() @@ -92,13 +95,14 @@ internal unsafe sealed partial class ComponentDataView : Control var componentObject = new ComponentObject(_world, _entity); var editorType = TypeCache.GetTypes().FirstOrDefault(t => - typeof(IComponentEditor).IsAssignableFrom(t) && + typeof(ComponentEditor).IsAssignableFrom(t) && t.GetCustomAttribute()?.TargetType.IsAssignableFrom(_componentType) == true); if (editorType != null) { - _customEditor = (IComponentEditor)Activator.CreateInstance(editorType)!; - _customEditor.Create(componentObject, _contentContainer); + _customEditor = (ComponentEditor)Activator.CreateInstance(editorType)!; + _customEditor.Initialize(componentObject); + _customEditor.Create(_contentContainer); } else { @@ -108,7 +112,7 @@ internal unsafe sealed partial class ComponentDataView : Control for (var i = 0; i < fields.Length; i++) { var field = fields[i]; - var component = _world.ComponentStorage.ComponentPools[_componentType.TypeHandle.Value].Get(_entity); + var component = _world.ComponentStorage.ComponentPools[TypeHandle.Get(_componentType)].Get(_entity); var propertyField = PropertyField.Create(field.Name, field, component); _propertyFields[i] = propertyField; @@ -116,7 +120,21 @@ internal unsafe sealed partial class ComponentDataView : Control } } - _updateHandler = _customEditor == null ? ReflectionUpdate : CustomEditorUpdate; - CompositionTarget.Rendering += _updateHandler; + _editorUpdate = _customEditor == null ? ReflectionUpdate : CustomEditorUpdate; + _editorUpdate(); + + _world.ComponentChanged += OnComponentChanged; + } + + private void OnComponentChanged(World world, Entity entity, Type type) + { + if (world != _world + || entity != _entity + || type != _componentType) + { + return; + } + + _editorUpdate?.Invoke(); } } \ No newline at end of file diff --git a/Ghost.App/Controls/Internal/ComponentDataView.xaml b/Ghost.Editor.Core/Controls/Internal/ComponentDataView.xaml similarity index 100% rename from Ghost.App/Controls/Internal/ComponentDataView.xaml rename to Ghost.Editor.Core/Controls/Internal/ComponentDataView.xaml diff --git a/Ghost.App/Controls/Internal/NavigationTabView.cs b/Ghost.Editor.Core/Controls/Internal/NavigationTabView.cs similarity index 77% rename from Ghost.App/Controls/Internal/NavigationTabView.cs rename to Ghost.Editor.Core/Controls/Internal/NavigationTabView.cs index cf33757..e5e4289 100644 --- a/Ghost.App/Controls/Internal/NavigationTabView.cs +++ b/Ghost.Editor.Core/Controls/Internal/NavigationTabView.cs @@ -1,4 +1,5 @@ -using Ghost.Editor.Contracts; +using Ghost.Editor.Core.Contracts; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Ghost.Editor.Controls.Internal; @@ -18,7 +19,9 @@ public sealed partial class NavigationTabView : TabView { public NavigationTabView() { - this.SelectionChanged += NavigationTabView_SelectionChanged; + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + SelectionChanged += NavigationTabView_SelectionChanged; } private void NavigationTabView_SelectionChanged(object sender, SelectionChangedEventArgs e) diff --git a/Ghost.App/Controls/Internal/NavigationTabView.xaml b/Ghost.Editor.Core/Controls/Internal/NavigationTabView.xaml similarity index 100% rename from Ghost.App/Controls/Internal/NavigationTabView.xaml rename to Ghost.Editor.Core/Controls/Internal/NavigationTabView.xaml diff --git a/Ghost.Editor.Core/Controls/ValueControl.cs b/Ghost.Editor.Core/Controls/ValueControl.cs new file mode 100644 index 0000000..6c83bdc --- /dev/null +++ b/Ghost.Editor.Core/Controls/ValueControl.cs @@ -0,0 +1,70 @@ +using Ghost.Editor.Core.Event; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Ghost.Editor.Core.Controls; + +public partial class ValueControl : Control +{ + private bool _suppressChangedEvent; + + protected bool SuppressChangedEvent + { + get => _suppressChangedEvent; + set => _suppressChangedEvent = value; + } + + public T Value + { + get => (T)GetValue(ValueProperty); + set + { + if (EqualityComparer.Default.Equals(Value, value)) + { + return; + } + + SetValue(ValueProperty, value); + } + } + + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register(nameof(Value), typeof(T), typeof(ValueControl), new PropertyMetadata(default(T), ChangedCallback)); + + public event ValueChangedEventHandler? OnValueChanged; + + private static void ChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is ValueControl valueControl) + { + valueControl.ValueChanged((T)e.OldValue, (T)e.NewValue); + + if (!valueControl._suppressChangedEvent) + { + valueControl.OnValueChanged?.Invoke(valueControl, new((T)e.OldValue, (T)e.NewValue)); + } + } + } + + protected virtual void ValueChanged(T oldValue, T newValue) + { + } + + protected void RiseChangedEvent(T oldValue, T newValue) + { + OnValueChanged?.Invoke(this, new(oldValue, newValue)); + } + + /// + /// Sets the value without notifying the change event. + /// + /// The new value to set. + /// This method only suppresses the change event notification, not the method. + /// Useful when you need to change the value programmatically without triggering the change event. + public void SetValueWithoutNotifying(T value) + { + _suppressChangedEvent = true; + SetValue(ValueProperty, value); + _suppressChangedEvent = false; + } +} \ No newline at end of file diff --git a/Ghost.App/Event/ValueChangedEventHandler.cs b/Ghost.Editor.Core/Event/ValueChangedEventHandler.cs similarity index 90% rename from Ghost.App/Event/ValueChangedEventHandler.cs rename to Ghost.Editor.Core/Event/ValueChangedEventHandler.cs index f04242d..76a5845 100644 --- a/Ghost.App/Event/ValueChangedEventHandler.cs +++ b/Ghost.Editor.Core/Event/ValueChangedEventHandler.cs @@ -1,4 +1,4 @@ -namespace Ghost.Editor.Event; +namespace Ghost.Editor.Core.Event; public delegate void ValueChangedEventHandler(object? sender, ValueChangedEventArgs args); diff --git a/Ghost.Editor.Core/Ghost.Editor.Core.csproj b/Ghost.Editor.Core/Ghost.Editor.Core.csproj new file mode 100644 index 0000000..cf4ff40 --- /dev/null +++ b/Ghost.Editor.Core/Ghost.Editor.Core.csproj @@ -0,0 +1,48 @@ + + + net9.0-windows10.0.22621.0 + 10.0.17763.0 + Ghost.Editor.Core + win-x86;win-x64;win-arm64 + true + enable + 10.0.20348.0 + enable + True + preview + + + + + + + + + + + + + + + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + + + + + \ No newline at end of file diff --git a/Ghost.Editor.Core/Inspector/ComponentEditor.cs b/Ghost.Editor.Core/Inspector/ComponentEditor.cs new file mode 100644 index 0000000..ec0e26b --- /dev/null +++ b/Ghost.Editor.Core/Inspector/ComponentEditor.cs @@ -0,0 +1,40 @@ +using Microsoft.UI.Xaml.Controls; + +namespace Ghost.Editor.Core.Inspector; + +public abstract class ComponentEditor +{ + private ComponentObject _componentObject; + + /// + /// Represents the underlying component object used by this class to manage its functionality. + /// + protected ComponentObject ComponentObject => _componentObject; + + internal void Initialize(ComponentObject componentObject) + { + _componentObject = componentObject; + } + + /// + /// Called when the component editor is created. + /// + /// The container to add the editor controls to. + public virtual void Create(StackPanel container) + { + } + + /// + /// Called when the component editor needs to update its UI based on the current state of the component data. + /// + public virtual void Update() + { + } + + /// + /// Called when the component editor is destroyed. + /// + public virtual void Destroy() + { + } +} \ No newline at end of file diff --git a/Ghost.App/Core/Inspector/ComponentObject.cs b/Ghost.Editor.Core/Inspector/ComponentObject.cs similarity index 100% rename from Ghost.App/Core/Inspector/ComponentObject.cs rename to Ghost.Editor.Core/Inspector/ComponentObject.cs diff --git a/Ghost.App/Core/Inspector/CustomEditorAttribute.cs b/Ghost.Editor.Core/Inspector/CustomEditorAttribute.cs similarity index 100% rename from Ghost.App/Core/Inspector/CustomEditorAttribute.cs rename to Ghost.Editor.Core/Inspector/CustomEditorAttribute.cs diff --git a/Ghost.App/Core/Inspector/IInspectable.cs b/Ghost.Editor.Core/Inspector/IInspectable.cs similarity index 100% rename from Ghost.App/Core/Inspector/IInspectable.cs rename to Ghost.Editor.Core/Inspector/IInspectable.cs diff --git a/Ghost.Editor/Services/Contracts/IInspectorService.cs b/Ghost.Editor.Core/Inspector/IInspectorService.cs similarity index 68% rename from Ghost.Editor/Services/Contracts/IInspectorService.cs rename to Ghost.Editor.Core/Inspector/IInspectorService.cs index d2881c6..17ce3f6 100644 --- a/Ghost.Editor/Services/Contracts/IInspectorService.cs +++ b/Ghost.Editor.Core/Inspector/IInspectorService.cs @@ -1,6 +1,4 @@ -using Ghost.Editor.Contracts; - -namespace Ghost.Editor.Services.Contracts; +namespace Ghost.Editor.Core.Inspector; internal interface IInspectorService { diff --git a/Ghost.App/Services/InspectorService.cs b/Ghost.Editor.Core/Inspector/InspectorService.cs similarity index 75% rename from Ghost.App/Services/InspectorService.cs rename to Ghost.Editor.Core/Inspector/InspectorService.cs index b1bb2be..4a16c82 100644 --- a/Ghost.App/Services/InspectorService.cs +++ b/Ghost.Editor.Core/Inspector/InspectorService.cs @@ -1,7 +1,4 @@ -using Ghost.Editor.Core.Inspector; -using Ghost.Editor.Services.Contracts; - -namespace Ghost.Editor.Services; +namespace Ghost.Editor.Core.Inspector; public class InspectorService : IInspectorService { diff --git a/Ghost.Editor/Services/Contracts/INotificationService.cs b/Ghost.Editor.Core/Notifications/INotificationService.cs similarity index 51% rename from Ghost.Editor/Services/Contracts/INotificationService.cs rename to Ghost.Editor.Core/Notifications/INotificationService.cs index cef71e7..780f3f7 100644 --- a/Ghost.Editor/Services/Contracts/INotificationService.cs +++ b/Ghost.Editor.Core/Notifications/INotificationService.cs @@ -1,8 +1,9 @@ -using Ghost.Editor.Models; +using CommunityToolkit.WinUI.Behaviors; -namespace Ghost.Editor.Services.Contracts; +namespace Ghost.Editor.Core.Notifications; public interface INotificationService { public void ShowNotification(string? message, MessageType type, int duration = 5, string? title = null); + public void ShowNotification(Notification notification); } diff --git a/Ghost.Editor/Models/MessageType.cs b/Ghost.Editor.Core/Notifications/MessageType.cs similarity index 63% rename from Ghost.Editor/Models/MessageType.cs rename to Ghost.Editor.Core/Notifications/MessageType.cs index c559200..8e52904 100644 --- a/Ghost.Editor/Models/MessageType.cs +++ b/Ghost.Editor.Core/Notifications/MessageType.cs @@ -1,4 +1,4 @@ -namespace Ghost.Editor.Models; +namespace Ghost.Editor.Core.Notifications; public enum MessageType { diff --git a/Ghost.App/Services/NotificationService.cs b/Ghost.Editor.Core/Notifications/NotificationService.cs similarity index 91% rename from Ghost.App/Services/NotificationService.cs rename to Ghost.Editor.Core/Notifications/NotificationService.cs index 3915c2e..d49207c 100644 --- a/Ghost.App/Services/NotificationService.cs +++ b/Ghost.Editor.Core/Notifications/NotificationService.cs @@ -1,10 +1,7 @@ using CommunityToolkit.WinUI.Behaviors; -using Ghost.Editor.Models; -using Ghost.Editor.Services.Contracts; using Microsoft.UI.Xaml.Controls; -using System; -namespace Ghost.Editor.Services; +namespace Ghost.Editor.Core.Notifications; public class NotificationService : INotificationService { diff --git a/Ghost.Editor/Services/Contracts/IProgressService.cs b/Ghost.Editor.Core/Progress/IProgressService.cs similarity index 83% rename from Ghost.Editor/Services/Contracts/IProgressService.cs rename to Ghost.Editor.Core/Progress/IProgressService.cs index fce0331..0b1ee1b 100644 --- a/Ghost.Editor/Services/Contracts/IProgressService.cs +++ b/Ghost.Editor.Core/Progress/IProgressService.cs @@ -1,4 +1,4 @@ -namespace Ghost.Editor.Services.Contracts; +namespace Ghost.Editor.Core.Progress; public interface IProgressService { diff --git a/Ghost.App/Services/ProgressService.cs b/Ghost.Editor.Core/Progress/ProgressService.cs similarity index 96% rename from Ghost.App/Services/ProgressService.cs rename to Ghost.Editor.Core/Progress/ProgressService.cs index 14163ed..cbe8ed5 100644 --- a/Ghost.App/Services/ProgressService.cs +++ b/Ghost.Editor.Core/Progress/ProgressService.cs @@ -1,10 +1,9 @@ using CommunityToolkit.WinUI; -using Ghost.Editor.Services.Contracts; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System.Runtime.CompilerServices; -namespace Ghost.Editor.Services; +namespace Ghost.Editor.Core.Progress; public class ProgressService : IProgressService { diff --git a/Ghost.App/Resources/EditorIconSource.cs b/Ghost.Editor.Core/Resources/EditorIconSource.cs similarity index 89% rename from Ghost.App/Resources/EditorIconSource.cs rename to Ghost.Editor.Core/Resources/EditorIconSource.cs index 619a528..2913793 100644 --- a/Ghost.App/Resources/EditorIconSource.cs +++ b/Ghost.Editor.Core/Resources/EditorIconSource.cs @@ -1,6 +1,6 @@ using Microsoft.UI.Xaml.Controls; -namespace Ghost.Editor.Resources; +namespace Ghost.Editor.Core.Resources; public static class EditorIconSource { diff --git a/Ghost.Editor/Resources/FileExtensions.cs b/Ghost.Editor.Core/Resources/FileExtensions.cs similarity index 90% rename from Ghost.Editor/Resources/FileExtensions.cs rename to Ghost.Editor.Core/Resources/FileExtensions.cs index 9b38981..5448d7a 100644 --- a/Ghost.Editor/Resources/FileExtensions.cs +++ b/Ghost.Editor.Core/Resources/FileExtensions.cs @@ -1,4 +1,4 @@ -namespace Ghost.Editor.Resources; +namespace Ghost.Editor.Core.Resources; internal static class FileExtensions { diff --git a/Ghost.App/Resources/StaticResource.cs b/Ghost.Editor.Core/Resources/StaticResource.cs similarity index 82% rename from Ghost.App/Resources/StaticResource.cs rename to Ghost.Editor.Core/Resources/StaticResource.cs index 247849e..bb5a6bf 100644 --- a/Ghost.App/Resources/StaticResource.cs +++ b/Ghost.Editor.Core/Resources/StaticResource.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace Ghost.Editor.Resources; +namespace Ghost.Editor.Core.Resources; internal static class StaticResource { diff --git a/Ghost.App/Core/SceneGraph/EditorWorldManager.cs b/Ghost.Editor.Core/SceneGraph/EditorWorldManager.cs similarity index 93% rename from Ghost.App/Core/SceneGraph/EditorWorldManager.cs rename to Ghost.Editor.Core/SceneGraph/EditorWorldManager.cs index 566c5d7..d196a77 100644 --- a/Ghost.App/Core/SceneGraph/EditorWorldManager.cs +++ b/Ghost.Editor.Core/SceneGraph/EditorWorldManager.cs @@ -1,5 +1,6 @@ -using Ghost.Editor.Resources; -using Ghost.Editor.Services.Contracts; +using Ghost.Editor.Core.Progress; +using Ghost.Editor.Core.Resources; +using Ghost.Editor.Core.Utilities; using System.Text.Json; namespace Ghost.Editor.Core.SceneGraph; diff --git a/Ghost.App/Core/SceneGraph/EntityNode.cs b/Ghost.Editor.Core/SceneGraph/EntityNode.cs similarity index 82% rename from Ghost.App/Core/SceneGraph/EntityNode.cs rename to Ghost.Editor.Core/SceneGraph/EntityNode.cs index edc9bd7..c0dc1d3 100644 --- a/Ghost.App/Core/SceneGraph/EntityNode.cs +++ b/Ghost.Editor.Core/SceneGraph/EntityNode.cs @@ -1,6 +1,6 @@ using Ghost.Editor.Controls.Internal; using Ghost.Editor.Core.Inspector; -using Ghost.Editor.Resources; +using Ghost.Editor.Core.Resources; using Ghost.Engine.Editor; using Ghost.Entities; using Microsoft.UI.Text; @@ -13,16 +13,23 @@ namespace Ghost.Editor.Core.SceneGraph; public partial class EntityNode : SceneGraphNode { - private WorldNode _owner; - private readonly Entity _entity; + public WorldNode Owner + { + get; + set; + } + + public Entity Entity + { + get; + } - public Entity Entity => _entity; public override SceneGraphNodeType NodeType => SceneGraphNodeType.Entity; public EntityNode(WorldNode owner, Entity entity, string name) { - _owner = owner; - _entity = entity; + Owner = owner; + Entity = entity; Name = name; } } @@ -48,7 +55,7 @@ public partial class EntityNode : IInspectable }; var idText = new TextBlock { - Text = $"ID: {_entity.ID} Generation: {_entity.Generation}", + Text = $"ID: {Entity.ID} Generation: {Entity.Generation}", Margin = new Thickness(5, 7, 0, 0), Opacity = 0.75, Style = Application.Current.Resources["CaptionTextBlockStyle"] as Style @@ -79,20 +86,20 @@ public partial class EntityNode : IInspectable VerticalAlignment = VerticalAlignment.Top }; - foreach (var (typeHandle, componentPtr) in _owner.World.EntityManager.GetComponentsUnsafe(_entity)) + foreach (var (typeHandle, componentPtr) in Owner.World.EntityManager.GetComponentsUnsafe(Entity)) { if (componentPtr == IntPtr.Zero) { continue; } - var type = Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(typeHandle)); + var type = typeHandle.ToType(); if (type == null || type.GetCustomAttribute() != null) { continue; } - var dataView = new ComponentDataView(type.Name, _owner.World, _entity, type); + var dataView = new ComponentDataView(type.Name, Owner.World, Entity, type); root.Children.Add(dataView); } diff --git a/Ghost.App/Core/SceneGraph/SceneGraphHelpers.cs b/Ghost.Editor.Core/SceneGraph/SceneGraphHelpers.cs similarity index 100% rename from Ghost.App/Core/SceneGraph/SceneGraphHelpers.cs rename to Ghost.Editor.Core/SceneGraph/SceneGraphHelpers.cs diff --git a/Ghost.App/Core/SceneGraph/SceneGraphNode.cs b/Ghost.Editor.Core/SceneGraph/SceneGraphNode.cs similarity index 100% rename from Ghost.App/Core/SceneGraph/SceneGraphNode.cs rename to Ghost.Editor.Core/SceneGraph/SceneGraphNode.cs diff --git a/Ghost.App/Core/SceneGraph/WorldNode.cs b/Ghost.Editor.Core/SceneGraph/WorldNode.cs similarity index 99% rename from Ghost.App/Core/SceneGraph/WorldNode.cs rename to Ghost.Editor.Core/SceneGraph/WorldNode.cs index 3ca7952..72d8c27 100644 --- a/Ghost.App/Core/SceneGraph/WorldNode.cs +++ b/Ghost.Editor.Core/SceneGraph/WorldNode.cs @@ -1,7 +1,7 @@ using Ghost.Editor.Core.AssetHandle; using Ghost.Editor.Core.Inspector; +using Ghost.Editor.Core.Resources; using Ghost.Editor.Core.Serializer; -using Ghost.Editor.Resources; using Ghost.Engine.Components; using Ghost.Entities; using Microsoft.UI.Xaml; diff --git a/Ghost.App/Core/Serializer/WorldNodeSerializer.cs b/Ghost.Editor.Core/Serializer/WorldNodeSerializer.cs similarity index 97% rename from Ghost.App/Core/Serializer/WorldNodeSerializer.cs rename to Ghost.Editor.Core/Serializer/WorldNodeSerializer.cs index d669a2a..d298fbc 100644 --- a/Ghost.App/Core/Serializer/WorldNodeSerializer.cs +++ b/Ghost.Editor.Core/Serializer/WorldNodeSerializer.cs @@ -2,7 +2,6 @@ using Ghost.Engine.Utilities; using Ghost.Entities; using Ghost.Entities.Components; -using Ghost.Entities.Utilities; using System.Text.Json; using System.Text.Json.Serialization; @@ -107,7 +106,7 @@ internal class WorldNodeSerializer : JsonConverter { foreach (var kvp in value.World.ComponentStorage.ComponentPools) { - var type = TypeHandle.ToType(kvp.Key) ?? throw new Exception($"Type {kvp.Key} not found."); + var type = kvp.Key.ToType() ?? throw new Exception($"Type {kvp.Key} not found."); var typeName = type.AssemblyQualifiedName ?? type.Name; writer.WriteArray(typeName, kvp.Value.Enumerate(), data => diff --git a/Ghost.Editor.Core/Utilities/EditorApplication.cs b/Ghost.Editor.Core/Utilities/EditorApplication.cs new file mode 100644 index 0000000..b7e6b6d --- /dev/null +++ b/Ghost.Editor.Core/Utilities/EditorApplication.cs @@ -0,0 +1,26 @@ +using Microsoft.UI.Xaml; + +namespace Ghost.Editor.Core.Utilities; + +public static class EditorApplication +{ + private static IServiceProvider? _serviceProvider; + + public static Application Current => Application.Current; + + internal static void Initialize(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public static T GetService() + where T : class + { + if (_serviceProvider?.GetService(typeof(T)) is not T service) + { + throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs."); + } + + return service; + } +} \ No newline at end of file diff --git a/Ghost.App/Utilities/TypeCache.cs b/Ghost.Editor.Core/Utilities/TypeCache.cs similarity index 86% rename from Ghost.App/Utilities/TypeCache.cs rename to Ghost.Editor.Core/Utilities/TypeCache.cs index 766b435..19223da 100644 --- a/Ghost.App/Utilities/TypeCache.cs +++ b/Ghost.Editor.Core/Utilities/TypeCache.cs @@ -1,7 +1,7 @@ using Ghost.Entities; using System.Reflection; -namespace Ghost.Editor.Utilities; +namespace Ghost.Editor.Core.Utilities; public static class TypeCache { @@ -31,7 +31,7 @@ public static class ComponentTypeCache { var world = World.GetWorld(i); var typeHandles = world.ComponentStorage.ComponentPools.Keys; - _componentTypes[i] = typeHandles.Select(handle => Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(handle))).ToArray(); + _componentTypes[i] = typeHandles.Select(handle => handle.ToType()).ToArray(); } } diff --git a/Ghost.App/ActivationHandler.cs b/Ghost.Editor/ActivationHandler.cs similarity index 85% rename from Ghost.App/ActivationHandler.cs rename to Ghost.Editor/ActivationHandler.cs index 9ac4713..2909a85 100644 --- a/Ghost.App/ActivationHandler.cs +++ b/Ghost.Editor/ActivationHandler.cs @@ -1,7 +1,7 @@ using Ghost.Data.Resources; using Ghost.Data.Services; +using Ghost.Editor.Core.Utilities; using Microsoft.UI.Xaml; -using System.IO; namespace Ghost.Editor; @@ -24,5 +24,7 @@ internal static class ActivationHandler { FolderInitialization(); ProjectService.EnsureDefaultTemplate(); + + EditorApplication.Initialize(((App)(Application.Current)).Host.Services); } -} +} \ No newline at end of file diff --git a/Ghost.App/App.xaml b/Ghost.Editor/App.xaml similarity index 82% rename from Ghost.App/App.xaml rename to Ghost.Editor/App.xaml index d768f52..32a4449 100644 --- a/Ghost.App/App.xaml +++ b/Ghost.Editor/App.xaml @@ -1,14 +1,15 @@ - + diff --git a/Ghost.App/App.xaml.cs b/Ghost.Editor/App.xaml.cs similarity index 86% rename from Ghost.App/App.xaml.cs rename to Ghost.Editor/App.xaml.cs index 2d8668e..01b06df 100644 --- a/Ghost.App/App.xaml.cs +++ b/Ghost.Editor/App.xaml.cs @@ -1,6 +1,7 @@ using Ghost.Editor.Core.AppState; -using Ghost.Editor.Services; -using Ghost.Editor.Services.Contracts; +using Ghost.Editor.Core.Inspector; +using Ghost.Editor.Core.Notifications; +using Ghost.Editor.Core.Progress; using Ghost.Editor.Utilities; using Ghost.Engine.Services; using Microsoft.Extensions.DependencyInjection; @@ -15,16 +16,16 @@ namespace Ghost.Editor; /// /// Provides application-specific behavior to supplement the default Application class. /// -public partial class EditorApplication : Application +public partial class App : Application { private Window? _window; internal static Window? Window { - get => (Current as EditorApplication)!._window; + get => (Current as App)!._window; set { - if (Current is EditorApplication app) + if (Current is App app) { app._window = value; } @@ -40,7 +41,7 @@ public partial class EditorApplication : Application /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// - internal EditorApplication() + internal App() { InitializeComponent(); @@ -64,12 +65,12 @@ public partial class EditorApplication : Application internal static IServiceScope CreateScope() { - return (Current as EditorApplication)!.Host.Services.CreateScope(); + return (Current as App)!.Host.Services.CreateScope(); } public static T GetService() where T : class { - if ((Current as EditorApplication)!.Host.Services.GetService(typeof(T)) is not T service) + if ((Current as App)!.Host.Services.GetService(typeof(T)) is not T service) { throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs."); } diff --git a/Ghost.Editor/AssemblyInfo.cs b/Ghost.Editor/AssemblyInfo.cs index 5a54667..d3b1fb4 100644 --- a/Ghost.Editor/AssemblyInfo.cs +++ b/Ghost.Editor/AssemblyInfo.cs @@ -1,3 +1,3 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Ghost.App")] \ No newline at end of file +[assembly: InternalsVisibleTo("Ghost.UnitTest")] diff --git a/Ghost.Editor/AssetHandle/Asset.cs b/Ghost.Editor/AssetHandle/Asset.cs deleted file mode 100644 index 22433ab..0000000 --- a/Ghost.Editor/AssetHandle/Asset.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Ghost.Editor.AssetHandle; - -public abstract class Asset -{ - /// - /// Get the Guid of the asset. - /// - public Guid GUID - { - get; - } = Guid.NewGuid(); - - /// - /// True if the asset is a folder, false if it is a file. - /// - public bool IsFolder - { - get; - } - - internal void GenerateMetadata() - { - } -} \ No newline at end of file diff --git a/Ghost.Editor/AssetHandle/AssetDatabase.cs b/Ghost.Editor/AssetHandle/AssetDatabase.cs deleted file mode 100644 index 846a4b5..0000000 --- a/Ghost.Editor/AssetHandle/AssetDatabase.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Diagnostics; -using System.Reflection; - -namespace Ghost.Editor.AssetHandle; - -public static class AssetDatabase -{ - private static readonly Dictionary> _assetOpenHandlers = new(StringComparer.OrdinalIgnoreCase); - private static readonly Dictionary> _asyncAssetOpenHandler = new(StringComparer.OrdinalIgnoreCase); - - static AssetDatabase() - { - RegisterAssetHandles(); - } - - private static void RegisterAssetHandles() - { - var methods = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(a => a.GetTypes()) - .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) - .Where(m => m.GetCustomAttribute() != null && - m.GetParameters().Length == 1 && - m.GetParameters()[0].ParameterType == typeof(string)); - - foreach (var method in methods) - { - var attr = method.GetCustomAttribute()!; - var del = (Action)Delegate.CreateDelegate(typeof(Action), method); - foreach (var ext in attr.Extensions) - { - if (_assetOpenHandlers.ContainsKey(ext)) - { - throw new InvalidOperationException($"Duplicate handler for extension '{ext}'"); - } - - _assetOpenHandlers[ext] = del; - } - } - - var asyncMethods = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(a => a.GetTypes()) - .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) - .Where(m => m.GetCustomAttribute() != null && - m.GetParameters().Length == 1 && - m.GetParameters()[0].ParameterType == typeof(string) && - m.ReturnType == typeof(Task)); - - foreach (var method in asyncMethods) - { - var attr = method.GetCustomAttribute()!; - var del = (Func)Delegate.CreateDelegate(typeof(Func), method); - foreach (var ext in attr.Extensions) - { - if (_asyncAssetOpenHandler.ContainsKey(ext)) - { - throw new InvalidOperationException($"Duplicate async handler for extension '{ext}'"); - } - _asyncAssetOpenHandler[ext] = del; - } - } - } - - public static async ValueTask OpenAsset(string path) - { - var extension = Path.GetExtension(path); - if (_assetOpenHandlers.TryGetValue(extension, out var handler)) - { - handler(path); - } - else if (_asyncAssetOpenHandler.TryGetValue(extension, out var asyncHandler)) - { - await asyncHandler(path); - } - else - { - Process.Start(new ProcessStartInfo(path) - { - UseShellExecute = true - }); - } - - } -} \ No newline at end of file diff --git a/Ghost.Editor/AssetHandle/AssetOpenHandlerAttribute .cs b/Ghost.Editor/AssetHandle/AssetOpenHandlerAttribute .cs deleted file mode 100644 index 084b565..0000000 --- a/Ghost.Editor/AssetHandle/AssetOpenHandlerAttribute .cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Ghost.Editor.AssetHandle; - -[AttributeUsage(AttributeTargets.Method)] -public class AssetOpenHandlerAttribute : Attribute -{ - public string[] Extensions - { - get; - } - - public AssetOpenHandlerAttribute(params string[] extensions) - { - Extensions = extensions.Select(e => e.StartsWith(".") ? e.ToLowerInvariant() : "." + e.ToLowerInvariant()).ToArray(); - } -} - -[AttributeUsage(AttributeTargets.Method)] -public class AsyncAssetOpenHandlerAttribute : Attribute -{ - public string[] Extensions - { - get; - } - - public AsyncAssetOpenHandlerAttribute(params string[] extensions) - { - Extensions = extensions.Select(e => e.StartsWith(".") ? e.ToLowerInvariant() : "." + e.ToLowerInvariant()).ToArray(); - } -} \ No newline at end of file diff --git a/Ghost.App/Assets/Icon.altform-lightunplated_targetsize-16.png b/Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-16.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-lightunplated_targetsize-16.png rename to Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-16.png diff --git a/Ghost.App/Assets/Icon.altform-lightunplated_targetsize-24.png b/Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-24.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-lightunplated_targetsize-24.png rename to Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-24.png diff --git a/Ghost.App/Assets/Icon.altform-lightunplated_targetsize-256.png b/Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-256.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-lightunplated_targetsize-256.png rename to Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-256.png diff --git a/Ghost.App/Assets/Icon.altform-lightunplated_targetsize-32.png b/Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-32.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-lightunplated_targetsize-32.png rename to Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-32.png diff --git a/Ghost.App/Assets/Icon.altform-lightunplated_targetsize-48.png b/Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-48.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-lightunplated_targetsize-48.png rename to Ghost.Editor/Assets/Icon.altform-lightunplated_targetsize-48.png diff --git a/Ghost.App/Assets/Icon.altform-unplated_targetsize-16.png b/Ghost.Editor/Assets/Icon.altform-unplated_targetsize-16.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-unplated_targetsize-16.png rename to Ghost.Editor/Assets/Icon.altform-unplated_targetsize-16.png diff --git a/Ghost.App/Assets/Icon.altform-unplated_targetsize-24.png b/Ghost.Editor/Assets/Icon.altform-unplated_targetsize-24.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-unplated_targetsize-24.png rename to Ghost.Editor/Assets/Icon.altform-unplated_targetsize-24.png diff --git a/Ghost.App/Assets/Icon.altform-unplated_targetsize-256.png b/Ghost.Editor/Assets/Icon.altform-unplated_targetsize-256.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-unplated_targetsize-256.png rename to Ghost.Editor/Assets/Icon.altform-unplated_targetsize-256.png diff --git a/Ghost.App/Assets/Icon.altform-unplated_targetsize-32.png b/Ghost.Editor/Assets/Icon.altform-unplated_targetsize-32.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-unplated_targetsize-32.png rename to Ghost.Editor/Assets/Icon.altform-unplated_targetsize-32.png diff --git a/Ghost.App/Assets/Icon.altform-unplated_targetsize-48.png b/Ghost.Editor/Assets/Icon.altform-unplated_targetsize-48.png similarity index 100% rename from Ghost.App/Assets/Icon.altform-unplated_targetsize-48.png rename to Ghost.Editor/Assets/Icon.altform-unplated_targetsize-48.png diff --git a/Ghost.App/Assets/Icon.scale-100.png b/Ghost.Editor/Assets/Icon.scale-100.png similarity index 100% rename from Ghost.App/Assets/Icon.scale-100.png rename to Ghost.Editor/Assets/Icon.scale-100.png diff --git a/Ghost.App/Assets/Icon.scale-125.png b/Ghost.Editor/Assets/Icon.scale-125.png similarity index 100% rename from Ghost.App/Assets/Icon.scale-125.png rename to Ghost.Editor/Assets/Icon.scale-125.png diff --git a/Ghost.App/Assets/Icon.scale-150.png b/Ghost.Editor/Assets/Icon.scale-150.png similarity index 100% rename from Ghost.App/Assets/Icon.scale-150.png rename to Ghost.Editor/Assets/Icon.scale-150.png diff --git a/Ghost.App/Assets/Icon.scale-200.png b/Ghost.Editor/Assets/Icon.scale-200.png similarity index 100% rename from Ghost.App/Assets/Icon.scale-200.png rename to Ghost.Editor/Assets/Icon.scale-200.png diff --git a/Ghost.App/Assets/Icon.scale-400.png b/Ghost.Editor/Assets/Icon.scale-400.png similarity index 100% rename from Ghost.App/Assets/Icon.scale-400.png rename to Ghost.Editor/Assets/Icon.scale-400.png diff --git a/Ghost.App/Assets/Icon.targetsize-16.png b/Ghost.Editor/Assets/Icon.targetsize-16.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-16.png rename to Ghost.Editor/Assets/Icon.targetsize-16.png diff --git a/Ghost.App/Assets/Icon.targetsize-16_altform-lightunplated.png b/Ghost.Editor/Assets/Icon.targetsize-16_altform-lightunplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-16_altform-lightunplated.png rename to Ghost.Editor/Assets/Icon.targetsize-16_altform-lightunplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-16_altform-unplated.png b/Ghost.Editor/Assets/Icon.targetsize-16_altform-unplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-16_altform-unplated.png rename to Ghost.Editor/Assets/Icon.targetsize-16_altform-unplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-24.png b/Ghost.Editor/Assets/Icon.targetsize-24.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-24.png rename to Ghost.Editor/Assets/Icon.targetsize-24.png diff --git a/Ghost.App/Assets/Icon.targetsize-24_altform-lightunplated.png b/Ghost.Editor/Assets/Icon.targetsize-24_altform-lightunplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-24_altform-lightunplated.png rename to Ghost.Editor/Assets/Icon.targetsize-24_altform-lightunplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-24_altform-unplated.png b/Ghost.Editor/Assets/Icon.targetsize-24_altform-unplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-24_altform-unplated.png rename to Ghost.Editor/Assets/Icon.targetsize-24_altform-unplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-256.png b/Ghost.Editor/Assets/Icon.targetsize-256.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-256.png rename to Ghost.Editor/Assets/Icon.targetsize-256.png diff --git a/Ghost.App/Assets/Icon.targetsize-256_altform-lightunplated.png b/Ghost.Editor/Assets/Icon.targetsize-256_altform-lightunplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-256_altform-lightunplated.png rename to Ghost.Editor/Assets/Icon.targetsize-256_altform-lightunplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-256_altform-unplated.png b/Ghost.Editor/Assets/Icon.targetsize-256_altform-unplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-256_altform-unplated.png rename to Ghost.Editor/Assets/Icon.targetsize-256_altform-unplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-32.png b/Ghost.Editor/Assets/Icon.targetsize-32.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-32.png rename to Ghost.Editor/Assets/Icon.targetsize-32.png diff --git a/Ghost.App/Assets/Icon.targetsize-32_altform-lightunplated.png b/Ghost.Editor/Assets/Icon.targetsize-32_altform-lightunplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-32_altform-lightunplated.png rename to Ghost.Editor/Assets/Icon.targetsize-32_altform-lightunplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-32_altform-unplated.png b/Ghost.Editor/Assets/Icon.targetsize-32_altform-unplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-32_altform-unplated.png rename to Ghost.Editor/Assets/Icon.targetsize-32_altform-unplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-48.png b/Ghost.Editor/Assets/Icon.targetsize-48.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-48.png rename to Ghost.Editor/Assets/Icon.targetsize-48.png diff --git a/Ghost.App/Assets/Icon.targetsize-48_altform-lightunplated.png b/Ghost.Editor/Assets/Icon.targetsize-48_altform-lightunplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-48_altform-lightunplated.png rename to Ghost.Editor/Assets/Icon.targetsize-48_altform-lightunplated.png diff --git a/Ghost.App/Assets/Icon.targetsize-48_altform-unplated.png b/Ghost.Editor/Assets/Icon.targetsize-48_altform-unplated.png similarity index 100% rename from Ghost.App/Assets/Icon.targetsize-48_altform-unplated.png rename to Ghost.Editor/Assets/Icon.targetsize-48_altform-unplated.png diff --git a/Ghost.App/Assets/LockScreenLogo.scale-200.png b/Ghost.Editor/Assets/LockScreenLogo.scale-200.png similarity index 100% rename from Ghost.App/Assets/LockScreenLogo.scale-200.png rename to Ghost.Editor/Assets/LockScreenLogo.scale-200.png diff --git a/Ghost.App/Assets/SplashScreen.scale-200.png b/Ghost.Editor/Assets/SplashScreen.scale-200.png similarity index 100% rename from Ghost.App/Assets/SplashScreen.scale-200.png rename to Ghost.Editor/Assets/SplashScreen.scale-200.png diff --git a/Ghost.App/Assets/Square150x150Logo.scale-200.png b/Ghost.Editor/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from Ghost.App/Assets/Square150x150Logo.scale-200.png rename to Ghost.Editor/Assets/Square150x150Logo.scale-200.png diff --git a/Ghost.App/Assets/StoreLogo.png b/Ghost.Editor/Assets/StoreLogo.png similarity index 100% rename from Ghost.App/Assets/StoreLogo.png rename to Ghost.Editor/Assets/StoreLogo.png diff --git a/Ghost.App/Assets/Wide310x150Logo.scale-200.png b/Ghost.Editor/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from Ghost.App/Assets/Wide310x150Logo.scale-200.png rename to Ghost.Editor/Assets/Wide310x150Logo.scale-200.png diff --git a/Ghost.App/Assets/icon-256.ico b/Ghost.Editor/Assets/icon-256.ico similarity index 100% rename from Ghost.App/Assets/icon-256.ico rename to Ghost.Editor/Assets/icon-256.ico diff --git a/Ghost.App/Assets/icon-256.png b/Ghost.Editor/Assets/icon-256.png similarity index 100% rename from Ghost.App/Assets/icon-256.png rename to Ghost.Editor/Assets/icon-256.png diff --git a/Ghost.App/Components/HierarchyEditor.cs b/Ghost.Editor/Components/HierarchyEditor.cs similarity index 88% rename from Ghost.App/Components/HierarchyEditor.cs rename to Ghost.Editor/Components/HierarchyEditor.cs index 706025e..ec16f55 100644 --- a/Ghost.App/Components/HierarchyEditor.cs +++ b/Ghost.Editor/Components/HierarchyEditor.cs @@ -4,7 +4,7 @@ using Microsoft.UI.Xaml.Controls; namespace Ghost.Editor.Components; //[CustomEditor(typeof(Hierarchy))] -internal class HierarchyEditor : IComponentEditor +internal class HierarchyEditor : ComponentEditor { public void Create(ComponentObject componentObject, StackPanel container) { diff --git a/Ghost.App/Components/LocalToWorldEditor.cs b/Ghost.Editor/Components/LocalToWorldEditor.cs similarity index 57% rename from Ghost.App/Components/LocalToWorldEditor.cs rename to Ghost.Editor/Components/LocalToWorldEditor.cs index 6083106..08cb11f 100644 --- a/Ghost.App/Components/LocalToWorldEditor.cs +++ b/Ghost.Editor/Components/LocalToWorldEditor.cs @@ -7,13 +7,13 @@ using Microsoft.UI.Xaml.Controls; namespace Ghost.Editor.Components; [CustomEditor(typeof(LocalToWorld))] -internal class LocalToWorldEditor : IComponentEditor +internal class LocalToWorldEditor : ComponentEditor { private Vector3Field _translationField = null!; private Vector3Field _rotationField = null!; private Vector3Field _scaleField = null!; - public void Create(ComponentObject componentObject, StackPanel container) + public override void Create(StackPanel container) { _translationField = new Vector3Field(); _rotationField = new Vector3Field(); @@ -21,22 +21,22 @@ internal class LocalToWorldEditor : IComponentEditor _translationField.OnValueChanged += (s, e) => { - var data = componentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var oldRotation, out var oldScale); + var data = ComponentObject.GetData(); + MatrixUtility.GetTRS(data.ValueRO.matrix, out var _, out var oldRotation, out var oldScale); data.ValueRW.matrix = MatrixUtility.CreateTRS(e.NewValue, oldRotation, oldScale); }; _rotationField.OnValueChanged += (s, e) => { - var data = componentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var oldRotation, out var oldScale); + var data = ComponentObject.GetData(); + MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var _, out var oldScale); data.ValueRW.matrix = MatrixUtility.CreateTRS(oldTranslation, e.NewValue.ToQuaternion(), oldScale); }; _scaleField.OnValueChanged += (s, e) => { - var data = componentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var oldRotation, out var oldScale); + var data = ComponentObject.GetData(); + MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var oldRotation, out var _); data.ValueRW.matrix = MatrixUtility.CreateTRS(oldTranslation, oldRotation, e.NewValue); }; @@ -45,28 +45,17 @@ internal class LocalToWorldEditor : IComponentEditor container.Children.Add(new PropertyField() { Label = "Scale", Content = _scaleField }); } - public void Update(ComponentObject componentObject) + public override void Update() { - var data = componentObject.GetData(); + var data = ComponentObject.GetData(); MatrixUtility.GetTRS(data.ValueRO.matrix, out var translation, out var rotation, out var scale); - if (_translationField.FocusState == Microsoft.UI.Xaml.FocusState.Unfocused) - { - _translationField.Value = translation; - } - - if (_rotationField.FocusState == Microsoft.UI.Xaml.FocusState.Unfocused) - { - _rotationField.Value = VectorUtility.CreateFromQuaternion(rotation); - } - - if (_scaleField.FocusState == Microsoft.UI.Xaml.FocusState.Unfocused) - { - _scaleField.Value = scale; - } + _translationField.Value = translation; + _rotationField.Value = VectorUtility.CreateFromQuaternion(rotation); + _scaleField.Value = scale; } - public void Destroy(ComponentObject componentObject) + public override void Destroy() { } } \ No newline at end of file diff --git a/Ghost.Editor/Contracts/IInspectable.cs b/Ghost.Editor/Contracts/IInspectable.cs deleted file mode 100644 index f959a81..0000000 --- a/Ghost.Editor/Contracts/IInspectable.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.UI.Xaml; - -namespace Ghost.Editor.Contracts; -internal interface IInspectable -{ - public UIElement HeaderContent(); - public UIElement InspectorContent(); -} \ No newline at end of file diff --git a/Ghost.App/Controls/ViewModelPage.cs b/Ghost.Editor/Controls/ViewModelPage.cs similarity index 95% rename from Ghost.App/Controls/ViewModelPage.cs rename to Ghost.Editor/Controls/ViewModelPage.cs index 06f3870..91014e7 100644 --- a/Ghost.App/Controls/ViewModelPage.cs +++ b/Ghost.Editor/Controls/ViewModelPage.cs @@ -1,5 +1,5 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.Editor.Contracts; +using Ghost.Editor.Core.Contracts; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; diff --git a/Ghost.App/Core/AppState/EditorState.cs b/Ghost.Editor/Core/AppState/EditorState.cs similarity index 69% rename from Ghost.App/Core/AppState/EditorState.cs rename to Ghost.Editor/Core/AppState/EditorState.cs index 0fa6514..2abf0cf 100644 --- a/Ghost.App/Core/AppState/EditorState.cs +++ b/Ghost.Editor/Core/AppState/EditorState.cs @@ -13,14 +13,14 @@ internal class EditorState : IAppState public Task OnExitingAsync() { - if (EditorApplication.Window == _window) + if (App.Window == _window) { - EditorApplication.Window = null; + App.Window = null; } return Task.CompletedTask; } - public async Task OnEnteringAsync(object? parameter) + public Task OnEnteringAsync(object? parameter) { if (parameter is not ProjectMetadataInfo metadataInfo) { @@ -29,13 +29,12 @@ internal class EditorState : IAppState ProjectService.CurrentProject = metadataInfo; - _engineCore = EditorApplication.GetService(); - await _engineCore.StartAsync(new Engine.Models.LaunchArgument()); - - _window = EditorApplication.GetService(); + _window = App.GetService(); _window.Activate(); - EditorApplication.Window = _window; + App.Window = _window; + + return Task.CompletedTask; } public async Task OnExitedAsync() @@ -45,18 +44,20 @@ internal class EditorState : IAppState await _engineCore.ShutDownAsync(); } - if (EditorApplication.Window == _window) + if (App.Window == _window) { - EditorApplication.Window = null; + App.Window = null; } _window?.Close(); _window = null; } - public Task OnEnteredAsync(object? parameter) + public async Task OnEnteredAsync(object? parameter) { AssetDatabase.Initialize(); - return Task.CompletedTask; + + _engineCore = App.GetService(); + await _engineCore.StartAsync(new Engine.Models.LaunchArgument()); } } \ No newline at end of file diff --git a/Ghost.App/Core/AppState/LandingState.cs b/Ghost.Editor/Core/AppState/LandingState.cs similarity index 69% rename from Ghost.App/Core/AppState/LandingState.cs rename to Ghost.Editor/Core/AppState/LandingState.cs index e6f7ec4..a9936f2 100644 --- a/Ghost.App/Core/AppState/LandingState.cs +++ b/Ghost.Editor/Core/AppState/LandingState.cs @@ -10,17 +10,17 @@ internal class LandingState : IAppState public Task OnExitingAsync() { - if (EditorApplication.Window == _window) + if (App.Window == _window) { - EditorApplication.Window = null; + App.Window = null; } return Task.CompletedTask; } public Task OnEnteringAsync(object? parameter) { - _window = EditorApplication.GetService(); - EditorApplication.Window = _window; + _window = App.GetService(); + App.Window = _window; _window.Activate(); return Task.CompletedTask; @@ -28,9 +28,9 @@ internal class LandingState : IAppState public Task OnExitedAsync() { - if (EditorApplication.Window == _window) + if (App.Window == _window) { - EditorApplication.Window = null; + App.Window = null; } _window?.Close(); diff --git a/Ghost.Editor/EditorApplication.cs b/Ghost.Editor/EditorApplication.cs deleted file mode 100644 index 54c8ca0..0000000 --- a/Ghost.Editor/EditorApplication.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Ghost.Editor; - -public static class EditorApplication -{ - private static IServiceProvider? _serviceProvider; - - public static bool IsInitialized => _serviceProvider != null; - - internal static void Activate(object? parameters, IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - - public static T GetService() where T : class - { - if (!IsInitialized) - { - throw new InvalidOperationException("EditorApplication is not initialized."); - } - - if (_serviceProvider!.GetService(typeof(T)) is not T service) - { - throw new ArgumentException($"{typeof(T)} needs to be registered in the service provider."); - } - - return service; - } -} \ No newline at end of file diff --git a/Ghost.Editor/Ghost.Editor.csproj b/Ghost.Editor/Ghost.Editor.csproj index a666739..a28fcaf 100644 --- a/Ghost.Editor/Ghost.Editor.csproj +++ b/Ghost.Editor/Ghost.Editor.csproj @@ -1,23 +1,167 @@ + WinExe net9.0-windows10.0.22621.0 10.0.17763.0 - Ghost.Editor + x86;x64;ARM64 win-x86;win-x64;win-arm64 + win-$(Platform).pubxml true - enable + true preview - enable - 10.0.20348.0 - - - + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + MSBuild:Compile + + + + + + + true + + + + + False + True + True + enable + 10.0.20348.0 + app.manifest + False + False + Ghost.Editor + enable + \ No newline at end of file diff --git a/Ghost.App/Models/AssetItem.cs b/Ghost.Editor/Models/AssetItem.cs similarity index 100% rename from Ghost.App/Models/AssetItem.cs rename to Ghost.Editor/Models/AssetItem.cs diff --git a/Ghost.App/Models/ExplorerItem.cs b/Ghost.Editor/Models/ExplorerItem.cs similarity index 100% rename from Ghost.App/Models/ExplorerItem.cs rename to Ghost.Editor/Models/ExplorerItem.cs diff --git a/Ghost.Editor/Models/WorldAsset.cs b/Ghost.Editor/Models/WorldAsset.cs deleted file mode 100644 index d601f04..0000000 --- a/Ghost.Editor/Models/WorldAsset.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Ghost.Editor.AssetHandle; -using Ghost.Editor.Resources; -using Ghost.Editor.SceneGraph; - -namespace Ghost.Editor.Models; - -public class WorldAsset : Asset -{ - [AsyncAssetOpenHandler(FileExtensions.SCENE_FILE_EXTENSION)] - public static async Task Open(string path) - { - await EditorWorldManager.LoadWorld(path); - } -} \ No newline at end of file diff --git a/Ghost.App/Package.appxmanifest b/Ghost.Editor/Package.appxmanifest similarity index 100% rename from Ghost.App/Package.appxmanifest rename to Ghost.Editor/Package.appxmanifest diff --git a/Ghost.App/Properties/launchSettings.json b/Ghost.Editor/Properties/launchSettings.json similarity index 100% rename from Ghost.App/Properties/launchSettings.json rename to Ghost.Editor/Properties/launchSettings.json diff --git a/Ghost.Editor/SceneGraph/EditorWorldManager.cs b/Ghost.Editor/SceneGraph/EditorWorldManager.cs deleted file mode 100644 index 4941d72..0000000 --- a/Ghost.Editor/SceneGraph/EditorWorldManager.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Ghost.Editor.Resources; -using Ghost.Editor.Services.Contracts; -using Ghost.Engine.Resources; -using System.Text.Json; - -namespace Ghost.Editor.SceneGraph; - -public enum OpenWorldMode -{ - Single, - Additive, - AdditiveWithoutLoading -} - -public static class EditorWorldManager -{ - // TODO: Use guid keys instead of string paths for better performance and uniqueness - private static readonly Dictionary _loadedWorlds = new(); - public static IEnumerable LoadedWorlds => _loadedWorlds.Values; - - public static event Action? OnWorldLoaded; - public static event Action? OnWorldUnloaded; - - public static async Task LoadWorld(string worldPath) - { - if (_loadedWorlds.ContainsKey(worldPath) - || !File.Exists(worldPath) - || Path.GetExtension(worldPath) != FileExtensions.SCENE_FILE_EXTENSION) - { - return; - } - - var progressService = EditorApplication.GetService(); - progressService.ShowIndeterminateProgress("Loading world..."); - - foreach (var world in _loadedWorlds) - { - world.Value.Unload(); - OnWorldUnloaded?.Invoke(world.Value); - } - - await using var readStream = new FileStream(worldPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var deserializedScene = await JsonSerializer.DeserializeAsync(readStream, StaticResource.defaultSerializerOptions) ?? throw new Exception("Deserialization failed."); - - _loadedWorlds.Clear(); - - _loadedWorlds[worldPath] = deserializedScene; - await deserializedScene.LoadAsync(); - - progressService.HideProgress(); - OnWorldLoaded?.Invoke(deserializedScene); - } -} \ No newline at end of file diff --git a/Ghost.Editor/SceneGraph/EntityNode.cs b/Ghost.Editor/SceneGraph/EntityNode.cs deleted file mode 100644 index fe0b992..0000000 --- a/Ghost.Editor/SceneGraph/EntityNode.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Ghost.Entities; - -namespace Ghost.Editor.SceneGraph; - -public partial class EntityNode : SceneGraphNode -{ - private readonly Entity _entity; - - public Entity Entity => _entity; - public override SceneGraphNodeType NodeType => SceneGraphNodeType.Entity; - - public EntityNode(Entity entity, string name) - { - _entity = entity; - Name = name; - } - - internal EntityNode() - { - } -} \ No newline at end of file diff --git a/Ghost.Editor/SceneGraph/SceneGraphHelpers.cs b/Ghost.Editor/SceneGraph/SceneGraphHelpers.cs deleted file mode 100644 index 309d559..0000000 --- a/Ghost.Editor/SceneGraph/SceneGraphHelpers.cs +++ /dev/null @@ -1,112 +0,0 @@ -using Ghost.Engine.Components; -using Ghost.Entities; - -namespace Ghost.Editor.SceneGraph; - -public class SceneGraphHelpers -{ - /// - /// Creates a new entity with default components. - /// - /// The world context where the entity will be created. - /// The entity to be wrapped in the . - public static EntityNode CreateEntityNode(World world, Entity entity, string name) - { - world.EntityManager.AddComponent(entity, LocalToWorld.Identity); - world.EntityManager.AddComponent(entity, Hierarchy.Root); - return new EntityNode(entity, name); - } - - /// - /// Creates a new and entity with default components. - /// - /// The world context where the entity will be created. - public static EntityNode CreateEntityNode(World world, string name) - { - var entity = world.EntityManager.CreateEntity(); - return CreateEntityNode(world, entity, name); - } - - /// - /// Attaches childEntity to parentEntity in the scene graph. - /// - /// The world context where the entities exist. - /// The parent entity to which the child will be attached. - /// The child entity to be attached. - public static void AttachChild(WorldNode scene, EntityNode parentNode, EntityNode childNode) - { - // 1) If the child already has a parent, detach it first - var childHierarchy = scene.World.EntityManager.GetComponent(childNode.Entity); - if (childHierarchy.ValueRO.parent != Entity.Invalid) - { - DetachFromParent(scene, childNode); - } - - // 2) Link child to new parent - childHierarchy.ValueRW.parent = parentNode.Entity; - - // 3) Insert child at the head of parent's child list - var parentHierarchy = scene.World.EntityManager.GetComponent(parentNode.Entity); - - childHierarchy.ValueRW.nextSibling = parentHierarchy.ValueRO.firstChild; - parentHierarchy.ValueRW.firstChild = childNode.Entity; - - // 4) Write back - scene.World.EntityManager.SetComponent(parentNode.Entity, in parentHierarchy.ValueRO); - scene.World.EntityManager.SetComponent(childNode.Entity, in childHierarchy.ValueRO); - - // 5) Update children list in parent node - parentNode.AddChild(childNode); - } - - /// - /// Detaches the specified entity from its parent in the scene graph. - /// - /// The world context where the entities exist. - /// The entity to detach from its parent. - public static void DetachFromParent(WorldNode scene, EntityNode node) - { - var hierarchy = scene.World.EntityManager.GetComponent(node.Entity); - var parent = hierarchy.ValueRO.parent; - if (parent == Entity.Invalid) - { - return; // already root - } - - var parentHierarchy = scene.World.EntityManager.GetComponent(parent); - - // If entity is the first child, simply move head - if (parentHierarchy.ValueRO.firstChild == node.Entity) - { - parentHierarchy.ValueRW.firstChild = hierarchy.ValueRO.nextSibling; - } - else - { - // Otherwise, find the previous sibling in the linked list - var prevSibling = parentHierarchy.ValueRO.firstChild; - while (prevSibling != Entity.Invalid) - { - var prevHierarchy = scene.World.EntityManager.GetComponent(prevSibling); - if (prevHierarchy.ValueRW.nextSibling == node.Entity) - { - prevHierarchy.ValueRW.nextSibling = hierarchy.ValueRO.nextSibling; - scene.World.EntityManager.SetComponent(prevSibling, in prevHierarchy.ValueRO); - break; - } - - prevSibling = prevHierarchy.ValueRO.nextSibling; - } - } - - // Clear child's references - hierarchy.ValueRW.parent = Entity.Invalid; - hierarchy.ValueRW.nextSibling = Entity.Invalid; - - // Write back - scene.World.EntityManager.SetComponent(parent, in parentHierarchy.ValueRO); - scene.World.EntityManager.SetComponent(node.Entity, in hierarchy.ValueRO); - - // Remove from parent's children list - scene.EntityNodeLookup[parent].RemoveChild(node); - } -} \ No newline at end of file diff --git a/Ghost.Editor/SceneGraph/SceneGraphNode.cs b/Ghost.Editor/SceneGraph/SceneGraphNode.cs deleted file mode 100644 index f177ce1..0000000 --- a/Ghost.Editor/SceneGraph/SceneGraphNode.cs +++ /dev/null @@ -1,54 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using System.Collections.ObjectModel; - -namespace Ghost.Editor.SceneGraph; - -public enum SceneGraphNodeType -{ - Scene, - Entity, -} - -public abstract partial class SceneGraphNode : ObservableObject -{ - public ObservableCollection? Children - { - get; - private set; - } - - [ObservableProperty] - public partial string Name - { - get; - set; - } - - public abstract SceneGraphNodeType NodeType - { - get; - } - - public int ChildCount => Children?.Count ?? 0; - - public virtual void AddChild(SceneGraphNode child) - { - Children ??= new(); - Children.Add(child); - } - - public virtual bool RemoveChild(SceneGraphNode child) - { - return Children?.Remove(child) ?? false; - } - - public SceneGraphNode GetChild(int index) - { - if (Children == null || index < 0 || index >= Children.Count) - { - throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); - } - - return Children[index]; - } -} \ No newline at end of file diff --git a/Ghost.Editor/SceneGraph/WorldNode.cs b/Ghost.Editor/SceneGraph/WorldNode.cs deleted file mode 100644 index ddc6be6..0000000 --- a/Ghost.Editor/SceneGraph/WorldNode.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Ghost.Editor.Contracts; -using Ghost.Editor.Serializer; -using Ghost.Engine.Components; -using Ghost.Entities; -using Microsoft.UI.Xaml; -using System.Text.Json.Serialization; - -namespace Ghost.Editor.SceneGraph; - -[JsonConverter(typeof(WorldNodeSerializer))] -public partial class WorldNode : SceneGraphNode, IEquatable -{ - private World _world; - private Dictionary _entityNodeLookup = new(); - - public World World => _world; - public Dictionary EntityNodeLookup => _entityNodeLookup; - - public override SceneGraphNodeType NodeType => SceneGraphNodeType.Scene; - - public WorldNode(World world, string name) - { - _world = world; - Name = name; - } - - internal WorldNode() - { - _world = World.Create(); - } - - private void UpdateLookup(Entity key, EntityNode value) - { - _entityNodeLookup[key] = value; - if (value.Children == null) - { - return; - } - - foreach (var child in value.Children) - { - if (child is EntityNode entityChild) - { - UpdateLookup(entityChild.Entity, entityChild); - } - } - } - - public override void AddChild(SceneGraphNode child) - { - if (child is not EntityNode entityNode) - { - throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); - } - - base.AddChild(entityNode); - UpdateLookup(entityNode.Entity, entityNode); - } - - public override bool RemoveChild(SceneGraphNode child) - { - if (child is not EntityNode entityNode) - { - throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); - } - - var result = base.RemoveChild(child); - if (result) - { - _entityNodeLookup.Remove(entityNode.Entity); - } - - return result; - } - - private EntityNode BuildNodeRecursive(Entity entity, World world) - { - if (!_entityNodeLookup.TryGetValue(entity, out var node)) - { - node = new EntityNode(entity, "New Entity"); - _entityNodeLookup[entity] = node; - } - - var hc = world.EntityManager.GetComponent(entity); - var child = hc.ValueRO.firstChild; - - while (child != Entity.Invalid) - { - node.AddChild(BuildNodeRecursive(child, world)); - var childHC = world.EntityManager.GetComponent(child); - child = childHC.ValueRO.nextSibling; - } - - return node; - } - - private void BuildGraph() - { - foreach (var (entity, hierarchy) in _world.Query()) - { - if (hierarchy.ValueRO.parent == Entity.Invalid) - { - var node = BuildNodeRecursive(entity, _world); - AddChild(node); - } - } - } - - public Task LoadAsync() - { - return Task.Run(BuildGraph); - } - - public void Unload() - { - _world.Dispose(); - _world = null!; - - Children?.Clear(); - _entityNodeLookup.Clear(); - } - - public override string ToString() - { - return $"WorldNode: {Name} (World ID: {_world.ID})"; - } - - public override int GetHashCode() - { - return HashCode.Combine(_world, Name); - } - - public override bool Equals(object? obj) - { - return obj is WorldNode other && Equals(other); - } - - public bool Equals(WorldNode? other) - { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return _world.Equals(other._world) && Name == other.Name; - } - - public static bool operator ==(WorldNode? left, WorldNode? right) - { - if (left is null) - { - return right is null; - } - return left.Equals(right); - } - - public static bool operator !=(WorldNode? left, WorldNode? right) - { - return !(left == right); - } -} - -public partial class WorldNode : IInspectable -{ - public UIElement HeaderContent() - { - throw new NotImplementedException(); - } - - public UIElement InspectorContent() - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/Ghost.Editor/Serializer/WorldNodeSerializer.cs b/Ghost.Editor/Serializer/WorldNodeSerializer.cs deleted file mode 100644 index 011e759..0000000 --- a/Ghost.Editor/Serializer/WorldNodeSerializer.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Ghost.Editor.SceneGraph; -using Ghost.Engine.Utilities; -using Ghost.Entities; -using Ghost.Entities.Components; -using Ghost.Entities.Utilities; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Ghost.Editor.Serializer; - -internal class WorldNodeSerializer : JsonConverter -{ - private static class Property - { - public const string NAME = "Name"; - public const string ENTITIES = "Entities"; - public const string ID = "ID"; - public const string ENTITY_ID = "EntityID"; - public const string COMPONENTS = "Components"; - public const string DATA = "Data"; - public const string SYSTEMS = "Systems"; - } - - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert == typeof(WorldNode) || typeToConvert.IsSubclassOf(typeof(WorldNode)); - } - - public override WorldNode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var element = JsonDocument.ParseValue(ref reader).RootElement; - var name = element.GetProperty(Property.NAME).GetString() ?? "New World"; - - var world = World.Create(); - var result = new WorldNode(world, name); - - foreach (var entityElement in element.GetProperty(Property.ENTITIES).EnumerateArray()) - { - var entityName = entityElement.GetProperty(Property.NAME).GetString() ?? "New Entity"; - var entityID = entityElement.GetProperty(Property.ID).GetInt32(); - var entity = new Entity(entityID, 0); - var node = new EntityNode(entity, entityName); - - world.EntityManager.AddEntityInternal(entity); - result.EntityNodeLookup[entity] = node; - } - - foreach (var componentElement in element.GetProperty(Property.COMPONENTS).EnumerateObject()) - { - var typeName = componentElement.Name; - var type = Type.GetType(typeName) ?? throw new Exception($"Type {typeName} not found."); - - foreach (var dataElement in componentElement.Value.EnumerateArray()) - { - var entityID = dataElement.GetProperty(Property.ENTITY_ID).GetInt32(); - var entity = new Entity(entityID, 0); - - var dataProperty = dataElement.GetProperty(Property.DATA); - var component = JsonSerializer.Deserialize(dataProperty.GetRawText(), type, options); - if (component is IComponentData data) - { - world.EntityManager.AddComponent(entity, data, type); - } - } - } - - foreach (var systemElement in element.GetProperty(Property.SYSTEMS).EnumerateArray()) - { - var typeString = systemElement.GetString(); - if (string.IsNullOrEmpty(typeString)) - { - continue; - } - - var systemType = Type.GetType(typeString); - if (systemType == null) - { - continue; - } - - world.SystemStorage.AddSystem(systemType); - } - - return result; - } - - public override void Write(Utf8JsonWriter writer, WorldNode value, JsonSerializerOptions options) - { - writer.WriteObject(() => - { - writer.WriteString(Property.NAME, value.Name); - writer.WriteArray(Property.ENTITIES, value.World.EntityManager.Entities, entity => - { - if (!entity.IsValid) - { - return; - } - - writer.WriteObject(() => - { - writer.WriteString(Property.NAME, value.EntityNodeLookup[entity].Name); - writer.WriteNumber(Property.ID, entity.ID); - }); - }); - - writer.WriteObject(Property.COMPONENTS, () => - { - foreach (var kvp in value.World.ComponentStorage.ComponentPools) - { - var type = TypeHandle.ToType(kvp.Key) ?? throw new Exception($"Type {kvp.Key} not found."); - var typeName = type.AssemblyQualifiedName ?? type.Name; - - writer.WriteArray(typeName, kvp.Value.Enumerate(), data => - { - writer.WriteObject(() => - { - writer.WriteNumber(Property.ENTITY_ID, data.entity.ID); - writer.WritePropertyName(Property.DATA); - JsonSerializer.Serialize(writer, data.component, type, options); - }); - }); - } - }); - - writer.WriteArray(Property.SYSTEMS, value.World.SystemStorage.Systems, systemType => - { - writer.WriteStringValue(systemType.AssemblyQualifiedName ?? systemType.Name); - }); - }); - } -} \ No newline at end of file diff --git a/Ghost.App/Themes/Override.xaml b/Ghost.Editor/Themes/Override.xaml similarity index 100% rename from Ghost.App/Themes/Override.xaml rename to Ghost.Editor/Themes/Override.xaml diff --git a/Ghost.App/Utilities/Converters/AssetPathToGlyphConverter.cs b/Ghost.Editor/Utilities/Converters/AssetPathToGlyphConverter.cs similarity index 100% rename from Ghost.App/Utilities/Converters/AssetPathToGlyphConverter.cs rename to Ghost.Editor/Utilities/Converters/AssetPathToGlyphConverter.cs diff --git a/Ghost.App/Utilities/Converters/GetDirectoryNameConverter .cs b/Ghost.Editor/Utilities/Converters/GetDirectoryNameConverter .cs similarity index 100% rename from Ghost.App/Utilities/Converters/GetDirectoryNameConverter .cs rename to Ghost.Editor/Utilities/Converters/GetDirectoryNameConverter .cs diff --git a/Ghost.App/Utilities/Converters/Vector3ToQuaternionConverter.cs b/Ghost.Editor/Utilities/Converters/Vector3ToQuaternionConverter.cs similarity index 100% rename from Ghost.App/Utilities/Converters/Vector3ToQuaternionConverter.cs rename to Ghost.Editor/Utilities/Converters/Vector3ToQuaternionConverter.cs diff --git a/Ghost.App/Utilities/HostHelpers.Page.cs b/Ghost.Editor/Utilities/HostHelpers.Page.cs similarity index 97% rename from Ghost.App/Utilities/HostHelpers.Page.cs rename to Ghost.Editor/Utilities/HostHelpers.Page.cs index 7941a5b..755ec5d 100644 --- a/Ghost.App/Utilities/HostHelpers.Page.cs +++ b/Ghost.Editor/Utilities/HostHelpers.Page.cs @@ -33,6 +33,8 @@ internal static partial class HostHelper services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); diff --git a/Ghost.App/Utilities/ReflectionBinding.cs b/Ghost.Editor/Utilities/ReflectionBinding.cs similarity index 100% rename from Ghost.App/Utilities/ReflectionBinding.cs rename to Ghost.Editor/Utilities/ReflectionBinding.cs diff --git a/Ghost.App/Utilities/SystemUtility.cs b/Ghost.Editor/Utilities/SystemUtility.cs similarity index 82% rename from Ghost.App/Utilities/SystemUtility.cs rename to Ghost.Editor/Utilities/SystemUtility.cs index 388a4fb..3d3614d 100644 --- a/Ghost.App/Utilities/SystemUtility.cs +++ b/Ghost.Editor/Utilities/SystemUtility.cs @@ -1,8 +1,4 @@ -using Ghost.Editor; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Windows.Storage; +using Windows.Storage; using Windows.Storage.Pickers; using WinRT.Interop; @@ -13,7 +9,7 @@ public static class SystemUtilities public static async Task OpenFolderPickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "") { var openPicker = new FolderPicker(); - var hWnd = WindowNative.GetWindowHandle(EditorApplication.Window); + var hWnd = WindowNative.GetWindowHandle(App.Window); InitializeWithWindow.Initialize(openPicker, hWnd); openPicker.SuggestedStartLocation = startLocation; @@ -27,7 +23,7 @@ public static class SystemUtilities public static async Task OpenFilePickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "", params IEnumerable filter) { var openPicker = new FileOpenPicker(); - var hWnd = WindowNative.GetWindowHandle(EditorApplication.Window); + var hWnd = WindowNative.GetWindowHandle(App.Window); InitializeWithWindow.Initialize(openPicker, hWnd); openPicker.SuggestedStartLocation = startLocation; diff --git a/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml b/Ghost.Editor/View/Pages/EngineEditor/ConsolePage.xaml similarity index 100% rename from Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml rename to Ghost.Editor/View/Pages/EngineEditor/ConsolePage.xaml diff --git a/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs b/Ghost.Editor/View/Pages/EngineEditor/ConsolePage.xaml.cs similarity index 81% rename from Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs rename to Ghost.Editor/View/Pages/EngineEditor/ConsolePage.xaml.cs index c606188..192e038 100644 --- a/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs +++ b/Ghost.Editor/View/Pages/EngineEditor/ConsolePage.xaml.cs @@ -12,7 +12,7 @@ internal sealed partial class ConsolePage : Page public ConsolePage() { - ViewModel = EditorApplication.GetService(); + ViewModel = App.GetService(); InitializeComponent(); } diff --git a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml b/Ghost.Editor/View/Pages/EngineEditor/HierarchyPage.xaml similarity index 100% rename from Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml rename to Ghost.Editor/View/Pages/EngineEditor/HierarchyPage.xaml diff --git a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs b/Ghost.Editor/View/Pages/EngineEditor/HierarchyPage.xaml.cs similarity index 85% rename from Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs rename to Ghost.Editor/View/Pages/EngineEditor/HierarchyPage.xaml.cs index e088031..6af8a04 100644 --- a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs +++ b/Ghost.Editor/View/Pages/EngineEditor/HierarchyPage.xaml.cs @@ -1,7 +1,6 @@ using Ghost.Editor.Controls.Internal; using Ghost.Editor.Core.Inspector; using Ghost.Editor.Core.SceneGraph; -using Ghost.Editor.Services.Contracts; using Ghost.Editor.ViewModels.Pages.EngineEditor; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -19,16 +18,10 @@ internal sealed partial class HierarchyPage : NavigationTabPage public HierarchyPage() { - _inspectorService = EditorApplication.GetService(); - ViewModel = EditorApplication.GetService(); + _inspectorService = App.GetService(); + ViewModel = App.GetService(); InitializeComponent(); - - Header = "Hierarchy"; - IconSource = new FontIconSource - { - Glyph = "\uE8A4" - }; } public override void OnNavigatedTo(object? parameter) diff --git a/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml b/Ghost.Editor/View/Pages/EngineEditor/InspectorPage.xaml similarity index 100% rename from Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml rename to Ghost.Editor/View/Pages/EngineEditor/InspectorPage.xaml diff --git a/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs b/Ghost.Editor/View/Pages/EngineEditor/InspectorPage.xaml.cs similarity index 70% rename from Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs rename to Ghost.Editor/View/Pages/EngineEditor/InspectorPage.xaml.cs index 3a1e0c8..2e01e73 100644 --- a/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs +++ b/Ghost.Editor/View/Pages/EngineEditor/InspectorPage.xaml.cs @@ -1,6 +1,5 @@ using Ghost.Editor.Controls.Internal; using Ghost.Editor.ViewModels.Pages.EngineEditor; -using Microsoft.UI.Xaml.Controls; namespace Ghost.Editor.View.Pages.EngineEditor; @@ -13,15 +12,9 @@ internal sealed partial class InspectorPage : NavigationTabPage public InspectorPage() { - ViewModel = EditorApplication.GetService(); + ViewModel = App.GetService(); InitializeComponent(); - - Header = "Inspector"; - IconSource = new FontIconSource - { - Glyph = "\uEC7A" - }; } public override void OnNavigatedTo(object? parameter) diff --git a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml b/Ghost.Editor/View/Pages/EngineEditor/ProjectPage.xaml similarity index 100% rename from Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml rename to Ghost.Editor/View/Pages/EngineEditor/ProjectPage.xaml diff --git a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs b/Ghost.Editor/View/Pages/EngineEditor/ProjectPage.xaml.cs similarity index 87% rename from Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs rename to Ghost.Editor/View/Pages/EngineEditor/ProjectPage.xaml.cs index 1292b88..d5efdf0 100644 --- a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs +++ b/Ghost.Editor/View/Pages/EngineEditor/ProjectPage.xaml.cs @@ -13,7 +13,7 @@ internal sealed partial class ProjectPage : Page public ProjectPage() { - ViewModel = EditorApplication.GetService(); + ViewModel = App.GetService(); InitializeComponent(); } diff --git a/Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml b/Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml new file mode 100644 index 0000000..c950e0b --- /dev/null +++ b/Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml.cs b/Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml.cs new file mode 100644 index 0000000..ab2dec2 --- /dev/null +++ b/Ghost.Editor/View/Pages/EngineEditor/ScenePage.xaml.cs @@ -0,0 +1,54 @@ +using Ghost.Editor.Controls.Internal; +using Ghost.Graphics; +using Ghost.Graphics.Contracts; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; +using SharpGen.Runtime; +using WinRT; + +namespace Ghost.Editor.View.Pages.EngineEditor; + +internal sealed partial class ScenePage : NavigationTabPage +{ + private IRenderView? _renderer; + + public ScenePage() + { + InitializeComponent(); + + SwapChainPanel.Loaded += SwapChainPanel_Loaded; + SwapChainPanel.Unloaded += SwapChainPanel_Unloaded; + SwapChainPanel.SizeChanged += SwapChainPanel_SizeChanged; + } + + private void OnRendering(object? sender, object e) + { + _renderer?.Render(); + } + + private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e) + { + var guid = typeof(Vortice.WinUI.ISwapChainPanelNative).GUID; + Result result = ((IWinRTObject)SwapChainPanel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle); + result.CheckError(); + + var swapChainPanelNative = new Vortice.WinUI.ISwapChainPanelNative(swapChainPanelNativeHandle); + _renderer = GraphicsPipeline.GraphicsDevice.CreateRenderView(new(swapChainPanelNative, (uint)SwapChainPanel.ActualWidth, (uint)SwapChainPanel.ActualHeight)); + + CompositionTarget.Rendering += OnRendering; + } + + private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e) + { + CompositionTarget.Rendering -= OnRendering; + _renderer?.Dispose(); + } + + private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e) + { + if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0) + { + _renderer?.Resize((uint)e.NewSize.Width, (uint)e.NewSize.Height); + } + } +} \ No newline at end of file diff --git a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml b/Ghost.Editor/View/Pages/Landing/CreateProjectPage.xaml similarity index 100% rename from Ghost.App/View/Pages/Landing/CreateProjectPage.xaml rename to Ghost.Editor/View/Pages/Landing/CreateProjectPage.xaml diff --git a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs b/Ghost.Editor/View/Pages/Landing/CreateProjectPage.xaml.cs similarity index 87% rename from Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs rename to Ghost.Editor/View/Pages/Landing/CreateProjectPage.xaml.cs index c645e4c..1acca94 100644 --- a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs +++ b/Ghost.Editor/View/Pages/Landing/CreateProjectPage.xaml.cs @@ -13,7 +13,7 @@ internal sealed partial class CreateProjectPage : Page public CreateProjectPage() { - ViewModel = EditorApplication.GetService(); + ViewModel = App.GetService(); InitializeComponent(); } diff --git a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml b/Ghost.Editor/View/Pages/Landing/OpenProjectPage.xaml similarity index 100% rename from Ghost.App/View/Pages/Landing/OpenProjectPage.xaml rename to Ghost.Editor/View/Pages/Landing/OpenProjectPage.xaml diff --git a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs b/Ghost.Editor/View/Pages/Landing/OpenProjectPage.xaml.cs similarity index 96% rename from Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs rename to Ghost.Editor/View/Pages/Landing/OpenProjectPage.xaml.cs index a6507b1..bb70791 100644 --- a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs +++ b/Ghost.Editor/View/Pages/Landing/OpenProjectPage.xaml.cs @@ -16,7 +16,7 @@ internal sealed partial class OpenProjectPage : Page public OpenProjectPage() { - ViewModel = EditorApplication.GetService(); + ViewModel = App.GetService(); InitializeComponent(); } diff --git a/Ghost.App/View/Windows/EngineEditorWindow.xaml b/Ghost.Editor/View/Windows/EngineEditorWindow.xaml similarity index 92% rename from Ghost.App/View/Windows/EngineEditorWindow.xaml rename to Ghost.Editor/View/Windows/EngineEditorWindow.xaml index a5ec2d1..70defcf 100644 --- a/Ghost.App/View/Windows/EngineEditorWindow.xaml +++ b/Ghost.Editor/View/Windows/EngineEditorWindow.xaml @@ -93,21 +93,21 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + + + + + - - + + - - - + + @@ -117,7 +117,11 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + + + + + diff --git a/Ghost.App/View/Windows/EngineEditorWindow.xaml.cs b/Ghost.Editor/View/Windows/EngineEditorWindow.xaml.cs similarity index 79% rename from Ghost.App/View/Windows/EngineEditorWindow.xaml.cs rename to Ghost.Editor/View/Windows/EngineEditorWindow.xaml.cs index 88131db..cb02dbe 100644 --- a/Ghost.App/View/Windows/EngineEditorWindow.xaml.cs +++ b/Ghost.Editor/View/Windows/EngineEditorWindow.xaml.cs @@ -1,6 +1,6 @@ using Ghost.Data.Resources; -using Ghost.Editor.Services; -using Ghost.Editor.Services.Contracts; +using Ghost.Editor.Core.Notifications; +using Ghost.Editor.Core.Progress; using Ghost.Editor.ViewModels.Windows; using Ghost.Engine.Resources; using Microsoft.Extensions.DependencyInjection; @@ -27,10 +27,10 @@ internal sealed partial class EngineEditorWindow : WindowEx public EngineEditorWindow() { - ViewModel = EditorApplication.GetService(); + ViewModel = App.GetService(); - _notificationService = (NotificationService)EditorApplication.GetService(); - _progressService = (ProgressService)EditorApplication.GetService(); + _notificationService = (NotificationService)App.GetService(); + _progressService = (ProgressService)App.GetService(); AppWindow.SetIcon(AssetsPath.s_appIconPath); Title = EngineData.ENGINE_NAME; @@ -46,7 +46,7 @@ internal sealed partial class EngineEditorWindow : WindowEx Bindings.Update(); _editorScope?.Dispose(); - _editorScope = EditorApplication.CreateScope(); + _editorScope = App.CreateScope(); _notificationService.SetReference(InfoBar, NotificationQueue); _progressService.SetReference(ProgressBarContainer); diff --git a/Ghost.App/View/Windows/LandingWindow.xaml b/Ghost.Editor/View/Windows/LandingWindow.xaml similarity index 100% rename from Ghost.App/View/Windows/LandingWindow.xaml rename to Ghost.Editor/View/Windows/LandingWindow.xaml diff --git a/Ghost.App/View/Windows/LandingWindow.xaml.cs b/Ghost.Editor/View/Windows/LandingWindow.xaml.cs similarity index 86% rename from Ghost.App/View/Windows/LandingWindow.xaml.cs rename to Ghost.Editor/View/Windows/LandingWindow.xaml.cs index a26c2e9..996ae41 100644 --- a/Ghost.App/View/Windows/LandingWindow.xaml.cs +++ b/Ghost.Editor/View/Windows/LandingWindow.xaml.cs @@ -1,7 +1,6 @@ -using Ghost.Editor.View.Pages.Landing; -using Ghost.Data.Resources; -using Ghost.Editor.Services; -using Ghost.Editor.Services.Contracts; +using Ghost.Data.Resources; +using Ghost.Editor.Core.Notifications; +using Ghost.Editor.View.Pages.Landing; using Ghost.Engine.Resources; using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Xaml.Controls; @@ -20,7 +19,7 @@ internal sealed partial class LandingWindow : WindowEx public LandingWindow() { - _notificationService = (NotificationService)EditorApplication.GetService(); + _notificationService = (NotificationService)App.GetService(); AppWindow.SetIcon(AssetsPath.s_appIconPath); Title = EngineData.ENGINE_NAME; @@ -36,7 +35,7 @@ internal sealed partial class LandingWindow : WindowEx private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args) { _landingScope?.Dispose(); - _landingScope = EditorApplication.CreateScope(); + _landingScope = App.CreateScope(); _notificationService.SetReference(InfoBar, NotificationQueue); } diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/ConsoleViewModel.cs b/Ghost.Editor/ViewModels/Pages/EngineEditor/ConsoleViewModel.cs similarity index 100% rename from Ghost.App/ViewModels/Pages/EngineEditor/ConsoleViewModel.cs rename to Ghost.Editor/ViewModels/Pages/EngineEditor/ConsoleViewModel.cs diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs b/Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs similarity index 96% rename from Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs rename to Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs index 35aec54..36a81d6 100644 --- a/Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs +++ b/Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs @@ -1,5 +1,5 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.Editor.Contracts; +using Ghost.Editor.Core.Contracts; using Ghost.Editor.Core.SceneGraph; using System.Collections.ObjectModel; diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs b/Ghost.Editor/ViewModels/Pages/EngineEditor/InspectorViewModel.cs similarity index 92% rename from Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs rename to Ghost.Editor/ViewModels/Pages/EngineEditor/InspectorViewModel.cs index 892d783..034cedb 100644 --- a/Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs +++ b/Ghost.Editor/ViewModels/Pages/EngineEditor/InspectorViewModel.cs @@ -1,7 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.Editor.Contracts; +using Ghost.Editor.Core.Contracts; using Ghost.Editor.Core.Inspector; -using Ghost.Editor.Services.Contracts; namespace Ghost.Editor.ViewModels.Pages.EngineEditor; diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs b/Ghost.Editor/ViewModels/Pages/EngineEditor/ProjectViewModel.cs similarity index 98% rename from Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs rename to Ghost.Editor/ViewModels/Pages/EngineEditor/ProjectViewModel.cs index 288f0f3..c09a3e3 100644 --- a/Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs +++ b/Ghost.Editor/ViewModels/Pages/EngineEditor/ProjectViewModel.cs @@ -91,7 +91,7 @@ internal partial class ProjectViewModel : ObservableObject private void NavigateToDirectory(string? path) { - EditorApplication.Window?.DispatcherQueue.TryEnqueue(async () => + App.Window?.DispatcherQueue.TryEnqueue(async () => { DirectoryAssets.Clear(); diff --git a/Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs b/Ghost.Editor/ViewModels/Pages/Landing/CreateProjectViewModel.cs similarity index 93% rename from Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs rename to Ghost.Editor/ViewModels/Pages/Landing/CreateProjectViewModel.cs index fb6b54d..820eccc 100644 --- a/Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs +++ b/Ghost.Editor/ViewModels/Pages/Landing/CreateProjectViewModel.cs @@ -2,16 +2,12 @@ using CommunityToolkit.Mvvm.Input; using Ghost.Data.Models; using Ghost.Data.Services; -using Ghost.Editor.Contracts; using Ghost.Editor.Core.AppState; -using Ghost.Editor.Models; -using Ghost.Editor.Services; +using Ghost.Editor.Core.Contracts; +using Ghost.Editor.Core.Notifications; using Ghost.Editor.Utilities; using Ghost.Engine.Resources; using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Threading.Tasks; namespace Ghost.Editor.ViewModels.Pages.Landing; @@ -85,7 +81,7 @@ internal partial class CreateProjectViewModel(NotificationService notificationSe try { - await stateService.TransitionToAsync(StateKey.EngineEditor, result.data); + await stateService.TransitionToAsync(StateKey.EngineEditor, result.value); } catch (System.Exception e) { diff --git a/Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs b/Ghost.Editor/ViewModels/Pages/Landing/OpenProjectViewModel.cs similarity index 95% rename from Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs rename to Ghost.Editor/ViewModels/Pages/Landing/OpenProjectViewModel.cs index e46c5d7..9d26cb3 100644 --- a/Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs +++ b/Ghost.Editor/ViewModels/Pages/Landing/OpenProjectViewModel.cs @@ -1,10 +1,9 @@ using CommunityToolkit.Mvvm.ComponentModel; using Ghost.Data.Models; using Ghost.Data.Services; -using Ghost.Editor.Contracts; using Ghost.Editor.Core.AppState; -using Ghost.Editor.Models; -using Ghost.Editor.Services.Contracts; +using Ghost.Editor.Core.Contracts; +using Ghost.Editor.Core.Notifications; using Microsoft.UI.Xaml; using System.Collections.ObjectModel; using Windows.ApplicationModel.DataTransfer; @@ -69,7 +68,7 @@ internal partial class OpenProjectViewModel(ProjectService projectService, INoti var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path); if (result.success) { - projects.Add(result.data); + projects.Add(result.value); goto CloseDropPanel; } else diff --git a/Ghost.App/ViewModels/Windows/EngineEditorViewModel.cs b/Ghost.Editor/ViewModels/Windows/EngineEditorViewModel.cs similarity index 100% rename from Ghost.App/ViewModels/Windows/EngineEditorViewModel.cs rename to Ghost.Editor/ViewModels/Windows/EngineEditorViewModel.cs diff --git a/Ghost.App/app.manifest b/Ghost.Editor/app.manifest similarity index 100% rename from Ghost.App/app.manifest rename to Ghost.Editor/app.manifest diff --git a/Ghost.Engine/EngineCore.cs b/Ghost.Engine/EngineCore.cs index f8f74fe..83dbc1c 100644 --- a/Ghost.Engine/EngineCore.cs +++ b/Ghost.Engine/EngineCore.cs @@ -1,13 +1,16 @@ using Ghost.Engine.Models; using Ghost.Engine.Services; +using Ghost.Graphics; +using Ghost.Graphics.Data; namespace Ghost.Engine; -internal class EngineCore : IDisposable, IAsyncDisposable +internal class EngineCore { public async Task StartAsync(LaunchArgument args) { ActivationHandler.Handle(args); + GraphicsPipeline.Initialize(GraphicsAPI.DX12); Logger.LogInfo("Engine started successfully."); @@ -16,16 +19,7 @@ internal class EngineCore : IDisposable, IAsyncDisposable public async Task ShutDownAsync() { + GraphicsPipeline.Shutdown(); await Task.CompletedTask; } - - public void Dispose() - { - ShutDownAsync().GetAwaiter().GetResult(); - } - - public async ValueTask DisposeAsync() - { - await ShutDownAsync(); - } } \ No newline at end of file diff --git a/Ghost.Engine/Ghost.Engine.csproj b/Ghost.Engine/Ghost.Engine.csproj index f3c3142..96716b3 100644 --- a/Ghost.Engine/Ghost.Engine.csproj +++ b/Ghost.Engine/Ghost.Engine.csproj @@ -17,7 +17,14 @@ + + + + ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll + + + diff --git a/Ghost.Engine/Models/LogMessage.cs b/Ghost.Engine/Models/LogMessage.cs index 948b153..4a21bda 100644 --- a/Ghost.Engine/Models/LogMessage.cs +++ b/Ghost.Engine/Models/LogMessage.cs @@ -14,7 +14,7 @@ internal class LogMessage get; set; } - public string Message + public string? Message { get; set; } @@ -29,7 +29,7 @@ internal class LogMessage get; set; } - public LogMessage(LogLevel level, string message, string? stackTrace = null) + public LogMessage(LogLevel level, string? message, string? stackTrace = null) { Level = level; Message = message; diff --git a/Ghost.Engine/Services/Logger.cs b/Ghost.Engine/Services/Logger.cs index d3ef227..6f27b40 100644 --- a/Ghost.Engine/Services/Logger.cs +++ b/Ghost.Engine/Services/Logger.cs @@ -25,7 +25,7 @@ public static class Logger get; set; } - private static void LogInternal(LogLevel level, string message, int skipFrame) + private static void LogInternal(LogLevel level, string? message, int skipFrame) { if (_logs.Count >= _MAX_LOGS) { @@ -59,22 +59,22 @@ public static class Logger OnLogsUpdate?.Invoke(LogChangeType.LogAdded); } - public static void Log(LogLevel level, string message) + public static void Log(LogLevel level, string? message) { LogInternal(level, message, 2); } - public static void LogInfo(string message) + public static void LogInfo(string? message) { LogInternal(LogLevel.Info, message, 3); } - public static void LogWarning(string message) + public static void LogWarning(string? message) { LogInternal(LogLevel.Warning, message, 3); } - public static void LogError(string message) + public static void LogError(string? message) { LogInternal(LogLevel.Error, message, 3); } @@ -84,6 +84,14 @@ public static class Logger LogExceptionInternal(ex); } + public static void Assert(bool condition, string? message = null) + { + if (!condition) + { + LogInternal(LogLevel.Error, message ?? "Assertion failed", 3); + } + } + internal static void Clear() { _logs.Clear(); diff --git a/Ghost.Entities/AssemblyInfo.cs b/Ghost.Entities/AssemblyInfo.cs index 6e543db..0416c34 100644 --- a/Ghost.Entities/AssemblyInfo.cs +++ b/Ghost.Entities/AssemblyInfo.cs @@ -5,5 +5,5 @@ global using WorldID = System.UInt16; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Ghost.Engine")] -[assembly: InternalsVisibleTo("Ghost.Editor")] +[assembly: InternalsVisibleTo("Ghost.Editor.Core")] [assembly: InternalsVisibleTo("Ghost.UnitTest")] \ No newline at end of file diff --git a/Ghost.Entities/Components/ComponentStorage.cs b/Ghost.Entities/Components/ComponentStorage.cs index 460d711..9742979 100644 --- a/Ghost.Entities/Components/ComponentStorage.cs +++ b/Ghost.Entities/Components/ComponentStorage.cs @@ -1,4 +1,4 @@ -using Ghost.Entities.Utilities; +using Ghost.Core; using Misaki.HighPerformance.Unsafe.Collections; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -463,8 +463,8 @@ internal class ScriptComponentPool : IComponentPool [SkipLocalsInit] internal readonly struct ComponentStorage : IDisposable { - private readonly Dictionary _componentPools = new(); - private readonly Dictionary _componentEntityMasks = new(); + private readonly Dictionary _componentPools = new(); + private readonly Dictionary _componentEntityMasks = new(); private readonly ScriptComponentPool _scriptComponentPool = new(); private readonly World _world; @@ -474,12 +474,12 @@ internal readonly struct ComponentStorage : IDisposable _world = world; } - internal Dictionary ComponentPools => _componentPools; - internal Dictionary ComponentEntityMasks => _componentEntityMasks; + internal Dictionary ComponentPools => _componentPools; + internal Dictionary ComponentEntityMasks => _componentEntityMasks; internal ScriptComponentPool ScriptComponentPool => _scriptComponentPool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetPool(nint typeHandle, [MaybeNullWhen(false)] out IComponentPool pool) + public bool TryGetPool(TypeHandle typeHandle, [MaybeNullWhen(false)] out IComponentPool pool) { return _componentPools.TryGetValue(typeHandle, out pool); } @@ -514,7 +514,7 @@ internal readonly struct ComponentStorage : IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetMask(nint typeHandle, [MaybeNullWhen(false)] out BitSet bitSet) + public bool TryGetMask(TypeHandle typeHandle, [MaybeNullWhen(false)] out BitSet bitSet) { return _componentEntityMasks.TryGetValue(typeHandle, out bitSet); } @@ -522,7 +522,7 @@ internal readonly struct ComponentStorage : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetMask(Type type, [MaybeNullWhen(false)] out BitSet bitSet) { - return TryGetMask(type.TypeHandle.Value, out bitSet); + return TryGetMask(TypeHandle.Get(type), out bitSet); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -532,7 +532,7 @@ internal readonly struct ComponentStorage : IDisposable return TryGetMask(TypeHandle.Get(), out bitSet); } - public BitSet GetOrCreateMask(nint typeHandle) + public BitSet GetOrCreateMask(TypeHandle typeHandle) { if (!_componentEntityMasks.TryGetValue(typeHandle, out var mask)) { diff --git a/Ghost.Entities/EntityManager.cs b/Ghost.Entities/EntityManager.cs index cf13561..00f796e 100644 --- a/Ghost.Entities/EntityManager.cs +++ b/Ghost.Entities/EntityManager.cs @@ -1,10 +1,11 @@ -using Ghost.Entities.Components; +using Ghost.Core; +using Ghost.Entities.Components; using Ghost.Entities.Query; -using Ghost.Entities.Utilities; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ghost.Entities; + public readonly struct EntityManager : IDisposable { private readonly List _entities; @@ -181,7 +182,7 @@ public readonly struct EntityManager : IDisposable /// The handle of the component type. /// True if the entity has the component; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool HasComponent(Entity entity, nint typeHandle) + public readonly bool HasComponent(Entity entity, TypeHandle typeHandle) { return _world.ComponentStorage.TryGetMask(typeHandle, out var bitSet) && bitSet.IsSet(entity.ID); } @@ -238,7 +239,7 @@ public readonly struct EntityManager : IDisposable /// An enumerable collection of representing the memory addresses of the components associated /// with the specified entity. The collection will be empty if the entity has no associated components. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly IEnumerable<(IntPtr, IntPtr)> GetComponentsUnsafe(Entity entity) + public readonly IEnumerable<(TypeHandle, IntPtr)> GetComponentsUnsafe(Entity entity) { foreach (var (typeHandle, pool) in _world.ComponentStorage.ComponentPools) { diff --git a/Ghost.Entities/Ghost.Entities.csproj b/Ghost.Entities/Ghost.Entities.csproj index 32e2d87..6999cd3 100644 --- a/Ghost.Entities/Ghost.Entities.csproj +++ b/Ghost.Entities/Ghost.Entities.csproj @@ -90,4 +90,8 @@ + + + + diff --git a/Ghost.Entities/Query/QueryFilter.cs b/Ghost.Entities/Query/QueryFilter.cs index ca4b3c8..c2aaae7 100644 --- a/Ghost.Entities/Query/QueryFilter.cs +++ b/Ghost.Entities/Query/QueryFilter.cs @@ -1,4 +1,5 @@ -using Misaki.HighPerformance.Unsafe.Collections; +using Ghost.Core; +using Misaki.HighPerformance.Unsafe.Collections; namespace Ghost.Entities.Query; @@ -11,30 +12,29 @@ internal enum FilterMode Disabled = 1 << 3, } -internal readonly struct FilterEntry(nint id, FilterMode mode) +internal readonly struct FilterEntry(TypeHandle id, FilterMode mode) { - public readonly nint typeHandle = id; + public readonly TypeHandle typeHandle = id; public readonly FilterMode mode = mode; } internal struct QueryFilter() { - internal List _all = new(6); - internal List _any = new(6); - internal List _absent = new(6); - internal List _disabled = new(6); + internal List _all = new(6); + internal List _any = new(6); + internal List _absent = new(6); + internal List _disabled = new(6); - public readonly void ComputeFilterBitMask(World world, BitSet result) + public readonly BitSet ComputeFilterBitMask(World world) { - BitSet allMask = new(); - BitSet anyMask = new(); - BitSet absentMask = new(); + BitSet? allMask = null; + BitSet? anyMask = null; + BitSet? absentMask = null; var hasAll = false; var hasAny = false; var hasAbsent = false; - // Compute All mask (intersection) foreach (var typeHandle in _all) { var mask = world.ComponentStorage.GetOrCreateMask(typeHandle); @@ -49,7 +49,6 @@ internal struct QueryFilter() allMask &= mask; } - // Compute Any mask (union) foreach (var typeHandle in _any) { var mask = world.ComponentStorage.GetOrCreateMask(typeHandle); @@ -63,7 +62,6 @@ internal struct QueryFilter() anyMask |= mask; } - // Compute Absent mask (union for exclusion) foreach (var typeHandle in _absent) { var mask = world.ComponentStorage.GetOrCreateMask(typeHandle); @@ -77,21 +75,24 @@ internal struct QueryFilter() absentMask |= mask; } + var result = new BitSet(world.EntityManager.EntityCount); result.SetAll(); if (hasAll) { - result &= allMask; + result &= allMask!; } if (hasAny) { - result &= anyMask; + result &= anyMask!; } if (hasAbsent) { - result &= ~absentMask; + result &= ~absentMask!; } + + return result; } } \ No newline at end of file diff --git a/Ghost.Entities/Template/QueryEnumerable.cs b/Ghost.Entities/Template/QueryEnumerable.cs index 53dabd6..56b947b 100644 --- a/Ghost.Entities/Template/QueryEnumerable.cs +++ b/Ghost.Entities/Template/QueryEnumerable.cs @@ -1,8 +1,8 @@  +using Ghost.Core; using Ghost.Entities.Components; using Ghost.Entities.Query; -using Ghost.Entities.Utilities; using Misaki.HighPerformance.Unsafe.Collections; namespace Ghost.Entities; @@ -59,8 +59,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -232,8 +231,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -410,8 +408,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -593,8 +590,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -781,8 +777,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -974,8 +969,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -1172,8 +1166,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } @@ -1375,8 +1368,7 @@ public struct QueryEnumerable _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } diff --git a/Ghost.Entities/Template/QueryEnumerable.tt b/Ghost.Entities/Template/QueryEnumerable.tt index 31dee4a..6c45780 100644 --- a/Ghost.Entities/Template/QueryEnumerable.tt +++ b/Ghost.Entities/Template/QueryEnumerable.tt @@ -5,9 +5,9 @@ <#@ import namespace="System.Linq" #> <#@ include file="Helpers.ttinclude" #> +using Ghost.Core; using Ghost.Entities.Components; using Ghost.Entities.Query; -using Ghost.Entities.Utilities; using Misaki.HighPerformance.Unsafe.Collections; namespace Ghost.Entities; @@ -85,8 +85,7 @@ public struct QueryEnumerable<<#= generics #>> _count = count; _index = -1; - _filterMask = new BitSet(_world.EntityManager.EntityCount); - filters.ComputeFilterBitMask(_world, _filterMask); + _filterMask = filters.ComputeFilterBitMask(_world); Current = default; } diff --git a/Ghost.Entities/World.cs b/Ghost.Entities/World.cs index e136f00..3c3c264 100644 --- a/Ghost.Entities/World.cs +++ b/Ghost.Entities/World.cs @@ -57,6 +57,8 @@ public partial class World : IDisposable, IEquatable public EntityManager EntityManager => _entityManager; public SystemStorage SystemStorage => _systemStorage; + public event Action? ComponentChanged; + private World(WorldID id, int entityCapacity) { _id = id; @@ -84,6 +86,21 @@ public partial class World : IDisposable, IEquatable return Enumerable.Empty(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void NotifyComponentChanged(Entity entity, Type type) + { + //#if GHOST_EDITOR + ComponentChanged?.Invoke(this, entity, type); + //#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void NotifyComponentChanged(Entity entity) + where T : unmanaged, IComponentData + { + NotifyComponentChanged(entity, typeof(T)); + } + public void Dispose() { _entityManager.Dispose(); diff --git a/Ghost.Graphics/AssemblyInfo.cs b/Ghost.Graphics/AssemblyInfo.cs new file mode 100644 index 0000000..fca6460 --- /dev/null +++ b/Ghost.Graphics/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Ghost.Engine")] +[assembly: InternalsVisibleTo("Ghost.Editor")] +[assembly: InternalsVisibleTo("Ghost.Editor.Core")] +[assembly: InternalsVisibleTo("Ghost.UnitTest")] \ No newline at end of file diff --git a/Ghost.Graphics/Class1.cs b/Ghost.Graphics/Class1.cs deleted file mode 100644 index 6132543..0000000 --- a/Ghost.Graphics/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ghost.Graphics; - -public class Class1 -{ - -} \ No newline at end of file diff --git a/Ghost.Graphics/Contracts/IDebugLayer.cs b/Ghost.Graphics/Contracts/IDebugLayer.cs new file mode 100644 index 0000000..d8ddafb --- /dev/null +++ b/Ghost.Graphics/Contracts/IDebugLayer.cs @@ -0,0 +1,5 @@ +namespace Ghost.Graphics.Contracts; + +internal interface IDebugLayer : IDisposable +{ +} \ No newline at end of file diff --git a/Ghost.Graphics/Contracts/IGraphicsDevice.cs b/Ghost.Graphics/Contracts/IGraphicsDevice.cs new file mode 100644 index 0000000..6956fcf --- /dev/null +++ b/Ghost.Graphics/Contracts/IGraphicsDevice.cs @@ -0,0 +1,11 @@ +using Ghost.Graphics.Data; + +namespace Ghost.Graphics.Contracts; + +internal interface IGraphicsDevice : IDisposable +{ + public static abstract IGraphicsDevice Create(); + + public IRenderView CreateRenderView(in SwapChainSurface swapChainSurface); + public void OnRender(); +} \ No newline at end of file diff --git a/Ghost.Graphics/Contracts/IRenderView.cs b/Ghost.Graphics/Contracts/IRenderView.cs new file mode 100644 index 0000000..44a0843 --- /dev/null +++ b/Ghost.Graphics/Contracts/IRenderView.cs @@ -0,0 +1,11 @@ +namespace Ghost.Graphics.Contracts; + +internal interface IRenderView : IDisposable +{ + public void Resize(uint width, uint height); + public void Render(); + + public void WaitNextFrame(); + public void WaitIdle(); + public void Flush(); +} \ No newline at end of file diff --git a/Ghost.Graphics/DX12/DX12DebugLayer.cs b/Ghost.Graphics/DX12/DX12DebugLayer.cs new file mode 100644 index 0000000..a1280a1 --- /dev/null +++ b/Ghost.Graphics/DX12/DX12DebugLayer.cs @@ -0,0 +1,36 @@ +using Ghost.Graphics.Contracts; +using Vortice.Direct3D12; +using Vortice.Direct3D12.Debug; +using Vortice.DXGI; +using Vortice.DXGI.Debug; + +namespace Ghost.Graphics.DX12; + +internal class DX12DebugLayer : IDebugLayer +{ +#if DEBUG + private readonly ID3D12Debug6 _d3d12Debug; + private readonly IDXGIDebug1 _dxgiDebug; +#endif + + public DX12DebugLayer() + { +#if DEBUG + _d3d12Debug = D3D12.D3D12GetDebugInterface(); + _d3d12Debug.EnableDebugLayer(); + + _dxgiDebug = DXGI.DXGIGetDebugInterface1(); + _dxgiDebug.EnableLeakTrackingForThread(); +#endif + } + + public void Dispose() + { +#if DEBUG + _dxgiDebug.ReportLiveObjects(DXGI.DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal); + + _d3d12Debug?.Dispose(); + _dxgiDebug?.Dispose(); +#endif + } +} \ No newline at end of file diff --git a/Ghost.Graphics/DX12/DX12GraphicsDevice.cs b/Ghost.Graphics/DX12/DX12GraphicsDevice.cs new file mode 100644 index 0000000..c636605 --- /dev/null +++ b/Ghost.Graphics/DX12/DX12GraphicsDevice.cs @@ -0,0 +1,117 @@ +using Ghost.Graphics.Contracts; +using Ghost.Graphics.Data; +using Vortice.Direct3D; +using Vortice.Direct3D12; +using Vortice.DXGI; + +namespace Ghost.Graphics.DX12; + +internal class DX12GraphicsDevice : IGraphicsDevice +{ + private readonly IDXGIFactory7 _dxgiFactory; + private readonly ID3D12Device14 _device; + private readonly ID3D12CommandQueue _commandQueue; + + private readonly List _renderViews = new(); + +#if DEBUG + private readonly IDebugLayer _debugLayer; +#endif + + public ID3D12Device14 Device => _device; + public IDXGIFactory7 DXGIFactory => _dxgiFactory; + public ID3D12CommandQueue CommandQueue => _commandQueue; + + public static IGraphicsDevice Create() => new DX12GraphicsDevice(); + + private DX12GraphicsDevice() + { +#if DEBUG + _debugLayer = new DX12DebugLayer(); +#endif + + InitializeDevice(out _dxgiFactory, out _device); + InitializeCommandQueue(out _commandQueue); + } + + private void InitializeDevice(out IDXGIFactory7 factory, out ID3D12Device14 device) + { +#if DEBUG + factory = DXGI.CreateDXGIFactory2(true); +#else + factory = DXGI.CreateDXGIFactory2(false); +#endif + + ID3D12Device14? d3d12Device = default; + for (uint adapterIndex = 0; + factory.EnumAdapters1(adapterIndex, out var adapter).Success; + adapterIndex++) + { + var desc = adapter.Description1; + + // Don't select the Basic Render Driver adapter. + if ((desc.Flags & AdapterFlags.Software) != AdapterFlags.None) + { + adapter.Dispose(); + continue; + } + + if (D3D12.D3D12CreateDevice(adapter, FeatureLevel.Level_11_0, out d3d12Device).Success) + { + adapter.Dispose(); + break; + } + } + + if (d3d12Device == null) + { + throw new PlatformNotSupportedException("Cannot create ID3D12Device"); + } + + device = d3d12Device; + } + + private void InitializeCommandQueue(out ID3D12CommandQueue queue) + { + var queueDesc = new CommandQueueDescription + { + Type = CommandListType.Direct, + Priority = (int)CommandQueuePriority.High, + Flags = CommandQueueFlags.None, + }; + + queue = _device.CreateCommandQueue(queueDesc); + } + + public IRenderView CreateRenderView(in SwapChainSurface swapChainSurface) + { + var renderView = new DX12RenderView(this, swapChainSurface); + _renderViews.Add(renderView); + return renderView; + } + + public void OnRender() + { + foreach (var renderView in _renderViews) + { + renderView.Render(); + } + } + + public void Dispose() + { + foreach (var renderView in _renderViews) + { + renderView.Dispose(); + } + _renderViews.Clear(); + + _commandQueue?.Dispose(); + _device?.Dispose(); + _dxgiFactory?.Dispose(); + +#if DEBUG + _debugLayer.Dispose(); +#endif + } +} \ No newline at end of file diff --git a/Ghost.Graphics/DX12/DX12RenderView.cs b/Ghost.Graphics/DX12/DX12RenderView.cs new file mode 100644 index 0000000..ae0414c --- /dev/null +++ b/Ghost.Graphics/DX12/DX12RenderView.cs @@ -0,0 +1,219 @@ +using Ghost.Graphics.Contracts; +using Ghost.Graphics.Data; +using Ghost.Graphics.DX12.Utilities; +using System.Runtime.CompilerServices; +using Vortice.Direct3D12; +using Vortice.DXGI; + +namespace Ghost.Graphics.DX12; + +internal class DX12RenderView : IRenderView +{ + private const int _RENDER_TARGET_VIEW_HEAP_SIZE = 1024; + private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256; + + private readonly DX12GraphicsDevice _graphicsDevice; + + private readonly IDXGISwapChain4 _swapChain; + private readonly ID3D12Resource[] _renderTargets; + private readonly uint[] _renderTargetDescriptorIndexes; + private uint _backBufferIndex; + + private readonly ID3D12CommandAllocator[] _commandAllocators; + private readonly ID3D12GraphicsCommandList7 _commandList; + + private readonly ID3D12Fence1 _fence; + private readonly AutoResetEvent _fenceEvent; + private readonly ulong[] _fenceValues; + + private readonly D3D12DescriptorAllocator _rtvHeap; + + public DX12RenderView(DX12GraphicsDevice pipelineContext, in SwapChainSurface swapChainSurface) + { + _graphicsDevice = pipelineContext; + + _rtvHeap = new(_graphicsDevice.Device, DescriptorHeapType.RenderTargetView, _RENDER_TARGET_VIEW_HEAP_SIZE); + + _fenceEvent = new AutoResetEvent(false); + _renderTargets = new ID3D12Resource[GraphicsPipeline.FRAME_COUNT]; + _fenceValues = new ulong[GraphicsPipeline.FRAME_COUNT]; + _renderTargetDescriptorIndexes = new uint[GraphicsPipeline.FRAME_COUNT]; + + InitializeSwapChain(swapChainSurface, out _swapChain); + InitializeCommandObjects(out _commandAllocators, out _commandList, out _fence); + CreateRenderTargets(); + } + + private void InitializeSwapChain(in SwapChainSurface swapChainSurface, out IDXGISwapChain4 swapChain) + { + var swapChainDesc = new SwapChainDescription1 + { + Width = swapChainSurface.Width, + Height = swapChainSurface.Height, + Format = Format.B8G8R8A8_UNorm, + Stereo = false, + SampleDescription = new SampleDescription(1, 0), + BufferUsage = Usage.RenderTargetOutput, + BufferCount = GraphicsPipeline.FRAME_COUNT, + Scaling = Scaling.Stretch, + SwapEffect = SwapEffect.FlipDiscard, + AlphaMode = AlphaMode.Ignore, + Flags = SwapChainFlags.AllowTearing + }; + + // NOTE: Not going to need it for now, this is for standalone applications. + var swapChainFullscreenDesc = new SwapChainFullscreenDescription + { + Windowed = true, + }; + + switch (swapChainSurface.Type) + { + case SwapChainSurface.TargetType.Composition: + var swapChain1 = _graphicsDevice.DXGIFactory.CreateSwapChainForComposition(_graphicsDevice.CommandQueue, swapChainDesc); + swapChain = swapChain1.QueryInterface(); + + _backBufferIndex = swapChain.CurrentBackBufferIndex; + swapChainSurface.SwapChainPanelNative!.SetSwapChain(swapChain); + break; + case SwapChainSurface.TargetType.Hwnd: + var swapChain2 = _graphicsDevice.DXGIFactory.CreateSwapChainForHwnd( + _graphicsDevice.CommandQueue, + swapChainSurface.Hwnd, + swapChainDesc, + swapChainFullscreenDesc, + null); + swapChain = swapChain2.QueryInterface(); + break; + default: + throw new ArgumentException("Unsupported swap chain surface type."); + } + } + + private void InitializeCommandObjects(out ID3D12CommandAllocator[] commandAllocator, out ID3D12GraphicsCommandList7 commandList, out ID3D12Fence1 fence) + { + commandAllocator = new ID3D12CommandAllocator[GraphicsPipeline.FRAME_COUNT]; + for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++) + { + commandAllocator[i] = _graphicsDevice.Device.CreateCommandAllocator(CommandListType.Direct); + } + + commandList = _graphicsDevice.Device.CreateCommandList(CommandListType.Direct, commandAllocator[0], null!); + fence = _graphicsDevice.Device.CreateFence(_fenceValues[_backBufferIndex], FenceFlags.None); + + _commandList.Close(); + _fenceValues[_backBufferIndex]++; + } + + private void CreateRenderTargets() + { + for (var i = 0u; i < GraphicsPipeline.FRAME_COUNT; i++) + { + _renderTargets[i] = _swapChain.GetBuffer(i); + _renderTargets[i].Name = $"RenderTarget_{i}"; + _renderTargetDescriptorIndexes[i] = _rtvHeap.AllocateDescriptor(); + + var rtvHandle = _rtvHeap.GetCpuHandle(_renderTargetDescriptorIndexes[i]); + _graphicsDevice.Device.CreateRenderTargetView(_renderTargets[i], null, rtvHandle); + } + } + + public void Resize(uint width, uint height) + { + WaitIdle(); + + for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++) + { + _renderTargets[i].Dispose(); + _rtvHeap.ReleaseDescriptor(_renderTargetDescriptorIndexes[i]); + + _fenceValues[i] = _fenceValues[_backBufferIndex]; + } + + _swapChain.ResizeBuffers(GraphicsPipeline.FRAME_COUNT, width, height, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError(); + + CreateRenderTargets(); + _backBufferIndex = _swapChain.CurrentBackBufferIndex; + } + + public void Render() + { + var commandAllocator = _commandAllocators[_backBufferIndex]; + commandAllocator.Reset(); + _commandList.Reset(commandAllocator, null); + + _commandList.Close(); + _graphicsDevice.CommandQueue.ExecuteCommandLists([_commandList]); + + _swapChain.Present(1, PresentFlags.None).CheckError(); + + WaitNextFrame(); + } + + public void WaitNextFrame() + { + var fenceValue = _fenceValues[_backBufferIndex]; + + if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Failure) + { + return; + } + + _backBufferIndex = _swapChain.CurrentBackBufferIndex; + if (_fence.CompletedValue < _fenceValues[_backBufferIndex] + && _fence.SetEventOnCompletion(_fenceValues[_backBufferIndex], _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success) + { + _fenceEvent.WaitOne(); + } + + _fenceValues[_backBufferIndex]++; + } + + public void WaitIdle() + { + var fenceValue = _fenceValues[_backBufferIndex]; + if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Failure + || _fence.SetEventOnCompletion(fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Failure) + { + return; + } + + _fenceEvent.WaitOne(); + _fenceValues[_backBufferIndex]++; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Flush() + { + for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++) + { + WaitIdle(); + } + } + + public void Dispose() + { + Flush(); + + foreach (var commandAllocator in _commandAllocators) + { + commandAllocator.Dispose(); + } + + foreach (var renderTarget in _renderTargets) + { + renderTarget.Dispose(); + } + + _swapChain.Dispose(); + _commandList.Dispose(); + + _fence.Dispose(); + _fenceEvent.Dispose(); + + _rtvHeap.Dispose(); + + _backBufferIndex = 0; + _fenceValues.AsSpan().Clear(); + } +} \ No newline at end of file diff --git a/Ghost.Graphics/DX12/Utilities/D3D12DescriptorAllocator.cs b/Ghost.Graphics/DX12/Utilities/D3D12DescriptorAllocator.cs new file mode 100644 index 0000000..cb25fd7 --- /dev/null +++ b/Ghost.Graphics/DX12/Utilities/D3D12DescriptorAllocator.cs @@ -0,0 +1,238 @@ +using System.Diagnostics; +using System.Numerics; +using Vortice.Direct3D12; +using DescriptorIndex = System.UInt32; + +namespace Ghost.Graphics.DX12.Utilities; + +internal class D3D12DescriptorAllocator : IDisposable +{ + private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u; + + private readonly ID3D12Device _device; + private readonly Lock _mutex = new(); + + private ID3D12DescriptorHeap? _heap; + private ID3D12DescriptorHeap? _shaderVisibleHeap; + private CpuDescriptorHandle _startCpuHandle = default; + private CpuDescriptorHandle _startCpuHandleShaderVisible = default; + private GpuDescriptorHandle _startGpuHandleShaderVisible = default; + private DescriptorIndex _searchStart; + private bool[] _allocatedDescriptors = []; + + public DescriptorHeapType HeapType + { + get; + } + + public uint NumDescriptors + { + get; private set; + } + + public uint NumAllocatedDescriptors + { + get; private set; + } + + public bool ShaderVisible + { + get; + } + + public uint Stride + { + get; + } + + public ID3D12DescriptorHeap Heap => _heap!; + public ID3D12DescriptorHeap? ShaderVisibleHeap => _shaderVisibleHeap; + + public D3D12DescriptorAllocator(ID3D12Device device, DescriptorHeapType type, uint numDescriptors) + { + _device = device; + HeapType = type; + NumDescriptors = numDescriptors; + ShaderVisible = type == DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView || type == DescriptorHeapType.Sampler; + Stride = device.GetDescriptorHandleIncrementSize(type); + + Debug.Assert(AllocateResources(numDescriptors)); + } + + public DescriptorIndex AllocateDescriptor() => AllocateDescriptors(1); + + public DescriptorIndex AllocateDescriptors(uint count) + { + lock (_mutex) + { + DescriptorIndex foundIndex = 0; + uint freeCount = 0; + var found = false; + + // Find a contiguous range of 'count' indices for which _allocatedDescriptors[index] is false + for (var index = _searchStart; index < NumDescriptors; index++) + { + if (_allocatedDescriptors[index]) + { + freeCount = 0; + } + else + { + freeCount += 1; + } + + if (freeCount >= count) + { + foundIndex = index > 0 ? index - count + 1 : 0; + found = true; + break; + } + } + + if (!found) + { + foundIndex = NumDescriptors; + + if (!Grow(NumDescriptors + count)) + { + Debug.WriteLine("ERROR: Failed to grow a descriptor heap!"); + return _INVALID_DESCRIPTOR_INDEX; + } + } + + for (var index = foundIndex; index < foundIndex + count; index++) + { + _allocatedDescriptors[index] = true; + } + + NumAllocatedDescriptors += count; + _searchStart = foundIndex + count; + return foundIndex; + } + } + + public void ReleaseDescriptor(DescriptorIndex index) => ReleaseDescriptors(index, 1); + + public void ReleaseDescriptors(DescriptorIndex baseIndex, uint count = 1) + { + if (count == 0) + { + return; + } + + lock (_mutex) + { + for (var index = baseIndex; index < baseIndex + count; index++) + { +#if DEBUG + if (!_allocatedDescriptors[index]) + { + Debug.WriteLine("Error: Attempted to release an un-allocated descriptor"); + } +#endif + + _allocatedDescriptors[index] = false; + } + + NumAllocatedDescriptors -= count; + + if (_searchStart > baseIndex) + { + _searchStart = baseIndex; + } + } + } + + public CpuDescriptorHandle GetCpuHandle(DescriptorIndex index) + { + var handle = _startCpuHandle; + return handle.Offset((int)index, Stride); + } + + public CpuDescriptorHandle GetCpuHandleShaderVisible(DescriptorIndex index) + { + var handle = _startCpuHandleShaderVisible; + return handle.Offset((int)index, Stride); + } + + public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) + { + var handle = _startGpuHandleShaderVisible; + return handle.Offset((int)index, Stride); + } + + public void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1) + { + _device.CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType); + } + + private bool AllocateResources(uint numDescriptors) + { + NumDescriptors = numDescriptors; + _heap?.Dispose(); + _shaderVisibleHeap?.Dispose(); + + DescriptorHeapDescription heapDesc = new() + { + Type = HeapType, + DescriptorCount = numDescriptors, + Flags = DescriptorHeapFlags.None, + NodeMask = 0 + }; + + var hr = _device.CreateDescriptorHeap(in heapDesc, out _heap); + if (hr.Failure) + { + return false; + } + + _startCpuHandle = _heap!.GetCPUDescriptorHandleForHeapStart(); + Array.Resize(ref _allocatedDescriptors, (int)numDescriptors); + + if (ShaderVisible) + { + heapDesc.Flags = DescriptorHeapFlags.ShaderVisible; + + hr = _device.CreateDescriptorHeap(in heapDesc, out _shaderVisibleHeap); + + if (hr.Failure) + { + return false; + } + + _startCpuHandleShaderVisible = _shaderVisibleHeap!.GetCPUDescriptorHandleForHeapStart(); + _startGpuHandleShaderVisible = _shaderVisibleHeap!.GetGPUDescriptorHandleForHeapStart(); + } + + return true; + } + + private bool Grow(uint minRequiredSize) + { + var oldSize = NumDescriptors; + var newSize = BitOperations.RoundUpToPowerOf2(minRequiredSize); + + var oldHeap = _heap; + + if (!AllocateResources(newSize)) + { + return false; + } + + _device.CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap!.GetCPUDescriptorHandleForHeapStart(), HeapType); + + if (_shaderVisibleHeap is not null) + { + _device.CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.GetCPUDescriptorHandleForHeapStart(), HeapType); + } + + return true; + } + + /// + public void Dispose() + { + _heap?.Dispose(); + _shaderVisibleHeap?.Dispose(); + } +} \ No newline at end of file diff --git a/Ghost.Graphics/Data/Camera.cs b/Ghost.Graphics/Data/Camera.cs new file mode 100644 index 0000000..d4ce3ee --- /dev/null +++ b/Ghost.Graphics/Data/Camera.cs @@ -0,0 +1,5 @@ +namespace Ghost.Graphics.Data; + +public class Camera +{ +} \ No newline at end of file diff --git a/Ghost.Graphics/Data/Color32.cs b/Ghost.Graphics/Data/Color32.cs new file mode 100644 index 0000000..f948ba6 --- /dev/null +++ b/Ghost.Graphics/Data/Color32.cs @@ -0,0 +1,27 @@ +using System.Drawing; + +namespace Ghost.Graphics.Data; + +/// +/// Represents a color with 32-bit components, the unmanaged version of ."/> +/// +public struct Color32 +{ + public byte r; + public byte g; + public byte b; + public byte a; + + public Color32(byte r, byte g, byte b, byte a) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public Color32(Color color) + : this(color.R, color.G, color.B, color.A) + { + } +} \ No newline at end of file diff --git a/Ghost.Graphics/Data/GraphicsAPI.cs b/Ghost.Graphics/Data/GraphicsAPI.cs new file mode 100644 index 0000000..a1c711e --- /dev/null +++ b/Ghost.Graphics/Data/GraphicsAPI.cs @@ -0,0 +1,6 @@ +namespace Ghost.Graphics.Data; + +public enum GraphicsAPI +{ + DX12 +} \ No newline at end of file diff --git a/Ghost.Graphics/Data/Mesh.cs b/Ghost.Graphics/Data/Mesh.cs new file mode 100644 index 0000000..275749f --- /dev/null +++ b/Ghost.Graphics/Data/Mesh.cs @@ -0,0 +1,234 @@ +using Misaki.HighPerformance.Unsafe.Collections; +using Misaki.HighPerformance.Unsafe.Helpers; +using System.Numerics; + +namespace Ghost.Graphics.Data; + +public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable +{ + private UnsafeList _vertices = new(initialVertexCapacity, Allocator.Persistent); + private UnsafeList _normals = new(initialVertexCapacity, Allocator.Persistent); + private UnsafeList _tangents = new(initialVertexCapacity, Allocator.Persistent); + private UnsafeList _colors = new(initialVertexCapacity, Allocator.Persistent); + private UnsafeList _uvs = new(initialVertexCapacity, Allocator.Persistent); + private UnsafeList _indices = new(initialIndexCapacity, Allocator.Persistent); + + public Span Vertices => _vertices.AsSpan(); + public Span Normals => _normals.AsSpan(); + public Span Tangents => _tangents.AsSpan(); + public Span Colors => _colors.AsSpan(); + public Span UVs => _uvs.AsSpan(); + public Span Indices => _indices.AsSpan(); + + public int VertexCount => _vertices.Count; + + ~Mesh() + { + Dispose(); + } + + /// + /// Adds a vertex to the mesh with the specified attributes. + /// + /// This method adds the vertex attributes to their respective collections, allowing the mesh + /// to be constructed with detailed vertex data. Ensure that all parameters are provided with valid values to + /// avoid incomplete or incorrect mesh data. + /// The position of the vertex in 3D space. + /// The normal vector at the vertex. + /// The tangent vector at the vertex. + /// The color of the vertex. + /// The UV coordinates of the vertex. + public void AddVertex(Vector3 position, Vector3 normal, Vector4 tangent, Color32 color, Vector2 uv) + { + _vertices.Add(position); + _normals.Add(normal); + _tangents.Add(tangent); + _colors.Add(color); + _uvs.Add(uv); + } + + /// + /// Adds a triangle to the mesh by specifying the indices of its three vertices. + /// + /// The index of the first vertex in the triangle. Must be within the range of the current vertex count. + /// The index of the second vertex in the triangle. Must be within the range of the current vertex count. + /// The index of the third vertex in the triangle. Must be within the range of the current vertex count. + /// Thrown if any of the specified indices are out of range for the current vertex count. + public void AddTriangle(int index0, int index1, int index2) + { + if (index0 >= _vertices.Count || index1 >= _vertices.Count || index2 >= _vertices.Count) + { + throw new ArgumentOutOfRangeException("Index out of range for the current vertex count."); + } + + _indices.Add(index0); + _indices.Add(index1); + _indices.Add(index2); + } + + public void TrimExcess() + { + _vertices.Resize(_vertices.Count); + _normals.Resize(_normals.Count); + _tangents.Resize(_tangents.Count); + _colors.Resize(_colors.Count); + _uvs.Resize(_uvs.Count); + _indices.Resize(_indices.Count); + } + + /// + /// Auto-compute smooth per-vertex normals. + /// + /// + /// Call this method before vertices and indices are valid. + /// + public void ComputeNormal() + { + if (!_vertices.IsCreated || _vertices.Count < 3 + || !_indices.IsCreated || _indices.Count < 3) + { + return; + } + + if (!_normals.IsCreated) + { + _normals = new(_vertices.Count, Allocator.Persistent); + } + else + { + _normals.Clear(); + _normals.Resize(_vertices.Count); + } + + for (var i = 0; i < _indices.Count; i += 3) + { + var i0 = _indices[i]; + var i1 = _indices[i + 1]; + var i2 = _indices[i + 2]; + + var v0 = _vertices[i0]; + var v1 = _vertices[i1]; + var v2 = _vertices[i2]; + + var edge1 = v1 - v0; + var edge2 = v2 - v0; + var faceNormal = Vector3.Cross(edge1, edge2); + + _normals[i0] += faceNormal; + _normals[i1] += faceNormal; + _normals[i2] += faceNormal; + } + + for (var i = 0; i < _normals.Count; i++) + { + _normals[i] = Vector3.Normalize(_normals[i]); + } + } + + /// + /// Auto-compute per-vertex tangents. + /// + /// + /// Call this method before vertices, normals, and UVs are valid. + /// + public void ComputeTangents() + { + if (!_vertices.IsCreated || _vertices.Count < 3 + || !_indices.IsCreated || _indices.Count < 3 + || !_uvs.IsCreated || _uvs.Count < _vertices.Count) + { + return; + } + + if (!_normals.IsCreated || _normals.Count != _vertices.Count) + { + ComputeNormal(); + } + + if (!_tangents.IsCreated) + { + _tangents = new(_vertices.Count, Allocator.Persistent); + } + else + { + _tangents.Clear(); + _tangents.Resize(_vertices.Count); + } + + var bitangents = new Vector3[_vertices.Count]; + + for (var i = 0; i < _indices.Count; i += 3) + { + var i0 = _indices[i]; + var i1 = _indices[i + 1]; + var i2 = _indices[i + 2]; + + var v0 = _vertices[i0]; + var v1 = _vertices[i1]; + var v2 = _vertices[i2]; + var uv0 = _uvs[i0]; + var uv1 = _uvs[i1]; + var uv2 = _uvs[i2]; + + var deltaPos1 = v1 - v0; + var deltaPos2 = v2 - v0; + var deltaUV1 = uv1 - uv0; + var deltaUV2 = uv2 - uv0; + + var r = 1.0f / (deltaUV1.X * deltaUV2.Y - deltaUV1.Y * deltaUV2.X); + var tangent = (deltaPos1 * deltaUV2.Y - deltaPos2 * deltaUV1.Y) * r; + var bitangent = (deltaPos2 * deltaUV1.X - deltaPos1 * deltaUV2.X) * r; + + for (var j = 0; j < 3; j++) + { + var idx = _indices[i + j]; + var t = _tangents[idx]; + _tangents[idx] = new Vector4( + t.X + tangent.X, + t.Y + tangent.Y, + t.Z + tangent.Z, + 0.0f // we’ll fill w later + ); + + bitangents[idx] += bitangent; + } + } + + for (var i = 0; i < _vertices.Count; i++) + { + var n = _normals![i]; + var t = _tangents[i]; + var t3 = new Vector3(t.X, t.Y, t.Z); + + var proj = n * Vector3.Dot(n, t3); + t3 = Vector3.Normalize(t3 - proj); + + var b = bitangents[i]; + var w = Vector3.Dot(Vector3.Cross(n, t3), b) < 0.0f ? -1.0f : 1.0f; + + _tangents[i] = new Vector4(t3.X, t3.Y, t3.Z, w); + } + } + + public void Clear() + { + _vertices.Clear(); + _normals.Clear(); + _tangents.Clear(); + _colors.Clear(); + _uvs.Clear(); + _indices.Clear(); + } + + public void Dispose() + { + _vertices.Dispose(); + _normals.Dispose(); + _tangents.Dispose(); + _colors.Dispose(); + _uvs.Dispose(); + _indices.Dispose(); + + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/Ghost.Graphics/Data/SwapChainSurface.cs b/Ghost.Graphics/Data/SwapChainSurface.cs new file mode 100644 index 0000000..205020f --- /dev/null +++ b/Ghost.Graphics/Data/SwapChainSurface.cs @@ -0,0 +1,53 @@ +namespace Ghost.Graphics.Data; + +internal readonly struct SwapChainSurface +{ + public enum TargetType + { + Composition, + Hwnd + } + + public readonly TargetType Type + { + get; + } + + public readonly Vortice.WinUI.ISwapChainPanelNative? SwapChainPanelNative + { + get; + } + + public readonly IntPtr Hwnd + { + get; + } + + public readonly uint Width + { + get; + } + + public readonly uint Height + { + get; + } + + public SwapChainSurface(Vortice.WinUI.ISwapChainPanelNative swapChainPanelNative, uint width, uint height) + { + Type = TargetType.Composition; + SwapChainPanelNative = swapChainPanelNative; + Hwnd = IntPtr.Zero; + Width = width; + Height = height; + } + + public SwapChainSurface(IntPtr hwnd, uint width, uint height) + { + Type = TargetType.Hwnd; + SwapChainPanelNative = null; + Hwnd = hwnd; + Width = width; + Height = height; + } +} \ No newline at end of file diff --git a/Ghost.Graphics/Ghost.Graphics.csproj b/Ghost.Graphics/Ghost.Graphics.csproj index 6a87d7c..1a17af8 100644 --- a/Ghost.Graphics/Ghost.Graphics.csproj +++ b/Ghost.Graphics/Ghost.Graphics.csproj @@ -5,6 +5,7 @@ enable enable True + preview @@ -17,6 +18,17 @@ + + + + + + + + + + ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll + diff --git a/Ghost.Graphics/GraphicsPipeline.cs b/Ghost.Graphics/GraphicsPipeline.cs new file mode 100644 index 0000000..f7218c2 --- /dev/null +++ b/Ghost.Graphics/GraphicsPipeline.cs @@ -0,0 +1,77 @@ +using Ghost.Graphics.Contracts; +using Ghost.Graphics.Data; + +namespace Ghost.Graphics; + +internal static class GraphicsPipeline +{ + public const int FRAME_COUNT = 2; + + private static IGraphicsDevice? _graphicsDevice; + private static Thread? _renderThread; + + private static bool _isRunning; + + public static IGraphicsDevice GraphicsDevice + { + get + { + if (_graphicsDevice == null) + { + throw new InvalidOperationException("Graphics pipeline is not initialized."); + } + return _graphicsDevice; + } + } + + public static void Initialize(GraphicsAPI api) + { + _graphicsDevice = api switch + { + GraphicsAPI.DX12 => DX12.DX12GraphicsDevice.Create(), + _ => throw new NotSupportedException($"Graphics API {api} is not supported.") + }; + + _renderThread = new Thread(RenderLoop); + } + + private static void RenderLoop() + { + while (_isRunning) + { + GraphicsDevice.OnRender(); + } + } + + public static void Start() + { + if (_isRunning) + { + return; + } + + if (_graphicsDevice == null || _renderThread == null) + { + throw new InvalidOperationException("Graphics pipeline is not initialized."); + } + + _isRunning = true; + _renderThread.Start(); + } + + public static void Stop() + { + _isRunning = false; + _renderThread?.Join(); + } + + public static void Shutdown() + { + Stop(); + + _graphicsDevice?.Dispose(); + + _graphicsDevice = null; + _renderThread = null; + } +} \ No newline at end of file diff --git a/Ghost.Graphics/Utilities/MeshBuilder.cs b/Ghost.Graphics/Utilities/MeshBuilder.cs new file mode 100644 index 0000000..6c27a0d --- /dev/null +++ b/Ghost.Graphics/Utilities/MeshBuilder.cs @@ -0,0 +1,126 @@ +using Ghost.Graphics.Data; +using System.Numerics; + +namespace Ghost.Graphics.Utilities; + +public static class MeshBuilder +{ + /// + /// Creates a unit cube centered at the origin with size 1. + /// + public static Mesh CreateCube(float size = 1.0f, Color32 color = default) + { + var half = size * 0.5f; + var mesh = new Mesh(24, 36); + + var corners = new Vector3[] + { + new(-half, -half, -half), new( half, -half, -half), new( half, half, -half), new(-half, half, -half), + new(-half, -half, half), new( half, -half, half), new( half, half, half), new(-half, half, half) + }; + + int[][] faces = + [ + [0,1,2,3], + [5,4,7,6], + [4,0,3,7], + [1,5,6,2], + [3,2,6,7], + [4,5,1,0] + ]; + + var uvs = new Vector2[] { new(0, 0), new(1, 0), new(1, 1), new(0, 1) }; + + foreach (var face in faces) + { + var baseIndex = mesh.VertexCount; + for (var i = 0; i < 4; i++) + { + mesh.AddVertex(corners[face[i]], Vector3.Zero, Vector4.Zero, color, uvs[i]); + } + + mesh.AddTriangle(baseIndex + 0, baseIndex + 1, baseIndex + 2); + mesh.AddTriangle(baseIndex + 0, baseIndex + 2, baseIndex + 3); + } + + mesh.ComputeNormal(); + mesh.ComputeTangents(); + return mesh; + } + + /// + /// Creates a plane on the XZ axis centered at the origin. + /// + public static Mesh CreatePlane(float width = 1.0f, float depth = 1.0f, Color32 color = default) + { + var hw = width * 0.5f; + var hd = depth * 0.5f; + var mesh = new Mesh(4, 6); + + mesh.AddVertex(new(-hw, 0, -hd), Vector3.Zero, Vector4.Zero, color, new(0, 0)); + mesh.AddVertex(new(hw, 0, -hd), Vector3.Zero, Vector4.Zero, color, new(1, 0)); + mesh.AddVertex(new(hw, 0, hd), Vector3.Zero, Vector4.Zero, color, new(1, 1)); + mesh.AddVertex(new(-hw, 0, hd), Vector3.Zero, Vector4.Zero, color, new(0, 1)); + + mesh.AddTriangle(0, 1, 2); + mesh.AddTriangle(0, 2, 3); + + mesh.ComputeNormal(); + mesh.ComputeTangents(); + return mesh; + } + + /// + /// Creates a UV sphere centered at the origin. + /// + public static Mesh CreateSphere(int latitudeSegments = 16, int longitudeSegments = 24, float radius = 0.5f, Color32 color = default) + { + var mesh = new Mesh((latitudeSegments + 1) * (longitudeSegments + 1), latitudeSegments * longitudeSegments * 6); + + // Vertices + for (var lat = 0; lat <= latitudeSegments; lat++) + { + var theta = (float)lat / latitudeSegments * MathF.PI; + var sinTheta = MathF.Sin(theta); + var cosTheta = MathF.Cos(theta); + + for (var lon = 0; lon <= longitudeSegments; lon++) + { + var phi = (float)lon / longitudeSegments * 2 * MathF.PI; + var sinPhi = MathF.Sin(phi); + var cosPhi = MathF.Cos(phi); + + var x = cosPhi * sinTheta; + var y = cosTheta; + var z = sinPhi * sinTheta; + + mesh.AddVertex( + position: new Vector3(x, y, z) * radius, + normal: Vector3.Zero, + tangent: Vector4.Zero, + color: color, + uv: new Vector2((float)lon / longitudeSegments, (float)lat / latitudeSegments) + ); + } + } + + // Indices + for (var lat = 0; lat < latitudeSegments; lat++) + { + for (var lon = 0; lon < longitudeSegments; lon++) + { + var i0 = lat * (longitudeSegments + 1) + lon; + var i1 = i0 + longitudeSegments + 1; + var i2 = i1 + 1; + var i3 = i0 + 1; + + mesh.AddTriangle(i0, i1, i2); + mesh.AddTriangle(i0, i2, i3); + } + } + + mesh.ComputeNormal(); + mesh.ComputeTangents(); + return mesh; + } +} \ No newline at end of file diff --git a/Ghost.UnitTest/Ghost.UnitTest.csproj b/Ghost.UnitTest/Ghost.UnitTest.csproj index 7922365..4992dfe 100644 --- a/Ghost.UnitTest/Ghost.UnitTest.csproj +++ b/Ghost.UnitTest/Ghost.UnitTest.csproj @@ -44,12 +44,12 @@ - - - + + + - + diff --git a/GhostEngine.sln b/GhostEngine.sln index 43bc32d..9b9ee17 100644 --- a/GhostEngine.sln +++ b/GhostEngine.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.14.35906.104 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor", "Ghost.App\Ghost.Editor.csproj", "{15AFE3A1-0CAF-4B36-8835-121C4D683BBF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor", "Ghost.Editor\Ghost.Editor.csproj", "{15AFE3A1-0CAF-4B36-8835-121C4D683BBF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Engine", "Ghost.Engine\Ghost.Engine.csproj", "{1ED62E09-8F36-4671-896B-16C1C1530202}" EndProject @@ -17,6 +17,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Generator", "Ghost.Ge EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.UnitTest", "Ghost.UnitTest\Ghost.UnitTest.csproj", "{4179873E-8174-4D17-9584-8C223BA71366}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Core", "Ghost.Core\Ghost.Core.csproj", "{337D9110-76FC-453C-9481-757052A216E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor.Core", "Ghost.Editor.Core\Ghost.Editor.Core.csproj", "{222A4E83-D902-423A-8E99-8321BBFC604C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -123,6 +127,30 @@ Global {4179873E-8174-4D17-9584-8C223BA71366}.Release|x86.ActiveCfg = Release|x86 {4179873E-8174-4D17-9584-8C223BA71366}.Release|x86.Build.0 = Release|x86 {4179873E-8174-4D17-9584-8C223BA71366}.Release|x86.Deploy.0 = Release|x86 + {337D9110-76FC-453C-9481-757052A216E5}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Debug|ARM64.Build.0 = Debug|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Debug|x64.ActiveCfg = Debug|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Debug|x64.Build.0 = Debug|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Debug|x86.ActiveCfg = Debug|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Debug|x86.Build.0 = Debug|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Release|ARM64.ActiveCfg = Release|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Release|ARM64.Build.0 = Release|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Release|x64.ActiveCfg = Release|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Release|x64.Build.0 = Release|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Release|x86.ActiveCfg = Release|Any CPU + {337D9110-76FC-453C-9481-757052A216E5}.Release|x86.Build.0 = Release|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Debug|ARM64.Build.0 = Debug|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Debug|x64.ActiveCfg = Debug|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Debug|x64.Build.0 = Debug|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Debug|x86.ActiveCfg = Debug|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Debug|x86.Build.0 = Debug|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Release|ARM64.ActiveCfg = Release|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Release|ARM64.Build.0 = Release|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x64.ActiveCfg = Release|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x64.Build.0 = Release|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x86.ActiveCfg = Release|Any CPU + {222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE