hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2025-08-16 22:00:16 +0200
committerhachem <im@hachem.wtf>2025-08-16 22:00:16 +0200
commitdf60b84f65fd5bfd80af2287463714fd0fa4a1a3 (patch)
tree466b027d57d9250684ae66ac6c7ede159130f170
parentf7334b82e8ab8b4a36847d7f9dd2a56f29dd0cbf (diff)
feat: preferences and saving
-rw-r--r--.gitmodules3
-rw-r--r--config/settings.toml17
m---------ext/toml110
-rw-r--r--premake5.lua2
-rw-r--r--src/core/application.cpp9
-rw-r--r--src/core/settings_manager.cpp178
-rw-r--r--src/core/settings_manager.h70
-rw-r--r--src/states/config_state.cpp54
-rw-r--r--src/states/simulation_state.cpp56
9 files changed, 382 insertions, 7 deletions
diff --git a/.gitmodules b/.gitmodules
index bd955cb..27a424d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -8,3 +8,6 @@
path = ext/imgui
url = https://github.com/ocornut/imgui.git
branch = docking
+[submodule "ext/toml11"]
+ path = ext/toml11
+ url = https://github.com/ToruNiina/toml11
diff --git a/config/settings.toml b/config/settings.toml
new file mode 100644
index 0000000..088d789
--- /dev/null
+++ b/config/settings.toml
@@ -0,0 +1,17 @@
+[simulation]
+max_steps_static = 15000
+max_steps_moving = 30000
+compute_height = 256
+target_fps = 60
+early_exit_distance = 5e+12
+gravity_enabled = true
+
+[graphics]
+render_api = "OpenGL"
+vsync_enabled = true
+enable_anti_aliasing = true
+show_fps = true
+show_performance_metrics = true
+show_debug_info = false
+selected_theme = "Dark"
+
diff --git a/ext/toml11 b/ext/toml11
new file mode 160000
+Subproject 2a18a89008d3daac6d8f9db03ddd582173032c7
diff --git a/premake5.lua b/premake5.lua
index 389bdb5..25db23d 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -29,6 +29,7 @@ IncludeDir["glfw"] = "ext/glfw/include"
IncludeDir["glad"] = "ext/glad/include"
IncludeDir["imgui"] = "ext/imgui"
IncludeDir["imgui_backends"] = "ext/imgui/backends"
+IncludeDir["toml11"] = "ext/toml11/include"
group "Dependencies"
@@ -284,6 +285,7 @@ project "Donut"
"%{IncludeDir.glad}",
"%{IncludeDir.imgui}",
"%{IncludeDir.imgui_backends}",
+ "%{IncludeDir.toml11}",
}
links
diff --git a/src/core/application.cpp b/src/core/application.cpp
index 30194de..9d2278e 100644
--- a/src/core/application.cpp
+++ b/src/core/application.cpp
@@ -1,6 +1,7 @@
#include "application.h"
#include "rendering/renderer.h"
+#include "settings_manager.h"
#include "states/simulation_state.h"
#include "states/config_state.h"
@@ -98,7 +99,12 @@ namespace Donut
{
Logger::init();
- RendererAPI::set_api(RendererAPI::API::OpenGL);
+ SettingsManager::initialize();
+
+ const auto& settings = SettingsManager::get_settings_const();
+ RendererAPI::API api = (settings.graphics.render_api == "Vulkan") ? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL;
+ RendererAPI::set_api(api);
+
Renderer::init();
m_window->init_im_gui();
@@ -117,6 +123,7 @@ namespace Donut
m_state_manager->shutdown();
Renderer::shutdown();
+ SettingsManager::shutdown();
Logger::shutdown();
}
diff --git a/src/core/settings_manager.cpp b/src/core/settings_manager.cpp
new file mode 100644
index 0000000..eb68794
--- /dev/null
+++ b/src/core/settings_manager.cpp
@@ -0,0 +1,178 @@
+#include "settings_manager.h"
+#include "log.h"
+#include "theme_manager.h"
+#include "rendering/renderer.h"
+
+#include <fstream>
+#include <filesystem>
+#include <algorithm>
+
+namespace Donut
+{
+ Settings SettingsManager::s_settings;
+ bool SettingsManager::s_initialized = false;
+
+ void SettingsManager::initialize()
+ {
+ if (s_initialized)
+ return;
+
+ load_settings();
+ s_initialized = true;
+ DONUT_INFO("Settings Manager initialized");
+ }
+
+ void SettingsManager::shutdown()
+ {
+ if (!s_initialized)
+ return;
+
+ save_settings();
+ s_initialized = false;
+ DONUT_INFO("Settings Manager shutdown");
+ }
+
+ void SettingsManager::load_settings()
+ {
+ std::string file_path = get_settings_file_path();
+
+ try
+ {
+ if (std::filesystem::exists(file_path))
+ {
+ auto config = toml::parse(file_path);
+
+ if (config.contains("simulation"))
+ {
+ auto sim = config["simulation"];
+ s_settings.simulation.target_fps = toml::find_or(sim, "target_fps", 60);
+ s_settings.simulation.compute_height = toml::find_or(sim, "compute_height", 512);
+ s_settings.simulation.max_steps_moving = toml::find_or(sim, "max_steps_moving", 30000);
+ s_settings.simulation.max_steps_static = toml::find_or(sim, "max_steps_static", 15000);
+ s_settings.simulation.early_exit_distance = toml::find_or(sim, "early_exit_distance", 5e12f);
+ s_settings.simulation.gravity_enabled = toml::find_or(sim, "gravity_enabled", true);
+
+ s_settings.simulation.target_fps = std::max(30, std::min(120, s_settings.simulation.target_fps));
+ s_settings.simulation.compute_height = std::max(64, std::min(2048, s_settings.simulation.compute_height));
+ s_settings.simulation.max_steps_moving = std::max(1000, std::min(60000, s_settings.simulation.max_steps_moving));
+ s_settings.simulation.max_steps_static = std::max(1000, std::min(30000, s_settings.simulation.max_steps_static));
+ s_settings.simulation.early_exit_distance = std::max(1e11f, std::min(1e13f, s_settings.simulation.early_exit_distance));
+ }
+
+ if (config.contains("graphics"))
+ {
+ auto gfx = config["graphics"];
+ s_settings.graphics.render_api = toml::find_or(gfx, "render_api", std::string("OpenGL"));
+ s_settings.graphics.v_sync_enabled = toml::find_or(gfx, "vsync_enabled", true);
+ s_settings.graphics.show_fps = toml::find_or(gfx, "show_fps", true);
+ s_settings.graphics.show_performance_metrics = toml::find_or(gfx, "show_performance_metrics", true);
+ s_settings.graphics.show_debug_info = toml::find_or(gfx, "show_debug_info", false);
+ s_settings.graphics.enable_anti_aliasing = toml::find_or(gfx, "enable_anti_aliasing", true);
+ s_settings.graphics.selected_theme = toml::find_or(gfx, "selected_theme", std::string("Dark"));
+
+ if (s_settings.graphics.render_api != "OpenGL" &&
+ s_settings.graphics.render_api != "Vulkan")
+ s_settings.graphics.render_api = "OpenGL";
+ if (s_settings.graphics.selected_theme != "Dark" &&
+ s_settings.graphics.selected_theme != "Light" &&
+ s_settings.graphics.selected_theme != "Blue")
+ s_settings.graphics.selected_theme = "Dark";
+ }
+
+ DONUT_INFO("Settings loaded from {}", file_path);
+ }
+ else
+ {
+ load_default_settings();
+ save_settings();
+ DONUT_INFO("No settings file found, created default settings");
+ }
+ }
+ catch (const std::exception& e)
+ {
+ DONUT_ERROR("Failed to load settings: {}", e.what());
+ load_default_settings();
+ }
+ }
+
+ void SettingsManager::save_settings()
+ {
+ std::string file_path = get_settings_file_path();
+
+ try
+ {
+ std::filesystem::path path(file_path);
+ std::filesystem::create_directories(path.parent_path());
+
+ toml::value simulation = toml::table
+ {
+ {"target_fps", s_settings.simulation.target_fps },
+ {"compute_height", s_settings.simulation.compute_height },
+ {"max_steps_moving", s_settings.simulation.max_steps_moving },
+ {"max_steps_static", s_settings.simulation.max_steps_static },
+ {"early_exit_distance", s_settings.simulation.early_exit_distance},
+ {"gravity_enabled", s_settings.simulation.gravity_enabled }
+ };
+
+ toml::value graphics = toml::table
+ {
+ {"render_api", s_settings.graphics.render_api },
+ {"vsync_enabled", s_settings.graphics.v_sync_enabled },
+ {"show_fps", s_settings.graphics.show_fps },
+ {"show_performance_metrics", s_settings.graphics.show_performance_metrics},
+ {"show_debug_info", s_settings.graphics.show_debug_info },
+ {"enable_anti_aliasing", s_settings.graphics.enable_anti_aliasing },
+ {"selected_theme", s_settings.graphics.selected_theme }
+ };
+
+ toml::value config = toml::table
+ {
+ {"simulation", simulation},
+ {"graphics", graphics}
+ };
+
+ std::ofstream file(file_path);
+ file << config;
+ file.close();
+
+ DONUT_INFO("Settings saved to {}", file_path);
+ }
+ catch (const std::exception& e)
+ DONUT_ERROR("Failed to save settings: {}", e.what());
+ }
+
+ void SettingsManager::set_simulation_settings(const SimulationSettings& settings)
+ {
+ s_settings.simulation = settings;
+ save_settings();
+ }
+
+ void SettingsManager::set_graphics_settings(const GraphicsSettings& settings)
+ {
+ s_settings.graphics = settings;
+ save_settings();
+ }
+
+ std::string SettingsManager::get_settings_file_path()
+ {
+ return "config/settings.toml";
+ }
+
+ void SettingsManager::load_default_settings()
+ {
+ s_settings.simulation.target_fps = 60;
+ s_settings.simulation.compute_height = 512;
+ s_settings.simulation.max_steps_moving = 30000;
+ s_settings.simulation.max_steps_static = 15000;
+ s_settings.simulation.early_exit_distance = 5e12f;
+ s_settings.simulation.gravity_enabled = true;
+
+ s_settings.graphics.render_api = "OpenGL";
+ s_settings.graphics.v_sync_enabled = true;
+ s_settings.graphics.show_fps = true;
+ s_settings.graphics.show_performance_metrics = true;
+ s_settings.graphics.show_debug_info = false;
+ s_settings.graphics.enable_anti_aliasing = true;
+ s_settings.graphics.selected_theme = "Dark";
+ }
+}
diff --git a/src/core/settings_manager.h b/src/core/settings_manager.h
new file mode 100644
index 0000000..1a36026
--- /dev/null
+++ b/src/core/settings_manager.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <string>
+#include <toml.hpp>
+
+namespace Donut
+{
+ struct SimulationSettings
+ {
+ int target_fps = 60;
+ int compute_height = 512;
+ int max_steps_moving = 30000;
+ int max_steps_static = 15000;
+ float early_exit_distance = 5e12f;
+ bool gravity_enabled = true;
+ };
+
+ struct GraphicsSettings
+ {
+ std::string render_api = "OpenGL";
+ bool v_sync_enabled = true;
+ bool show_fps = true;
+ bool show_performance_metrics = true;
+ bool show_debug_info = false;
+ bool enable_anti_aliasing = true;
+ std::string selected_theme = "Dark";
+ };
+
+ struct Settings
+ {
+ SimulationSettings simulation;
+ GraphicsSettings graphics;
+ };
+
+ class SettingsManager
+ {
+ public:
+ static void initialize();
+ static void shutdown();
+
+ static void load_settings();
+ static void save_settings();
+
+ static Settings& get_settings() { return s_settings; }
+ static const Settings& get_settings_const() { return s_settings; }
+
+ static void set_simulation_settings(const SimulationSettings& settings);
+ static void set_graphics_settings(const GraphicsSettings& settings);
+
+ static int get_target_fps() { return s_settings.simulation.target_fps; }
+ static int get_compute_height() { return s_settings.simulation.compute_height; }
+ static int get_max_steps_moving() { return s_settings.simulation.max_steps_moving; }
+ static int get_max_steps_static() { return s_settings.simulation.max_steps_static; }
+ static float get_early_exit_distance() { return s_settings.simulation.early_exit_distance; }
+ static bool get_gravity_enabled() { return s_settings.simulation.gravity_enabled; }
+ static std::string get_render_api() { return s_settings.graphics.render_api; }
+ static bool get_v_sync_enabled() { return s_settings.graphics.v_sync_enabled; }
+ static bool get_show_fps() { return s_settings.graphics.show_fps; }
+ static bool get_show_performance_metrics() { return s_settings.graphics.show_performance_metrics; }
+ static bool get_show_debug_info() { return s_settings.graphics.show_debug_info; }
+ static bool get_enable_anti_aliasing() { return s_settings.graphics.enable_anti_aliasing; }
+ static std::string get_selected_theme() { return s_settings.graphics.selected_theme; }
+ private:
+ static std::string get_settings_file_path();
+ static void load_default_settings();
+ private:
+ static Settings s_settings;
+ static bool s_initialized;
+ };
+}
diff --git a/src/states/config_state.cpp b/src/states/config_state.cpp
index 8cf24ef..1f1ef16 100644
--- a/src/states/config_state.cpp
+++ b/src/states/config_state.cpp
@@ -2,6 +2,7 @@
#include "rendering/renderer.h"
#include "core/application.h"
#include "core/theme_manager.h"
+#include "core/settings_manager.h"
#include <imgui.h>
#include <imgui_internal.h>
@@ -13,8 +14,22 @@ namespace Donut
{
DONUT_INFO("Entering Config State");
- m_selected_api = Renderer::get_api();
- m_selected_theme = static_cast<int>(ThemeManager::get_current_theme());
+ const auto& settings = SettingsManager::get_settings_const();
+
+ m_selected_api = (settings.graphics.render_api == "Vulkan") ? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL;
+ m_selected_theme = (settings.graphics.selected_theme == "Light") ? 1 :
+ (settings.graphics.selected_theme == "Blue") ? 2 : 0;
+ m_target_fps = settings.simulation.target_fps;
+ m_compute_height = settings.simulation.compute_height;
+ m_max_steps_moving = settings.simulation.max_steps_moving;
+ m_max_steps_static = settings.simulation.max_steps_static;
+ m_early_exit_distance = settings.simulation.early_exit_distance;
+ m_gravity_enabled = settings.simulation.gravity_enabled;
+ m_v_sync_enabled = settings.graphics.v_sync_enabled;
+ m_show_fps = settings.graphics.show_fps;
+ m_show_performance_metrics = settings.graphics.show_performance_metrics;
+ m_show_debug_info = settings.graphics.show_debug_info;
+ m_enable_anti_aliasing = settings.graphics.enable_anti_aliasing;
}
void ConfigState::on_exit()
@@ -200,7 +215,7 @@ namespace Donut
ImGui::Separator();
ImGui::Spacing();
- float button_width = (ImGui::GetWindowWidth() - 80) / 4.0f;
+ float button_width = (ImGui::GetWindowWidth() - 100) / 5.0f;
if (ImGui::Button("Start Simulation", ImVec2(button_width, 35)))
{
@@ -217,6 +232,13 @@ namespace Donut
apply_settings();
ImGui::SameLine();
+ if (ImGui::Button("Save Settings", ImVec2(button_width, 35)))
+ {
+ apply_settings();
+ DONUT_INFO("Settings saved manually");
+ }
+
+ ImGui::SameLine();
if (ImGui::Button("Exit", ImVec2(button_width, 35)))
Application::get().close();
@@ -237,8 +259,29 @@ namespace Donut
void ConfigState::apply_settings()
{
+ SimulationSettings sim_settings;
+ sim_settings.target_fps = m_target_fps;
+ sim_settings.compute_height = m_compute_height;
+ sim_settings.max_steps_moving = m_max_steps_moving;
+ sim_settings.max_steps_static = m_max_steps_static;
+ sim_settings.early_exit_distance = m_early_exit_distance;
+ sim_settings.gravity_enabled = m_gravity_enabled;
+
+ GraphicsSettings gfx_settings;
+ gfx_settings.render_api = (m_selected_api == RendererAPI::API::Vulkan) ? "Vulkan" : "OpenGL";
+ gfx_settings.v_sync_enabled = m_v_sync_enabled;
+ gfx_settings.show_fps = m_show_fps;
+ gfx_settings.show_performance_metrics = m_show_performance_metrics;
+ gfx_settings.show_debug_info = m_show_debug_info;
+ gfx_settings.enable_anti_aliasing = m_enable_anti_aliasing;
+ gfx_settings.selected_theme = (m_selected_theme == 1) ? "Light" :
+ (m_selected_theme == 2) ? "Blue" : "Dark";
+
+ SettingsManager::set_simulation_settings(sim_settings);
+ SettingsManager::set_graphics_settings(gfx_settings);
+
RendererAPI::set_api(m_selected_api);
- DONUT_INFO("Settings applied");
+ DONUT_INFO("Settings applied and saved");
}
void ConfigState::reset_to_defaults()
@@ -257,6 +300,7 @@ namespace Donut
m_enable_anti_aliasing = true;
m_selected_theme = 0;
- DONUT_INFO("Settings reset to defaults");
+ apply_settings();
+ DONUT_INFO("Settings reset to defaults and saved");
}
};
diff --git a/src/states/simulation_state.cpp b/src/states/simulation_state.cpp
index ff7f658..e71d498 100644
--- a/src/states/simulation_state.cpp
+++ b/src/states/simulation_state.cpp
@@ -3,6 +3,7 @@
#include "core/application.h"
#include "core/window.h"
#include "core/event.h"
+#include "core/settings_manager.h"
#include <imgui.h>
#include <GLFW/glfw3.h>
@@ -12,6 +13,17 @@ namespace Donut
void SimulationState::on_enter()
{
DONUT_INFO("Entering Simulation State");
+
+ const auto& settings = SettingsManager::get_settings_const();
+
+ m_engine.set_target_fps(settings.simulation.target_fps);
+ m_engine.set_compute_height(settings.simulation.compute_height);
+ m_engine.set_max_steps_moving(settings.simulation.max_steps_moving);
+ m_engine.set_max_steps_static(settings.simulation.max_steps_static);
+ m_engine.set_early_exit_distance(settings.simulation.early_exit_distance);
+ m_engine.get_gravity() = settings.simulation.gravity_enabled;
+
+ m_engine.update_compute_dimensions();
m_initialized = true;
}
@@ -92,8 +104,22 @@ namespace Donut
ImGui::Begin("Simulation Controls", nullptr, ImGuiWindowFlags_NoCollapse);
ImGui::TextColored(ImVec4(0.8f, 0.8f, 1.0f, 1.0f), "Black Hole Simulation");
+ ImGui::SameLine();
if (ImGui::Button("Back to Config"))
Application::get().get_state_manager().switch_to_state("Config");
+ ImGui::SameLine();
+ if (ImGui::Button("Save Settings"))
+ {
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.target_fps = m_engine.get_target_fps();
+ settings.compute_height = m_engine.get_compute_height();
+ settings.max_steps_moving = m_engine.get_max_steps_moving();
+ settings.max_steps_static = m_engine.get_max_steps_static();
+ settings.early_exit_distance = m_engine.get_early_exit_distance();
+ settings.gravity_enabled = m_engine.get_gravity();
+ SettingsManager::set_simulation_settings(settings);
+ DONUT_INFO("Simulation settings saved manually");
+ }
ImGui::Separator();
@@ -106,7 +132,12 @@ namespace Donut
int target_fps = m_engine.get_target_fps();
if (ImGui::SliderInt("Target FPS", &target_fps, 30, 120))
+ {
m_engine.set_target_fps(target_fps);
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.target_fps = target_fps;
+ SettingsManager::set_simulation_settings(settings);
+ }
ImGui::Spacing();
@@ -122,6 +153,9 @@ namespace Donut
{
m_engine.set_compute_height(compute_height);
m_engine.update_compute_dimensions();
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.compute_height = compute_height;
+ SettingsManager::set_simulation_settings(settings);
}
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Compute Width: %d (auto-calculated)", m_engine.get_compute_width());
@@ -133,13 +167,28 @@ namespace Donut
int max_steps_moving = m_engine.get_max_steps_moving();
if (ImGui::SliderInt("Max Steps (Moving)", &max_steps_moving, 1000, 60000))
+ {
m_engine.set_max_steps_moving(max_steps_moving);
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.max_steps_moving = max_steps_moving;
+ SettingsManager::set_simulation_settings(settings);
+ }
int max_steps_static = m_engine.get_max_steps_static();
if (ImGui::SliderInt("Max Steps (Static)", &max_steps_static, 1000, 30000))
+ {
m_engine.set_max_steps_static(max_steps_static);
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.max_steps_static = max_steps_static;
+ SettingsManager::set_simulation_settings(settings);
+ }
float early_exit_distance = m_engine.get_early_exit_distance();
if (ImGui::SliderFloat("Early Exit Distance", &early_exit_distance, 1e11f, 1e13f, "%.2e"))
+ {
m_engine.set_early_exit_distance(early_exit_distance);
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.early_exit_distance = early_exit_distance;
+ SettingsManager::set_simulation_settings(settings);
+ }
ImGui::Spacing();
@@ -147,7 +196,12 @@ namespace Donut
ImGui::Separator();
bool& gravity = m_engine.get_gravity();
- ImGui::Checkbox("Gravity Enabled", &gravity);
+ if (ImGui::Checkbox("Gravity Enabled", &gravity))
+ {
+ SimulationSettings settings = SettingsManager::get_settings_const().simulation;
+ settings.gravity_enabled = gravity;
+ SettingsManager::set_simulation_settings(settings);
+ }
ImGui::Spacing();