From 68399e4e74bf9bd15276cdb38b09575365c51ae7 Mon Sep 17 00:00:00 2001 From: Antti Veikkolainen Date: Thu, 13 Mar 2025 20:20:05 +0200 Subject: [PATCH 1/4] #28 Launcher changes --- .../{ => MinecraftClone}/Block/BlockBase.cs | 0 Examples/{ => MinecraftClone}/Block/Dirt.cs | 0 Examples/{ => MinecraftClone}/Block/Stone.cs | 0 Examples/{ => MinecraftClone}/BlockFactory.cs | 0 Examples/{ => MinecraftClone}/Game.cs | 0 Examples/{ => MinecraftClone}/Input.cs | 0 Examples/{ => MinecraftClone}/Inventory.cs | 0 .../{ => MinecraftClone}/Minecraft.csproj | 0 Examples/{ => MinecraftClone}/Minecraft.sln | 0 Examples/{ => MinecraftClone}/Program.cs | 0 .../Resources/container2.png | Bin .../Resources/container2_specular.png | Bin .../{ => MinecraftClone}/Resources/grass.jpg | Bin Examples/{ => MinecraftClone}/Settings.cs | 0 Examples/Minimal/Minimal.sln | 30 +++++++ Examples/Minimal/Minimal/Minimal.csproj | 14 ++++ Examples/Minimal/Minimal/Program.cs | 52 ++++++++++++ Launcher/Launcher.csproj | 10 ++- Launcher/MauiProgram.cs | 3 +- Launcher/Services/ApplicationManager.cs | 23 ------ ...tializationService.cs => EditorService.cs} | 72 ++++++++++++---- Launcher/Services/NotificationService.cs | 46 ++++++++++- .../Dialogs/ConfirmDeleteDialog.razor | 2 +- .../Dialogs/CreateProjectDialog.razor | 2 +- Launcher/UI/Components/Dialogs/DialogBase.cs | 12 +-- .../UI/Components/Dialogs/Notification.razor | 17 ++-- Launcher/UI/Home.razor | 33 ++------ Launcher/UI/Home.razor.cs | 77 ++++++------------ Launcher/UI/_Imports.razor | 3 +- 29 files changed, 246 insertions(+), 150 deletions(-) rename Examples/{ => MinecraftClone}/Block/BlockBase.cs (100%) rename Examples/{ => MinecraftClone}/Block/Dirt.cs (100%) rename Examples/{ => MinecraftClone}/Block/Stone.cs (100%) rename Examples/{ => MinecraftClone}/BlockFactory.cs (100%) rename Examples/{ => MinecraftClone}/Game.cs (100%) rename Examples/{ => MinecraftClone}/Input.cs (100%) rename Examples/{ => MinecraftClone}/Inventory.cs (100%) rename Examples/{ => MinecraftClone}/Minecraft.csproj (100%) rename Examples/{ => MinecraftClone}/Minecraft.sln (100%) rename Examples/{ => MinecraftClone}/Program.cs (100%) rename Examples/{ => MinecraftClone}/Resources/container2.png (100%) rename Examples/{ => MinecraftClone}/Resources/container2_specular.png (100%) rename Examples/{ => MinecraftClone}/Resources/grass.jpg (100%) rename Examples/{ => MinecraftClone}/Settings.cs (100%) create mode 100644 Examples/Minimal/Minimal.sln create mode 100644 Examples/Minimal/Minimal/Minimal.csproj create mode 100644 Examples/Minimal/Minimal/Program.cs delete mode 100644 Launcher/Services/ApplicationManager.cs rename Launcher/Services/{ProjectInitializationService.cs => EditorService.cs} (63%) diff --git a/Examples/Block/BlockBase.cs b/Examples/MinecraftClone/Block/BlockBase.cs similarity index 100% rename from Examples/Block/BlockBase.cs rename to Examples/MinecraftClone/Block/BlockBase.cs diff --git a/Examples/Block/Dirt.cs b/Examples/MinecraftClone/Block/Dirt.cs similarity index 100% rename from Examples/Block/Dirt.cs rename to Examples/MinecraftClone/Block/Dirt.cs diff --git a/Examples/Block/Stone.cs b/Examples/MinecraftClone/Block/Stone.cs similarity index 100% rename from Examples/Block/Stone.cs rename to Examples/MinecraftClone/Block/Stone.cs diff --git a/Examples/BlockFactory.cs b/Examples/MinecraftClone/BlockFactory.cs similarity index 100% rename from Examples/BlockFactory.cs rename to Examples/MinecraftClone/BlockFactory.cs diff --git a/Examples/Game.cs b/Examples/MinecraftClone/Game.cs similarity index 100% rename from Examples/Game.cs rename to Examples/MinecraftClone/Game.cs diff --git a/Examples/Input.cs b/Examples/MinecraftClone/Input.cs similarity index 100% rename from Examples/Input.cs rename to Examples/MinecraftClone/Input.cs diff --git a/Examples/Inventory.cs b/Examples/MinecraftClone/Inventory.cs similarity index 100% rename from Examples/Inventory.cs rename to Examples/MinecraftClone/Inventory.cs diff --git a/Examples/Minecraft.csproj b/Examples/MinecraftClone/Minecraft.csproj similarity index 100% rename from Examples/Minecraft.csproj rename to Examples/MinecraftClone/Minecraft.csproj diff --git a/Examples/Minecraft.sln b/Examples/MinecraftClone/Minecraft.sln similarity index 100% rename from Examples/Minecraft.sln rename to Examples/MinecraftClone/Minecraft.sln diff --git a/Examples/Program.cs b/Examples/MinecraftClone/Program.cs similarity index 100% rename from Examples/Program.cs rename to Examples/MinecraftClone/Program.cs diff --git a/Examples/Resources/container2.png b/Examples/MinecraftClone/Resources/container2.png similarity index 100% rename from Examples/Resources/container2.png rename to Examples/MinecraftClone/Resources/container2.png diff --git a/Examples/Resources/container2_specular.png b/Examples/MinecraftClone/Resources/container2_specular.png similarity index 100% rename from Examples/Resources/container2_specular.png rename to Examples/MinecraftClone/Resources/container2_specular.png diff --git a/Examples/Resources/grass.jpg b/Examples/MinecraftClone/Resources/grass.jpg similarity index 100% rename from Examples/Resources/grass.jpg rename to Examples/MinecraftClone/Resources/grass.jpg diff --git a/Examples/Settings.cs b/Examples/MinecraftClone/Settings.cs similarity index 100% rename from Examples/Settings.cs rename to Examples/MinecraftClone/Settings.cs diff --git a/Examples/Minimal/Minimal.sln b/Examples/Minimal/Minimal.sln new file mode 100644 index 0000000..9653d95 --- /dev/null +++ b/Examples/Minimal/Minimal.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35821.62 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minimal", "Minimal\Minimal.csproj", "{D5FCD32F-4279-48B4-ADD9-385A5CB1D145}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEngine.Core.NET", "..\..\Core\SharpEngine.Core.NET.csproj", "{66489F2C-3EFC-99B8-140D-3076B120A01E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Release|Any CPU.Build.0 = Release|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {26D1831D-3120-4E90-BF49-0844EB460F55} + EndGlobalSection +EndGlobal diff --git a/Examples/Minimal/Minimal/Minimal.csproj b/Examples/Minimal/Minimal/Minimal.csproj new file mode 100644 index 0000000..e55defb --- /dev/null +++ b/Examples/Minimal/Minimal/Minimal.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/Examples/Minimal/Minimal/Program.cs b/Examples/Minimal/Minimal/Program.cs new file mode 100644 index 0000000..f056eb2 --- /dev/null +++ b/Examples/Minimal/Minimal/Program.cs @@ -0,0 +1,52 @@ +using SharpEngine.Core; +using SharpEngine.Core.Entities.Views; +using SharpEngine.Core.Enums; +using SharpEngine.Core.Interfaces; +using SharpEngine.Core.Renderers; +using SharpEngine.Core.Scenes; +using Silk.NET.Input; +using Silk.NET.Maths; +using Silk.NET.Windowing; + +namespace SharpEngine.Core +{ + // TODO: Minimal isn't really "minimal" + // TODO: Make this project work. + + public static class Program + { + public static void Main(string[] args) + { + var game = new Game(); + var window = new Window(game, new Scene(), new WindowOptions() with + { + Title = "SharpEngine minimal game", + Size = new Vector2D(800, 600) + }); + + window.Run(); + } + } + + public class Settings : ISettings + { + public WindowOptions WindowOptions { get; set; } = new WindowOptions(); + public bool UseWireFrame { get; set; } + public bool PrintFrameRate { get; set; } + public RenderFlags RendererFlags { get; set; } + } + + public class Game : IGame + { + public ISettings CoreSettings => new Settings(); + + public CameraView Camera { get; set; } + + public void HandleKeyboard(IKeyboard input, double deltaTime) { } + public void HandleMouse(IMouse mouse) { } + public void HandleMouseDown(IMouse mouse, MouseButton button) { } + public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) { } + public void Initialize() { } + public void Update(double deltaTime, IInputContext input) { } + } +} diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj index 8ffada1..bc6e7b6 100644 --- a/Launcher/Launcher.csproj +++ b/Launcher/Launcher.csproj @@ -69,10 +69,14 @@ - - + + + + + - + + diff --git a/Launcher/MauiProgram.cs b/Launcher/MauiProgram.cs index 3d0393d..d079b26 100644 --- a/Launcher/MauiProgram.cs +++ b/Launcher/MauiProgram.cs @@ -25,8 +25,7 @@ public static MauiApp CreateMauiApp() builder.Services.AddMauiBlazorWebView(); builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddScoped(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); diff --git a/Launcher/Services/ApplicationManager.cs b/Launcher/Services/ApplicationManager.cs deleted file mode 100644 index 58e8c28..0000000 --- a/Launcher/Services/ApplicationManager.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Launcher.Services -{ - public interface IApplicationManager - { - void Close(); - } - - public class ApplicationManager : IApplicationManager - { - public void Close() - { - // TODO: Does not work - var window = (Application.Current as App)?.Handler?.MauiContext?.Services.GetService(); - window?.Close(); - } - } -} diff --git a/Launcher/Services/ProjectInitializationService.cs b/Launcher/Services/EditorService.cs similarity index 63% rename from Launcher/Services/ProjectInitializationService.cs rename to Launcher/Services/EditorService.cs index 1aba1e3..185dfd4 100644 --- a/Launcher/Services/ProjectInitializationService.cs +++ b/Launcher/Services/EditorService.cs @@ -4,23 +4,25 @@ namespace Launcher.Services { - public interface IProjectInitializationService + public interface IEditorService { void Initialize(Project project); + void OpenInEditor(Project project); } - public class ProjectInitializationService : IProjectInitializationService + public class EditorService : IEditorService { + private const string SHARP_ENGINE_PROJECT_EXTENSION = ".sharpproject"; private const string SHARP_ENGINE_CORE_NUGET_PACKAGE = "SharpEngine.Core"; private const string FRAMEWORK = "net8.0"; private readonly INotificationService _notificationService; /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// /// The notification message service used to display messages in the UI. - public ProjectInitializationService(INotificationService notificationService) + public EditorService(INotificationService notificationService) { _notificationService = notificationService; } @@ -28,7 +30,6 @@ public ProjectInitializationService(INotificationService notificationService) /// public void Initialize(Project project) { - CreateProjectDirectory(project); CreateSharpEngineProject(project); CreateSolution(project); @@ -43,7 +44,8 @@ private static void CreateProjectDirectory(Project project) private static void CreateSharpEngineProject(Project project) { var json = System.Text.Json.JsonSerializer.Serialize(project); - File.WriteAllText(Path.Join(project.Path, $"{project.Name}.sharpproject"), json); + string projectFilePath = Path.Join(project.Path, $"{project.Name}.{SHARP_ENGINE_PROJECT_EXTENSION}"); + File.WriteAllText(projectFilePath, json); } private void CreateSolution(Project project) @@ -65,8 +67,27 @@ private void CreateSolution(Project project) $"-c \"{createSolution}; {createProject}; {addProjectToSolution}; {installNugetPackage}\""; // Setup process - var process = new Process + Process process = GetProcess(arguments); + process.Start(); + process.WaitForExit(); + + // Read and display output (optional) + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + + if (!string.IsNullOrWhiteSpace(error)) + _notificationService.Show(error); + + string programFilePath = Path.Combine(project.Path!, "Program.cs"); + string programContent = ""; // TODO: Replace with program content + + File.WriteAllText(programFilePath, programContent); + } + + private static Process GetProcess(string arguments) + => new() { + StartInfo = new ProcessStartInfo { FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "/bin/sh", @@ -78,20 +99,37 @@ private void CreateSolution(Project project) } }; - process.Start(); - process.WaitForExit(); + public void OpenInEditor(Project project) + { + if (!Path.Exists(project.Path)) + { + _notificationService.Show($"The project file '{project.Path}' no longer exists."); + return; + } - // Read and display output (optional) - string output = process.StandardOutput.ReadToEnd(); - string error = process.StandardError.ReadToEnd(); + try + { + const string editorPathEnvironmentVariable = "SHARPENGINE_EDITOR_PATH"; + string? editorPath = Environment.GetEnvironmentVariable(editorPathEnvironmentVariable); + if (editorPath is null) + { + _notificationService.Show("Editor variable not found"); + return; + } - if (!string.IsNullOrWhiteSpace(error)) - _notificationService.Show(error); + const string editorExecutable = "SharpEngine.Editor.exe"; - string programFilePath = Path.Combine(project.Path!, "Program.cs"); - string programContent = ""; // TODO: Replace with program content + string projectFile = project.Path.EndsWith(SHARP_ENGINE_PROJECT_EXTENSION) ? + project.Path : $"{project.Path}/{project.Name}.{SHARP_ENGINE_PROJECT_EXTENSION}"; - File.WriteAllText(programFilePath, programContent); + var process = GetProcess($"{editorPath}/{editorExecutable} {projectFile}"); + process.Start(); + process.WaitForExit(); + } + catch (Exception ex) + { + _notificationService.Show($"An error occurred while opening the project in the editor.", ex.Message); + } } } } diff --git a/Launcher/Services/NotificationService.cs b/Launcher/Services/NotificationService.cs index 3ccca92..d2e3cdb 100644 --- a/Launcher/Services/NotificationService.cs +++ b/Launcher/Services/NotificationService.cs @@ -1,15 +1,21 @@ -namespace Launcher.Services +using Launcher.UI; +using Serilog; +using Serilog.Core; + +namespace Launcher.Services { /// /// Contains definitions for displaying notifications in the UI. /// public interface INotificationService { + void Discard(string message); + /// /// Displays a notification message in the UI. /// /// - void Show(string message); + void Show(string message, bool writeToFile = false, params object?[] details); } /// @@ -17,12 +23,44 @@ public interface INotificationService /// public class NotificationService : INotificationService { - // TODO: Dictionary to store the notification messages + // TODO: The notification messages should be added into a separate service. + private Dictionary _notificationMessages = new() + { + { "some message", 2 }, + { "another", 2 }, + }; + + private Logger _log; + + public NotificationService() + { + _log = new LoggerConfiguration() + .WriteTo.Console() + .CreateLogger(); + } /// - public void Show(string message) + public void Show(string message, bool writeToFile = false, params object?[] details) { + if (writeToFile) + _log.Information(message, details); + + if (_notificationMessages.TryGetValue(message, out int count)) + _notificationMessages[message] = count + 1; + else + _notificationMessages.Add(message, 1); + } + /// + public void Discard(string message) + { + if (!_notificationMessages.TryGetValue(message, out int amount) || amount < 1) + return; + + if (amount == 1) + _notificationMessages.Remove(message); + else + _notificationMessages[message] = amount - 1; } } } diff --git a/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor index ae3c60d..52ed7ad 100644 --- a/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor +++ b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor @@ -3,7 +3,7 @@ - -@code { - // This method needs to be in the razor file due to the way the MAUI GetParentWindow method is exposed. - private void OpenInEditor(Project project) - { - if (!Path.Exists(project.Path)) - { - HandleMessage($"The project file '{project.Path}' no longer exists."); - return; - } - - // TODO: Close down the window when a valid project is loaded. - Process.Start("cmd"); - _applicationManager.Close(); - // Application.Current.CloseWindow(GetParentWindow()); - } -} \ No newline at end of file + \ No newline at end of file diff --git a/Launcher/UI/Home.razor.cs b/Launcher/UI/Home.razor.cs index 5674e39..eb6a3bd 100644 --- a/Launcher/UI/Home.razor.cs +++ b/Launcher/UI/Home.razor.cs @@ -4,52 +4,29 @@ namespace Launcher.UI { - public class Message - { - public readonly Guid Id = Guid.NewGuid(); - public string Contents { get; set; } = string.Empty; - - public Message(string contents) - { - Contents = contents; - } - } - /// Represents the home page of the launcher. public partial class Home : ComponentBase { [Inject] - private IProjectInitializationService _projectInitializationService { get; init; } = default!; + private IEditorService _editorService { get; init; } = default!; [Inject] private INotificationService _notificationService { get; init; } = default!; - [Inject] - private IApplicationManager _applicationManager { get; init; } = default!; - - private bool _showNotification; private bool _showConfirmDelete; private bool _showCreateProjectDialog; - // TODO: The notification messages should be added into a separate service. - private Dictionary _notificationMessages = new() - { - { new("some message"), 2 }, - { new("another"), 2 }, - }; - private const string _projectsFile = "projects.json"; private const string _launcherEnvironmentVariable = "SHARP_ENGINE_LAUNCHER_DIRECTORY"; private readonly string _currentDirectory = AppContext.BaseDirectory; - private string _projectDirectory => Path.Join(_currentDirectory, _projectsFile); + private string _projectFilePath => Path.Join(_currentDirectory, _projectsFile); - - private List Projects { get; set; } = new() - { - // TODO: Remove these mock projects once the actual projects are loaded. - new Project { Name = "Project 1", LastModified = DateTime.Now }, - new Project { Name = "Project 2", LastModified = DateTime.Now.AddDays(6).AddHours(2) }, - }; + private List Projects { get; set; } = new(); + //{ + // // TODO: Remove these mock projects once the actual projects are loaded. + // new Project { Name = "Project 1", LastModified = DateTime.Now }, + // new Project { Name = "Project 2", LastModified = DateTime.Now.AddDays(6).AddHours(2) }, + //}; private Project _selectedProject = new(); @@ -63,7 +40,7 @@ protected override void OnInitialized() // TODO: Check if GitHub has a new version released. CheckEnvironmentVariable(); - // LoadProjects(); + LoadProjects(); } private void CheckEnvironmentVariable() @@ -75,21 +52,30 @@ private void CheckEnvironmentVariable() private void LoadProjects() { - if (!File.Exists(_projectDirectory)) - File.WriteAllText(_projectDirectory, "[]"); + if (!File.Exists(_projectFilePath)) + { + File.WriteAllText(_projectFilePath, "[]"); + return; + } - string json = File.ReadAllText(_projectDirectory); + string json = File.ReadAllText(_projectFilePath); var projects = System.Text.Json.JsonSerializer.Deserialize>(json); if (projects is not null) Projects = projects; else - HandleMessage($"Unable to load projects.", json, projects); + _notificationService.Show($"Unable to load projects.", false, json, projects); } private async Task LoadFileAsync(InputFileChangeEventArgs e) { - // TODO: Check if file is a "sharpproject.json" file + const string sharpProjectExtension = ".sharpproject"; + string fileName = e.File.Name; + if (!fileName.EndsWith(sharpProjectExtension, StringComparison.OrdinalIgnoreCase)) + { + _notificationService.Show($"The file '{fileName}' is not a valid project file."); + return; + } using var stream = e.File.OpenReadStream(); using var reader = new StreamReader(stream); @@ -100,7 +86,7 @@ private async Task LoadFileAsync(InputFileChangeEventArgs e) if (project is not null) Projects.Add(project); else - HandleMessage($"Unable to load project file.", project, e.File.Name, fileContent); + _notificationService.Show($"Unable to load project file.", false, project, e.File.Name, fileContent); } private void OnCreateProjectClicked() @@ -119,24 +105,13 @@ private void OnDeleteProjectConfirm() if (project is not null) Projects.Remove(project); else - HandleMessage($"Unable to delete project '{project?.Name}'.", _selectedProject, Projects); + _notificationService.Show($"Unable to delete project '{project?.Name}'.", false, _selectedProject, Projects); } private void ProjectCreated(Project project) { - _projectInitializationService.Initialize(project); + _editorService.Initialize(project); Projects.Add(project); } - - private void HandleMessage(string message, params object?[] details) - { - // TODO: Get the message using the Id - // if (!_notificationMessages.TryGetValue(message, out int amount)) - _notificationMessages.Add(new Message(message), 1); - // else - // _notificationMessages[message] = amount + 1; - - // TODO: Log the message and details. - } } } diff --git a/Launcher/UI/_Imports.razor b/Launcher/UI/_Imports.razor index ac1123b..464f868 100644 --- a/Launcher/UI/_Imports.razor +++ b/Launcher/UI/_Imports.razor @@ -8,4 +8,5 @@ @using Microsoft.JSInterop @using Launcher -@using Launcher.UI \ No newline at end of file +@using Launcher.UI +@using Launcher.Services \ No newline at end of file From b9d37631cac09df2338ae6e67fdc2970146f1b26 Mon Sep 17 00:00:00 2001 From: Antti Veikkolainen Date: Fri, 14 Mar 2025 00:25:46 +0200 Subject: [PATCH 2/4] #28 Launcher finalization --- Launcher/Launcher.csproj | 1 + Launcher/MauiProgram.cs | 2 +- Launcher/Services/EditorService.cs | 97 +++++++++++--- Launcher/Services/NotificationService.cs | 50 ++++--- .../Dialogs/ConfirmDeleteDialog.razor.css | 14 ++ .../Dialogs/ConfirmDeleteDialog.razor.min.css | 1 + .../Dialogs/CreateProjectDialog.razor | 38 +----- .../Dialogs/CreateProjectDialog.razor.cs | 37 ++++++ .../UI/Components/Dialogs/Notification.razor | 25 ++-- .../UI/Components/Dialogs/NotificationType.cs | 8 +- Launcher/UI/Home.razor | 4 +- Launcher/UI/Home.razor.cs | 123 +++++++++--------- Launcher/UI/Project.cs | 3 +- Launcher/compilerconfig.json | 4 + Launcher/wwwroot/index.html | 2 - Launcher/wwwroot/js/filePicker.js | 3 - 16 files changed, 252 insertions(+), 160 deletions(-) create mode 100644 Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.css create mode 100644 Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.min.css create mode 100644 Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs delete mode 100644 Launcher/wwwroot/js/filePicker.js diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj index bc6e7b6..06a471b 100644 --- a/Launcher/Launcher.csproj +++ b/Launcher/Launcher.csproj @@ -70,6 +70,7 @@ + diff --git a/Launcher/MauiProgram.cs b/Launcher/MauiProgram.cs index d079b26..3fe0581 100644 --- a/Launcher/MauiProgram.cs +++ b/Launcher/MauiProgram.cs @@ -24,7 +24,7 @@ public static MauiApp CreateMauiApp() builder.Services.AddMauiBlazorWebView(); - builder.Services.AddScoped(); + builder.Services.AddSingleton(); builder.Services.AddScoped(); #if DEBUG diff --git a/Launcher/Services/EditorService.cs b/Launcher/Services/EditorService.cs index 185dfd4..211adfe 100644 --- a/Launcher/Services/EditorService.cs +++ b/Launcher/Services/EditorService.cs @@ -4,15 +4,49 @@ namespace Launcher.Services { + /// + /// Contains definitions for interacting with the editor. + /// public interface IEditorService { + /// + /// Creates a new project and solution. + /// + /// The project to be created. void Initialize(Project project); + + /// + /// Opens the specified project in the editor. + /// + /// The file to be opened. + /// + /// A representing an asynchronous operation. + /// The result of the operation contains the project that was loaded; if the project could not be loaded. + /// + Task LoadFileAsync(string filePath); + + /// + /// Loads the projects previously associated with the launcher. + /// + /// The projects file. + /// + /// All the found projects. + /// + List LoadProjects(string projectsFile); + + /// + /// Opens the specified project in the editor. + /// + /// void OpenInEditor(Project project); } + /// + /// Handles interactions with the editor. + /// public class EditorService : IEditorService { - private const string SHARP_ENGINE_PROJECT_EXTENSION = ".sharpproject"; + private const string SHARP_ENGINE_PROJECT_EXTENSION = "sharpproject"; private const string SHARP_ENGINE_CORE_NUGET_PACKAGE = "SharpEngine.Core"; private const string FRAMEWORK = "net8.0"; @@ -30,17 +64,10 @@ public EditorService(INotificationService notificationService) /// public void Initialize(Project project) { - CreateProjectDirectory(project); CreateSharpEngineProject(project); CreateSolution(project); } - private static void CreateProjectDirectory(Project project) - { - string projectDirectory = Path.GetDirectoryName(Path.GetFullPath(project.Path!))!; - Directory.CreateDirectory(projectDirectory); - } - private static void CreateSharpEngineProject(Project project) { var json = System.Text.Json.JsonSerializer.Serialize(project); @@ -53,9 +80,9 @@ private void CreateSolution(Project project) string projectName = project.Name!.Replace(" ", "_"); // Define individual commands - string createSolution = $"dotnet new sln -n {projectName}"; + string createSolution = $"dotnet new sln -n {projectName} -o {project.Path}"; string createProject = $"dotnet new console -n {projectName} -o {project.Path} -f {FRAMEWORK}"; - string addProjectToSolution = $"dotnet sln {projectName}.sln add {project.Path}/{projectName}.csproj"; + string addProjectToSolution = $"dotnet sln {project.Path}/{projectName}.sln add {project.Path}/{projectName}.csproj"; // TODO: Add the nuget package after it's been published //string installNugetPackage = $"dotnet add {project.Path}/{projectName}.csproj package {SHARP_ENGINE_CORE_NUGET_PACKAGE}"; @@ -77,11 +104,14 @@ private void CreateSolution(Project project) if (!string.IsNullOrWhiteSpace(error)) _notificationService.Show(error); - - string programFilePath = Path.Combine(project.Path!, "Program.cs"); - string programContent = ""; // TODO: Replace with program content - - File.WriteAllText(programFilePath, programContent); + else + _notificationService.Show("Solution and project created successfully!"); + + // TODO: Set example programc.cs content with the minimal projects content. + // string programFilePath = Path.Combine(project.Path!, "Program.cs"); + // string programContent = ""; // TODO: Replace with program content + // + // File.WriteAllText(programFilePath, programContent); } private static Process GetProcess(string arguments) @@ -99,6 +129,7 @@ private static Process GetProcess(string arguments) } }; + /// public void OpenInEditor(Project project) { if (!Path.Exists(project.Path)) @@ -128,8 +159,42 @@ public void OpenInEditor(Project project) } catch (Exception ex) { - _notificationService.Show($"An error occurred while opening the project in the editor.", ex.Message); + _notificationService.Show($"An error occurred while opening the project in the editor.", false, ex.Message); } } + + /// + public List LoadProjects(string projectsFile) + { + if (!File.Exists(projectsFile)) + { + File.WriteAllText(projectsFile, "[]"); + return []; + } + + string json = File.ReadAllText(projectsFile); + var projects = System.Text.Json.JsonSerializer.Deserialize>(json); + + if (projects is not null) + return projects; + + _notificationService.Show($"Unable to load projects.", false, json, projects); + return []; + } + + /// + public async Task LoadFileAsync(string filePath) + { + if (!filePath.EndsWith(SHARP_ENGINE_PROJECT_EXTENSION, StringComparison.OrdinalIgnoreCase)) + { + _notificationService.Show($"The file '{filePath}' is not a valid project file."); + return null; + } + + using var reader = new StreamReader(filePath); + string fileContent = await reader.ReadToEndAsync(); + + return System.Text.Json.JsonSerializer.Deserialize(fileContent); + } } } diff --git a/Launcher/Services/NotificationService.cs b/Launcher/Services/NotificationService.cs index d2e3cdb..f9b9185 100644 --- a/Launcher/Services/NotificationService.cs +++ b/Launcher/Services/NotificationService.cs @@ -1,5 +1,4 @@ -using Launcher.UI; -using Serilog; +using Serilog; using Serilog.Core; namespace Launcher.Services @@ -9,12 +8,24 @@ namespace Launcher.Services /// public interface INotificationService { + /// Gets or sets the notifications that are displayed in the UI. + Dictionary Notifications { get; set; } + + /// Sets the event executed when the notifications are changed. + void SetOnNotificationsChanged(Action action); + + /// + /// Discards a notification message from the UI. + /// + /// The message to be discarded. void Discard(string message); /// /// Displays a notification message in the UI. /// - /// + /// The message to be displayed. + /// Determines whether the message should be logged to a file. + /// The details that should be logged with the message. void Show(string message, bool writeToFile = false, params object?[] details); } @@ -23,15 +34,15 @@ public interface INotificationService /// public class NotificationService : INotificationService { - // TODO: The notification messages should be added into a separate service. - private Dictionary _notificationMessages = new() - { - { "some message", 2 }, - { "another", 2 }, - }; + /// + public Dictionary Notifications { get; set; } = new(); - private Logger _log; + private event Action? OnNotificationsChanged; + private readonly Logger _log; + /// + /// Initializes a new instance of . + /// public NotificationService() { _log = new LoggerConfiguration() @@ -39,28 +50,35 @@ public NotificationService() .CreateLogger(); } + /// + public void SetOnNotificationsChanged(Action action) + => OnNotificationsChanged = action; + /// public void Show(string message, bool writeToFile = false, params object?[] details) { if (writeToFile) _log.Information(message, details); - if (_notificationMessages.TryGetValue(message, out int count)) - _notificationMessages[message] = count + 1; + if (Notifications.TryGetValue(message, out int count)) + Notifications[message] = count + 1; else - _notificationMessages.Add(message, 1); + Notifications.Add(message, 1); + + // Notify UI about the change + OnNotificationsChanged?.Invoke(); } /// public void Discard(string message) { - if (!_notificationMessages.TryGetValue(message, out int amount) || amount < 1) + if (!Notifications.TryGetValue(message, out int amount) || amount < 1) return; if (amount == 1) - _notificationMessages.Remove(message); + Notifications.Remove(message); else - _notificationMessages[message] = amount - 1; + Notifications[message] = amount - 1; } } } diff --git a/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.css b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.css new file mode 100644 index 0000000..be1d367 --- /dev/null +++ b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.css @@ -0,0 +1,14 @@ +.dialog { + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + width: auto; + max-width: 90%; +} + +.flex-end { + justify-content: end; + display: flex; + gap: 8px; +} diff --git a/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.min.css b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.min.css new file mode 100644 index 0000000..3266ec4 --- /dev/null +++ b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor.min.css @@ -0,0 +1 @@ +.dialog{left:0;right:0;margin-left:auto;margin-right:auto;width:auto;max-width:90%;}.flex-end{justify-content:end;display:flex;gap:8px;} \ No newline at end of file diff --git a/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor b/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor index ff93aa2..27d5313 100644 --- a/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor +++ b/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor @@ -20,7 +20,7 @@
- +
@@ -34,39 +34,3 @@ - -@code { - private string _createProjectDialogVisibleClass => Visible ? "create-project-dialog-visible" : "create-project-dialog-hidden"; - - private EditContext _createProjectFormEditContext; - private Project _newProject; - - /// Gets or sets the event executed when the project is created. - [Parameter] - public EventCallback OnProjectCreated { get; set; } - - /// - /// Initializes a new instance of . - /// - public CreateProjectDialog() - { - _newProject = new(); - _createProjectFormEditContext = new(_newProject); - } - - /// - protected override void OnInitialized() - { - base.OnInitialized(); - - _createProjectFormEditContext = new(_newProject); - } - - private async Task OnCreateProjectSubmitClickedAsync() - { - await OnProjectCreated.InvokeAsync(_newProject); - - _newProject = new(); - await CloseAsync(); - } -} diff --git a/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs b/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs new file mode 100644 index 0000000..b4842e0 --- /dev/null +++ b/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Components; +using Launcher.Services; +using CommunityToolkit.Maui.Storage; + +namespace Launcher.UI.Components.Dialogs +{ + public partial class CreateProjectDialog : DialogBase + { + [Inject] + private INotificationService _notificationService { get; init; } = default!; + + private string _createProjectDialogVisibleClass => Visible ? "create-project-dialog-visible" : "create-project-dialog-hidden"; + private Project _newProject = new(); + + /// Gets or sets the event executed when the project is created. + [Parameter] + public EventCallback OnProjectCreated { get; set; } + + private async Task OnCreateProjectSubmitClickedAsync() + { + await OnProjectCreated.InvokeAsync(_newProject); + + _newProject = new(); + await CloseAsync(); + } + + private async Task HandleFileSelection() + { + var result = await FolderPicker.Default.PickAsync(); + + if (result is not null && result.IsSuccessful) + _newProject.Path = result.Folder.Path; + else + _notificationService.Show("No file selected.", false); + } + } +} diff --git a/Launcher/UI/Components/Dialogs/Notification.razor b/Launcher/UI/Components/Dialogs/Notification.razor index 050fc0a..4943980 100644 --- a/Launcher/UI/Components/Dialogs/Notification.razor +++ b/Launcher/UI/Components/Dialogs/Notification.razor @@ -1,19 +1,24 @@ -@inherits DialogBase +@using Launcher.Services +@inherits DialogBase @inject INotificationService NotificationService -@foreach (var notification in NotificationService.Notifications) -{ - -} +
+ @foreach (var notification in NotificationService.Notifications) + { + + } +
@code { - private string _notificationVisibleClass => Visible ? "notification-visible" : "notification-hidden"; - /// Gets or sets the type of the notification. [Parameter] public NotificationType Type { get; set; } = NotificationType.Warning; + + /// + protected override void OnInitialized() + => NotificationService.SetOnNotificationsChanged(StateHasChanged); } diff --git a/Launcher/UI/Components/Dialogs/NotificationType.cs b/Launcher/UI/Components/Dialogs/NotificationType.cs index 4ce4c7b..9f34c0a 100644 --- a/Launcher/UI/Components/Dialogs/NotificationType.cs +++ b/Launcher/UI/Components/Dialogs/NotificationType.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Launcher.UI.Components +namespace Launcher.UI.Components { /// /// Represents the type of notification. diff --git a/Launcher/UI/Home.razor b/Launcher/UI/Home.razor index 0b8a1b8..35fb873 100644 --- a/Launcher/UI/Home.razor +++ b/Launcher/UI/Home.razor @@ -12,9 +12,7 @@

Projects

- @* TODO: Make this button work - - *@ +
diff --git a/Launcher/UI/Home.razor.cs b/Launcher/UI/Home.razor.cs index eb6a3bd..e0e57a4 100644 --- a/Launcher/UI/Home.razor.cs +++ b/Launcher/UI/Home.razor.cs @@ -1,6 +1,7 @@ using Launcher.Services; using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Forms; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Launcher.UI { @@ -16,77 +17,30 @@ public partial class Home : ComponentBase private bool _showConfirmDelete; private bool _showCreateProjectDialog; - private const string _projectsFile = "projects.json"; - private const string _launcherEnvironmentVariable = "SHARP_ENGINE_LAUNCHER_DIRECTORY"; - private readonly string _currentDirectory = AppContext.BaseDirectory; - private string _projectFilePath => Path.Join(_currentDirectory, _projectsFile); - - private List Projects { get; set; } = new(); - //{ - // // TODO: Remove these mock projects once the actual projects are loaded. - // new Project { Name = "Project 1", LastModified = DateTime.Now }, - // new Project { Name = "Project 2", LastModified = DateTime.Now.AddDays(6).AddHours(2) }, - //}; - + private List Projects { get; set; } = []; private Project _selectedProject = new(); + private const string _projectsFile = "projects.json"; + private static readonly string[] _sharpProjectFilePickerExtension = [".sharpproject"]; +#if DEBUG + private string _projectsFilePath = Path.Join(Path.GetTempPath(), _projectsFile); +#else + private string _projectsFilePath = Path.Join(AppContext.BaseDirectory, _projectsFile); +#endif /// - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { - base.OnInitialized(); + await base.OnInitializedAsync(); // TODO: Get current launcher version - // TODO: Check if GitHub has a new version released. - CheckEnvironmentVariable(); - LoadProjects(); - } - - private void CheckEnvironmentVariable() - { - var launcherDirectory = Environment.GetEnvironmentVariable(_launcherEnvironmentVariable); - if (string.IsNullOrEmpty(launcherDirectory) || launcherDirectory != _currentDirectory) - Environment.SetEnvironmentVariable(_launcherEnvironmentVariable, _currentDirectory); - } - - private void LoadProjects() - { - if (!File.Exists(_projectFilePath)) - { - File.WriteAllText(_projectFilePath, "[]"); - return; - } - - string json = File.ReadAllText(_projectFilePath); - var projects = System.Text.Json.JsonSerializer.Deserialize>(json); - + var projects = _editorService.LoadProjects(_projectsFilePath); + if (projects is not null) - Projects = projects; + Projects.AddRange(projects); else - _notificationService.Show($"Unable to load projects.", false, json, projects); - } - - private async Task LoadFileAsync(InputFileChangeEventArgs e) - { - const string sharpProjectExtension = ".sharpproject"; - string fileName = e.File.Name; - if (!fileName.EndsWith(sharpProjectExtension, StringComparison.OrdinalIgnoreCase)) - { - _notificationService.Show($"The file '{fileName}' is not a valid project file."); - return; - } - - using var stream = e.File.OpenReadStream(); - using var reader = new StreamReader(stream); - string fileContent = await reader.ReadToEndAsync(); - - var project = System.Text.Json.JsonSerializer.Deserialize(fileContent); - - if (project is not null) - Projects.Add(project); - else - _notificationService.Show($"Unable to load project file.", false, project, e.File.Name, fileContent); + _notificationService.Show($"Unable to load project file.", false, projects); } private void OnCreateProjectClicked() @@ -101,9 +55,11 @@ private void OnDeleteProjectClicked(Project project) private void OnDeleteProjectConfirm() { var project = Projects.Find(p => p.Id == _selectedProject.Id); - if (project is not null) + { Projects.Remove(project); + // TODO: Delete project files + } else _notificationService.Show($"Unable to delete project '{project?.Name}'.", false, _selectedProject, Projects); } @@ -111,7 +67,48 @@ private void OnDeleteProjectConfirm() private void ProjectCreated(Project project) { _editorService.Initialize(project); + SetProjects(project); + } + + private async Task OpenExistingFileAsync() + { + var pickedFile = await FilePicker.Default.PickAsync(new PickOptions + { + PickerTitle = "Select a SharpProject file", + FileTypes = new FilePickerFileType(new Dictionary> + { + { DevicePlatform.WinUI, _sharpProjectFilePickerExtension }, + { DevicePlatform.MacCatalyst, _sharpProjectFilePickerExtension }, + { DevicePlatform.iOS, _sharpProjectFilePickerExtension }, + { DevicePlatform.Android, new[] { "application/octet-stream" } } + }) + }); + + if (pickedFile?.FileName.EndsWith(".sharpproject") == false) + { + _notificationService.Show("Invalid file type selected. Please select a .sharpproject file."); + return; + } + + var project = await _editorService.LoadFileAsync(pickedFile!.FullPath); + if (project is not null) + SetProjects(project); + else + _notificationService.Show($"Unable to load project file '{pickedFile.FullPath}'"); + } + + private void SetProjects(Project project) + { + bool projectAlreadyExists = Projects.Any(p => p.Path == project.Path || p.Name == project.Name); + if (projectAlreadyExists) + { + _notificationService.Show($"Project '{project.Name}' already exists.", false, project); + return; + } + Projects.Add(project); + string json = JsonSerializer.Serialize(Projects); + File.WriteAllText(_projectsFilePath, json); } } } diff --git a/Launcher/UI/Project.cs b/Launcher/UI/Project.cs index f56f3c5..bbdac82 100644 --- a/Launcher/UI/Project.cs +++ b/Launcher/UI/Project.cs @@ -24,12 +24,11 @@ public class Project /// Gets or sets the path to the project file. ///
[Required] - [File] public string? Path { get; set; } /// /// Gets or sets when the project was last modified. /// [JsonIgnore] - public DateTime LastModified { get; set; } + public DateTime LastModified { get; set; } = DateTime.Now; } diff --git a/Launcher/compilerconfig.json b/Launcher/compilerconfig.json index 0b02419..9fe060d 100644 --- a/Launcher/compilerconfig.json +++ b/Launcher/compilerconfig.json @@ -18,5 +18,9 @@ { "outputFile": "wwwroot/css/additional.css", "inputFile": "wwwroot/css/additional.scss" + }, + { + "outputFile": "UI/Components/Dialogs/ConfirmDeleteDialog.razor.css", + "inputFile": "UI/Components/Dialogs/ConfirmDeleteDialog.razor.scss" } ] \ No newline at end of file diff --git a/Launcher/wwwroot/index.html b/Launcher/wwwroot/index.html index 15dd33a..0adf935 100644 --- a/Launcher/wwwroot/index.html +++ b/Launcher/wwwroot/index.html @@ -12,8 +12,6 @@ - - diff --git a/Launcher/wwwroot/js/filePicker.js b/Launcher/wwwroot/js/filePicker.js deleted file mode 100644 index 43518c0..0000000 --- a/Launcher/wwwroot/js/filePicker.js +++ /dev/null @@ -1,3 +0,0 @@ -window.triggerFileInput = function (id) { - document.getElementById(id).click(); -}; \ No newline at end of file From c5815a25bb0fee51842bfe480da0beb5a7be24e2 Mon Sep 17 00:00:00 2001 From: Antti Veikkolainen Date: Sat, 15 Mar 2025 15:19:37 +0200 Subject: [PATCH 3/4] #28 Bunch of fixes and improvements Added root level solution to contain every project for easier access. New configuration for launcher to only handle windows builds for faster build times. Fixed minimal project and cleaned up redundant classes --- Core/Interfaces/IGame.cs | 19 +++--- Core/Interfaces/ISettings.cs | 20 ++++++ Core/Renderers/Renderer.cs | 4 +- Core/Renderers/UIRenderer.cs | 2 +- ...ore.NET.csproj => SharpEngine.Core.csproj} | 4 ++ Core/SilkWindow.cs | 44 ++++++------ Core/Window.cs | 68 +++++++++++++------ Editor/Editor.cs | 41 +---------- Editor/EditorWindow.cs | 3 +- Editor/Program.cs | 4 +- Editor/SharpEngine.Editor.csproj | 3 +- .../MinecraftClone/{Game.cs => Minecraft.cs} | 60 ++++++++-------- Examples/MinecraftClone/Minecraft.csproj | 3 +- Examples/MinecraftClone/Program.cs | 8 +-- Examples/MinecraftClone/Settings.cs | 22 ------ Examples/Minimal/{Minimal => }/Minimal.csproj | 3 +- Examples/Minimal/Minimal/Program.cs | 52 -------------- Examples/Minimal/Program.cs | 47 +++++++++++++ Launcher/Launcher.csproj | 11 ++- SharpEngine.sln | 63 +++++++++++++++++ SharpEngine.slnx | 27 ++++++++ 21 files changed, 296 insertions(+), 212 deletions(-) rename Core/{SharpEngine.Core.NET.csproj => SharpEngine.Core.csproj} (90%) rename Examples/MinecraftClone/{Game.cs => Minecraft.cs} (82%) delete mode 100644 Examples/MinecraftClone/Settings.cs rename Examples/Minimal/{Minimal => }/Minimal.csproj (66%) delete mode 100644 Examples/Minimal/Minimal/Program.cs create mode 100644 Examples/Minimal/Program.cs create mode 100644 SharpEngine.sln create mode 100644 SharpEngine.slnx diff --git a/Core/Interfaces/IGame.cs b/Core/Interfaces/IGame.cs index ed8428f..de0b798 100644 --- a/Core/Interfaces/IGame.cs +++ b/Core/Interfaces/IGame.cs @@ -1,4 +1,5 @@ using SharpEngine.Core.Entities.Views; +using SharpEngine.Core.Entities.Views.Settings; using SharpEngine.Core.Enums; using Silk.NET.Input; @@ -9,50 +10,50 @@ namespace SharpEngine.Core.Interfaces; /// Contains definitions the Game class must implement. /// To consider: move into a abstract class so that the game doesn't necessarily have to implement all methods. /// -public interface IGame +public class Game { /// /// Gets or sets the core settings of the game. /// - public ISettings CoreSettings { get; } + public ISettings CoreSettings { get; init; } = new DefaultSettings(); /// /// Gets or sets the camera of the game. /// - public CameraView Camera { get; set; } + public CameraView Camera { get; set; } = new(new System.Numerics.Vector3(0), new DefaultViewSettings()); /// /// Executed when a mouse button is pressed. /// - public void HandleMouseDown(IMouse mouse, MouseButton button); + public virtual void HandleMouseDown(IMouse mouse, MouseButton button) { } /// /// A method executed when the mouse state is changed /// /// The state of the mouse on the current frame. - public void HandleMouse(IMouse mouse); + public virtual void HandleMouse(IMouse mouse) { } /// /// Gets or sets the event executed when the mouse wheel is scrolled. /// /// The direction the wheel was scrolled. /// Contains information about the scroll wheel changes during the current frame. - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel); + public virtual void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) { } /// /// Executed when a key is pressed. /// /// The state of the keyboard on the current frame. /// The time since the last frame. - public void HandleKeyboard(IKeyboard input, double deltaTime); + public virtual void HandleKeyboard(IKeyboard input, double deltaTime) { } /// /// Executed when the game is first loaded. /// - public void Initialize(); + public virtual void Initialize() { } /// /// Executed each frame. /// - public void Update(double deltaTime, IInputContext input); + public virtual void Update(double deltaTime, IInputContext input) { } } diff --git a/Core/Interfaces/ISettings.cs b/Core/Interfaces/ISettings.cs index bf793b3..8ccad16 100644 --- a/Core/Interfaces/ISettings.cs +++ b/Core/Interfaces/ISettings.cs @@ -1,4 +1,5 @@ using SharpEngine.Core.Renderers; +using Silk.NET.Windowing; namespace SharpEngine.Core.Interfaces; @@ -19,4 +20,23 @@ public interface ISettings /// Gets or sets which renderer should be enabled for the current window. public RenderFlags RendererFlags { get; set; } + + /// Gets or sets the settings for a window. + WindowOptions WindowOptions { get; set; } +} + +/// +public class DefaultSettings : ISettings +{ + /// + public bool UseWireFrame { get; set; } + + /// + public bool PrintFrameRate { get; set; } + + /// + public RenderFlags RendererFlags { get; set; } = RenderFlags.All; + + /// + public WindowOptions WindowOptions { get; set; } } diff --git a/Core/Renderers/Renderer.cs b/Core/Renderers/Renderer.cs index 14eed64..807363b 100644 --- a/Core/Renderers/Renderer.cs +++ b/Core/Renderers/Renderer.cs @@ -21,7 +21,7 @@ public class Renderer : RendererBase private LampShader _lampShader; private LightingShader _lightingShader; - private readonly IGame _game; + private readonly Game _game; private readonly Scene _scene; // Read only once, load into OpenGL buffer once. @@ -35,7 +35,7 @@ public class Renderer : RendererBase /// /// The game the renderer is being used for. /// The game scene to be rendered. - public Renderer(IGame game, Scene scene) : base(game.CoreSettings) + public Renderer(Game game, Scene scene) : base(game.CoreSettings) { _game = game; _scene = scene; diff --git a/Core/Renderers/UIRenderer.cs b/Core/Renderers/UIRenderer.cs index 546496d..b7967aa 100644 --- a/Core/Renderers/UIRenderer.cs +++ b/Core/Renderers/UIRenderer.cs @@ -23,7 +23,7 @@ public class UIRenderer : RendererBase /// /// Initializes a new instance of . /// - public UIRenderer(IGame game, Scene scene) : base(game.CoreSettings) + public UIRenderer(Game game, Scene scene) : base(game.CoreSettings) { _scene = scene; diff --git a/Core/SharpEngine.Core.NET.csproj b/Core/SharpEngine.Core.csproj similarity index 90% rename from Core/SharpEngine.Core.NET.csproj rename to Core/SharpEngine.Core.csproj index 3473f2f..d61202d 100644 --- a/Core/SharpEngine.Core.NET.csproj +++ b/Core/SharpEngine.Core.csproj @@ -13,6 +13,10 @@ https://github.com/Av3boy/SharpEngine game-engine; True + + + $(AssemblyName).NET + Debug;Release;LauncherNonWindows diff --git a/Core/SilkWindow.cs b/Core/SilkWindow.cs index 324fa85..b88605b 100644 --- a/Core/SilkWindow.cs +++ b/Core/SilkWindow.cs @@ -13,7 +13,7 @@ namespace SharpEngine.Core; /// /// Represents a class abstraction for the interface. /// -public abstract class SilkWindow : IWindow +public abstract class SilkWindow : IDisposable { /// public virtual IWindowHost? Parent { get; } @@ -39,12 +39,12 @@ public abstract class SilkWindow : IWindow /// public Vector2D Size { get; set; } - /// - public string Title - { - get => CurrentWindow.Title; - set => CurrentWindow.Title = value; - } + // /// + // public string Title + // { + // get => CurrentWindow.Title; + // set => CurrentWindow.Title = value; + // } /// public WindowState WindowState { get; set; } @@ -158,21 +158,21 @@ public virtual void Close() { } /// public virtual void ContinueEvents() { } - private IWindow? _currentWindow; - - /// Gets or sets the current window. - public IWindow CurrentWindow - { - get => _currentWindow ?? this; - set => _currentWindow = value; - } - - /// - public virtual IWindow CreateWindow(WindowOptions opts) - { - CurrentWindow = Silk.NET.Windowing.Window.Create(opts); - return CurrentWindow; - } + // private IWindow? _currentWindow; + // + // /// Gets or sets the current window. + // public IWindow CurrentWindow + // { + // get => _currentWindow!; + // set => _currentWindow = value; + // } + + /// + // 7public IWindow CreateWindow(WindowOptions opts) + // 7{ + // 7 CurrentWindow = Silk.NET.Windowing.Window.Create(opts); + // 7 return CurrentWindow; + // 7} /// protected virtual void Dispose(bool disposing) { } diff --git a/Core/Window.cs b/Core/Window.cs index ecae665..38c7690 100644 --- a/Core/Window.cs +++ b/Core/Window.cs @@ -28,12 +28,15 @@ namespace SharpEngine.Core; /// public class Window : SilkWindow { - private readonly IGame _game; + private readonly Game _game; + private static IWindow _window; private IEnumerable _renderers = []; private ImGuiController? _imGuiController; + public IInputContext Input { get; protected set; } + /// /// The scene that is currently being rendered. /// @@ -46,7 +49,7 @@ public class Window : SilkWindow /// /// Gets the current OpenGL context. /// - /// The OpennGL context for this window. + /// The OpenGL context for this window. public static GL GetGL() => GL; private static void SetGL(GL gl) => GL = gl; @@ -57,22 +60,35 @@ public class Window : SilkWindow /// /// Contains the actual game implementation. /// Contains the game scene. - public Window(IGame game, Scene scene, WindowOptions options) + public Window(Game game, Scene scene, WindowOptions options) { // TODO: Game should be refactored out of the window class. _game = game; Scene = scene; - CreateWindow(options); - _game.Camera = new CameraView(Vector3.UnitZ * 3, new DefaultViewSettings()); + Initialize(options); + } + + public Window(Scene scene, WindowOptions options) + { + _game = new Game(); + Scene = scene; + + Initialize(options); + } + + private void Initialize(WindowOptions options) + { + _window = Silk.NET.Windowing.Window.Create(options); + // _game.Camera = new CameraView(Vector3.UnitZ * 3, new DefaultViewSettings()); - CurrentWindow.Update += OnUpdateFrame; - CurrentWindow.Render += RenderFrame; - CurrentWindow.Resize += OnResize; - CurrentWindow.Load += OnLoad; - CurrentWindow.Closing += OnClosing; + _window.Update += OnUpdateFrame; + _window.Render += RenderFrame; + _window.Resize += OnResize; + _window.Load += OnLoad; + _window.Closing += OnClosing; - CurrentWindow.Run(); + _window.Run(); } private void OnClosing() @@ -85,10 +101,11 @@ public override void OnLoad() { try { - SetGL(CurrentWindow.CreateOpenGL()); + var context = _window.CreateOpenGL(); + SetGL(context); - Input = CurrentWindow.CreateInput(); - CurrentWindow.MakeCurrent(); + Input = _window.CreateInput(); + _window.MakeCurrent(); AssignInputEvents(); @@ -120,16 +137,19 @@ public override void OnLoad() foreach (var renderer in _renderers) renderer.Initialize(); - _imGuiController = new ImGuiController(GL, CurrentWindow, Input); + _imGuiController = new ImGuiController(GL, _window, Input); _initialized = true; } catch (Exception ex) { - Debug.LogInformation(ex.Message); + Debug.LogInformation("Error loading window: " + ex.Message, ex); } } + protected virtual void PreRender(double deltaTime) { } + + /// /// Renders the current view and all specified renderers. /// @@ -172,6 +192,9 @@ protected void RenderFrame(double deltaTime) } } + protected virtual void AfterRender(double deltaTime) { } + + /// /// Toggles the renderer between wireframe and fill mode. /// @@ -213,7 +236,7 @@ protected void OnUpdateFrame(double deltaTime) _game.HandleMouse(mouse); if (keyboard.IsKeyPressed(Key.Escape)) - CurrentWindow.Close(); + _window.Close(); _game.HandleKeyboard(keyboard, deltaTime); @@ -234,11 +257,14 @@ private void AssignInputEvents() } } + public virtual void OnMouseClick(IMouse mouse, MouseButton button, Vector2 vector) { } + + /// protected void KeyDown(IKeyboard keyboard, Key key, int keyCode) { if (key == Key.Escape) - CurrentWindow.Close(); + _window.Close(); // _game.HandleKeyboard(keyboard); } @@ -266,7 +292,7 @@ protected void OnResize(Vector2D size) } /// - protected override void OnMouseDown(IMouse mouse, MouseButton button) + protected void OnMouseDown(IMouse mouse, MouseButton button) => _game.HandleMouseDown(mouse, button); /// @@ -280,8 +306,8 @@ protected void SetScene(Scene scene) } /// - protected override void Dispose(bool disposing) + protected void Dispose(bool disposing) { - base.Dispose(disposing); + //base.Dispose(disposing); } } diff --git a/Editor/Editor.cs b/Editor/Editor.cs index d59a403..5960dfc 100644 --- a/Editor/Editor.cs +++ b/Editor/Editor.cs @@ -6,44 +6,7 @@ namespace SharpEngine.Editor; -public class Editor : IGame +public class Editor : Game { - public ISettings CoreSettings { get; } = new EditorSettings(); - - public CameraView Camera { get; set; } - - public void HandleKeyboard(IKeyboard input, double deltaTime) - { - - } - public void HandleMouse(IMouse mouse) - { - - } - public void HandleMouseDown(IMouse mouse, MouseButton button) - { - - } - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) - { - - } - public void Initialize() - { - - } - - public void Update(double deltaTime, IInputContext input) - { - - } - - public void SaveScene(Scene scene) - { - - } - - public void LoadScene(string sceneFile) - { - } + } \ No newline at end of file diff --git a/Editor/EditorWindow.cs b/Editor/EditorWindow.cs index 576bde5..d1bab81 100644 --- a/Editor/EditorWindow.cs +++ b/Editor/EditorWindow.cs @@ -21,10 +21,9 @@ public class EditorWindow : Window /// /// Initializes a new instance of . /// - /// /// The scene to be initialized. /// The settings for the editor window. - public EditorWindow(IGame game, Scene scene, IViewSettings settings) : base(game, scene, settings.WindowOptions) + public EditorWindow(Scene scene, IViewSettings settings) : base(scene, settings.WindowOptions) { } diff --git a/Editor/Program.cs b/Editor/Program.cs index e01e009..437bb8b 100644 --- a/Editor/Program.cs +++ b/Editor/Program.cs @@ -15,9 +15,9 @@ public static class Program [STAThread] public static void Main(string[] args) { - var editor = new Editor(); + //var editor = new Editor(); var scene = args.Length > 0 ? Scene.LoadScene(args[0]) : new Scene(); - using var window = new EditorWindow(editor, scene, new DefaultViewSettings()); + using var window = new EditorWindow(scene, new DefaultViewSettings()); } } \ No newline at end of file diff --git a/Editor/SharpEngine.Editor.csproj b/Editor/SharpEngine.Editor.csproj index f735be1..9af225b 100644 --- a/Editor/SharpEngine.Editor.csproj +++ b/Editor/SharpEngine.Editor.csproj @@ -15,10 +15,11 @@ Early release, expect broken / missing features. en true + Debug;Release;LauncherNonWindows - + diff --git a/Examples/MinecraftClone/Game.cs b/Examples/MinecraftClone/Minecraft.cs similarity index 82% rename from Examples/MinecraftClone/Game.cs rename to Examples/MinecraftClone/Minecraft.cs index 80f3792..a66ee25 100644 --- a/Examples/MinecraftClone/Game.cs +++ b/Examples/MinecraftClone/Minecraft.cs @@ -4,7 +4,6 @@ using SharpEngine.Core.Scenes; using SharpEngine.Core.Entities; using SharpEngine.Core.Entities.Lights; -using SharpEngine.Core.Entities.Views; using Minecraft.Block; using Silk.NET.Input; @@ -14,47 +13,48 @@ namespace Minecraft; -public class Game : IGame -{ - // TODO: Add documentation to the code - - /// Gets or sets the player camera. - public CameraView Camera { get; set; } - - /// - public ISettings CoreSettings { get; } +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Justification = Those values are set in the Initialize method. +/// +/// Contains the logic for the Minecraft game. +/// +public class Minecraft : Game +{ private readonly Scene _scene; private SceneNode _lightsNode; private SceneNode _blocksNode; private Input _input; - private Inventory _inventory; + private readonly Inventory _inventory; - UIElement uiElem; + private readonly UIElement uiElem; /// - /// Initializes a new instance of the . + /// Initializes a new instance of the . /// - public Game(Scene scene, ISettings settings) + public Minecraft(Scene scene, ISettings settings) { _scene = scene; CoreSettings = settings; + + _inventory = new Inventory(); + // uiElem = new UIElement("uiElement"); } /// - public void Initialize() + public override void Initialize() { + base.Initialize(); + _input = new Input(Camera); - _inventory = new Inventory(); _inventory.Initialize(); _lightsNode = _scene.Root.AddChild("lights"); _blocksNode = _scene.Root.AddChild("blocks"); - uiElem = new UIElement("uiElement"); - _scene.UIElements.Add(uiElem); + // TODO: Fix UI renderer + // _scene.UIElements.Add(uiElem); InitializeWorld(); } @@ -93,7 +93,7 @@ private void InitializeChunks() { // TODO: Generate chunks when player moves - // TODO: Generate chunks using 3d perlin noise + // TODO: Generate chunks using 3d Perlin noise const int chunkSize = 16; const int numChunks = 3; @@ -127,16 +127,16 @@ private void GenerateChunk(int chunkSize, Vector3 chunkPos) } /// - public void Update(double deltaTime, IInputContext input) + public override void Update(double deltaTime, IInputContext input) { - uiElem.Transform.Rotation += 0.01f; + // uiElem.Transform.Rotation += 0.01f; _input.HandleKeyboard(input.Keyboards[0], (float)deltaTime); } // TODO: Input system to let users change change key bindings? /// - public void HandleKeyboard(IKeyboard input, double deltaTime) + public override void HandleKeyboard(IKeyboard input, double deltaTime) { for (int i = 0; i <= 9; i++) { @@ -164,10 +164,10 @@ private void Input_KeyUp(IKeyboard arg1, Key arg2, int arg3) } /// - public void HandleMouse(IMouse mouse) { } + public override void HandleMouse(IMouse mouse) { } /// - public void HandleMouseDown(IMouse mouse, MouseButton button) + public override void HandleMouseDown(IMouse mouse, MouseButton button) { if (button == MouseButton.Right) { @@ -187,13 +187,13 @@ public void HandleMouseDown(IMouse mouse, MouseButton button) if (button == MouseButton.Left) { - var destoryedBlockType = DestroyBlock(); - if (destoryedBlockType != BlockType.None) + var destroyedBlockType = DestroyBlock(); + if (destroyedBlockType != BlockType.None) { // TODO: The block should be added to the slot so that 0 is the last slot instead of 9. - // TODO: The first block destoryed doesn't seem to be added to the inventory. - Console.WriteLine($"Block destroyed: {destoryedBlockType}."); - _inventory.AddToolbarItem(destoryedBlockType); + // TODO: The first block destroyed doesn't seem to be added to the inventory. + Console.WriteLine($"Block destroyed: {destroyedBlockType}."); + _inventory.AddToolbarItem(destroyedBlockType); } } } @@ -244,7 +244,7 @@ public bool IsBlockInView(out GameObject? intersectingObject, out Vector3 hitPos } /// - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) + public override void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) { int slotIndex = _inventory.SelectedSlotIndex; diff --git a/Examples/MinecraftClone/Minecraft.csproj b/Examples/MinecraftClone/Minecraft.csproj index 97ff3b5..89889c9 100644 --- a/Examples/MinecraftClone/Minecraft.csproj +++ b/Examples/MinecraftClone/Minecraft.csproj @@ -3,10 +3,11 @@ Exe AnyCPU;x64;x86 + Debug;Release;LauncherNonWindows - + diff --git a/Examples/MinecraftClone/Program.cs b/Examples/MinecraftClone/Program.cs index 2ec0a7f..59e95fd 100644 --- a/Examples/MinecraftClone/Program.cs +++ b/Examples/MinecraftClone/Program.cs @@ -1,4 +1,5 @@ -using SharpEngine.Core.Scenes; +using SharpEngine.Core.Interfaces; +using SharpEngine.Core.Scenes; using Silk.NET.Maths; using Silk.NET.Windowing; @@ -12,7 +13,7 @@ public static class Program { private static void Main() { - Settings gameSettings = new() + DefaultSettings gameSettings = new() { UseWireFrame = false, WindowOptions = WindowOptions.Default with @@ -23,9 +24,8 @@ private static void Main() }; Scene scene = new Scene(); - Game game = new Game(scene, gameSettings); + Minecraft game = new Minecraft(scene, gameSettings); using var window = new SharpEngine.Core.Window(game, scene, gameSettings.WindowOptions); - window.Run(); } } diff --git a/Examples/MinecraftClone/Settings.cs b/Examples/MinecraftClone/Settings.cs deleted file mode 100644 index b5ea3f1..0000000 --- a/Examples/MinecraftClone/Settings.cs +++ /dev/null @@ -1,22 +0,0 @@ -using SharpEngine.Core.Interfaces; -using SharpEngine.Core.Renderers; - -using Silk.NET.Windowing; - -namespace Minecraft; - -/// -public class Settings : ISettings -{ - /// - public bool UseWireFrame { get; set; } - - /// - public bool PrintFrameRate { get; set; } - - /// - public RenderFlags RendererFlags { get; set; } = RenderFlags.All; - - /// - public WindowOptions WindowOptions { get; set; } -} diff --git a/Examples/Minimal/Minimal/Minimal.csproj b/Examples/Minimal/Minimal.csproj similarity index 66% rename from Examples/Minimal/Minimal/Minimal.csproj rename to Examples/Minimal/Minimal.csproj index e55defb..2821290 100644 --- a/Examples/Minimal/Minimal/Minimal.csproj +++ b/Examples/Minimal/Minimal.csproj @@ -5,10 +5,11 @@ net8.0 enable enable + Debug;Release;LauncherNonWindows - + diff --git a/Examples/Minimal/Minimal/Program.cs b/Examples/Minimal/Minimal/Program.cs deleted file mode 100644 index f056eb2..0000000 --- a/Examples/Minimal/Minimal/Program.cs +++ /dev/null @@ -1,52 +0,0 @@ -using SharpEngine.Core; -using SharpEngine.Core.Entities.Views; -using SharpEngine.Core.Enums; -using SharpEngine.Core.Interfaces; -using SharpEngine.Core.Renderers; -using SharpEngine.Core.Scenes; -using Silk.NET.Input; -using Silk.NET.Maths; -using Silk.NET.Windowing; - -namespace SharpEngine.Core -{ - // TODO: Minimal isn't really "minimal" - // TODO: Make this project work. - - public static class Program - { - public static void Main(string[] args) - { - var game = new Game(); - var window = new Window(game, new Scene(), new WindowOptions() with - { - Title = "SharpEngine minimal game", - Size = new Vector2D(800, 600) - }); - - window.Run(); - } - } - - public class Settings : ISettings - { - public WindowOptions WindowOptions { get; set; } = new WindowOptions(); - public bool UseWireFrame { get; set; } - public bool PrintFrameRate { get; set; } - public RenderFlags RendererFlags { get; set; } - } - - public class Game : IGame - { - public ISettings CoreSettings => new Settings(); - - public CameraView Camera { get; set; } - - public void HandleKeyboard(IKeyboard input, double deltaTime) { } - public void HandleMouse(IMouse mouse) { } - public void HandleMouseDown(IMouse mouse, MouseButton button) { } - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) { } - public void Initialize() { } - public void Update(double deltaTime, IInputContext input) { } - } -} diff --git a/Examples/Minimal/Program.cs b/Examples/Minimal/Program.cs new file mode 100644 index 0000000..d00aca3 --- /dev/null +++ b/Examples/Minimal/Program.cs @@ -0,0 +1,47 @@ +using SharpEngine.Core.Interfaces; +using SharpEngine.Core.Scenes; + +using Silk.NET.Maths; +using Silk.NET.Windowing; + +namespace Minimal; + +/// +/// Represents the entry point of the application. +/// +public static class Program +{ + /// + /// The entry point of the application. + /// + public static void Main(string[] _) + { + var game = new Minimal(new DefaultSettings() + { + UseWireFrame = false, + WindowOptions = WindowOptions.Default with + { + Size = new Vector2D(800, 600), + Title = "Minimal", + } + }); + + var scene = new Scene(); + using var window = new SharpEngine.Core.Window(game, scene, game.CoreSettings.WindowOptions); + } +} + +/// +/// Represents the game instance. +/// +public class Minimal : Game +{ + /// + /// Initializes a new instance of + /// + /// Provides configuration options for the instance. + public Minimal(ISettings settings) + { + CoreSettings = settings; + } +} diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj index 06a471b..dd4dabb 100644 --- a/Launcher/Launcher.csproj +++ b/Launcher/Launcher.csproj @@ -3,8 +3,11 @@ - net8.0-android;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);net8.0-windows10.0.19041.0 + + 10.0.19041.0 + + net8.0-android;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);net8.0-windows$(WindowsVersion) @@ -37,8 +40,10 @@ 14.0 24.0 10.0.17763.0 - 10.0.17763.0 + $(WindowsVersion) 6.5 + + Debug;Release;LauncherNonWindows diff --git a/SharpEngine.sln b/SharpEngine.sln new file mode 100644 index 0000000..f56a679 --- /dev/null +++ b/SharpEngine.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher", "Launcher\Launcher.csproj", "{3751B661-BE0E-9CD7-CA51-361A34450372}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minimal", "Examples\Minimal\Minimal.csproj", "{1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEngine.Editor", "Editor\SharpEngine.Editor.csproj", "{F72A74DC-54F9-0C76-2D40-32D2524683F8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEngine.Core.NET", "Core\SharpEngine.Core.NET.csproj", "{6B242114-B3AC-D598-A74D-D84B64702471}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minecraft", "Examples\MinecraftClone\Minecraft.csproj", "{E8A75C3B-1D82-8B30-C854-AD35FB123CDC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3751B661-BE0E-9CD7-CA51-361A34450372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Release|Any CPU.Build.0 = Release|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Release|Any CPU.Deploy.0 = Release|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Release|Any CPU.Build.0 = Release|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Release|Any CPU.Build.0 = Release|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Release|Any CPU.Build.0 = Release|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {78D50298-A91B-44A5-A78E-4858134662B2} + EndGlobalSection +EndGlobal diff --git a/SharpEngine.slnx b/SharpEngine.slnx new file mode 100644 index 0000000..4815b93 --- /dev/null +++ b/SharpEngine.slnx @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + From cdcbd82702ebdc9223b620a4a2653d19f7bc5c14 Mon Sep 17 00:00:00 2001 From: Antti Veikkolainen Date: Sat, 15 Mar 2025 16:13:13 +0200 Subject: [PATCH 4/4] #36 Fix unsafe OpenGL calls --- .../Entities/Properties/Meshes/MeshService.cs | 9 +------- Core/Entities/UIElement.cs | 22 +++++++------------ Core/SharpEngine.Core.csproj | 1 - 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/Core/Entities/Properties/Meshes/MeshService.cs b/Core/Entities/Properties/Meshes/MeshService.cs index 4245269..67e5300 100644 --- a/Core/Entities/Properties/Meshes/MeshService.cs +++ b/Core/Entities/Properties/Meshes/MeshService.cs @@ -30,14 +30,7 @@ public Mesh LoadMesh(string identifier, Mesh mesh) var vertexBufferObject = Window.GL.GenBuffer(); Window.GL.BindBuffer(GLEnum.ArrayBuffer, vertexBufferObject); - - unsafe - { - fixed (float* meshDataPtr = meshData) - { - Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)meshData.Length * sizeof(float), meshDataPtr, GLEnum.StaticDraw); - } - } + Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)meshData.Length * sizeof(float), meshData, GLEnum.StaticDraw); Meshes.Add(identifier, mesh); return mesh; diff --git a/Core/Entities/UIElement.cs b/Core/Entities/UIElement.cs index b0d10a0..30fa3a0 100644 --- a/Core/Entities/UIElement.cs +++ b/Core/Entities/UIElement.cs @@ -63,21 +63,15 @@ public void Initialize() InitializeBuffers(); } - private unsafe void InitializeBuffers() + private void InitializeBuffers() { - fixed (float* vertexDataPtr = _vertices) - { - var vertexBufferObject = Window.GL.GenBuffer(); - Window.GL.BindBuffer(GLEnum.ArrayBuffer, vertexBufferObject); - Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)_vertices.Length * sizeof(float), vertexDataPtr, GLEnum.StaticDraw); - } - - fixed (uint* indicieDataPtr = _indices) - { - var elementBufferObject = Window.GL.GenBuffer(); - Window.GL.BindBuffer(GLEnum.ElementArrayBuffer, elementBufferObject); - Window.GL.BufferData(GLEnum.ElementArrayBuffer, (uint)_indices.Length * sizeof(uint), indicieDataPtr, GLEnum.StaticDraw); - } + var vertexBufferObject = Window.GL.GenBuffer(); + Window.GL.BindBuffer(GLEnum.ArrayBuffer, vertexBufferObject); + Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)_vertices.Length * sizeof(float), _vertices, GLEnum.StaticDraw); + + var elementBufferObject = Window.GL.GenBuffer(); + Window.GL.BindBuffer(GLEnum.ElementArrayBuffer, elementBufferObject); + Window.GL.BufferData(GLEnum.ElementArrayBuffer, (uint)_indices.Length * sizeof(uint), _indices, GLEnum.StaticDraw); } /// diff --git a/Core/SharpEngine.Core.csproj b/Core/SharpEngine.Core.csproj index d61202d..44c552b 100644 --- a/Core/SharpEngine.Core.csproj +++ b/Core/SharpEngine.Core.csproj @@ -7,7 +7,6 @@ Required by some low level OpenGL functions. We should see if there is a better way to approach where ever these unsafe contexts are being used. --> - true AnyCPU;x64;x86 $(GenerateNuGetPackages) https://github.com/Av3boy/SharpEngine