From a288d0961f93933e49f7f27747e5e2446d134b6b Mon Sep 17 00:00:00 2001 From: Timerix Date: Wed, 18 Jun 2025 16:06:54 +0500 Subject: [PATCH] split engine code into modules --- src/Engine.cpp | 152 +++++++++--------- src/Engine.hpp | 67 ++++---- src/common/function_shared_ptr.hpp | 2 - src/gui/ErrorWindow.cpp | 11 -- src/gui/gui.hpp | 11 -- src/main.cpp | 71 +++++--- .../MainWindowSDL2.cpp} | 51 ++++-- .../MainWindowSDL2.hpp} | 23 +-- src/modules/MonoGameObjectSystem.cpp | 35 ++++ src/modules/MonoGameObjectSystem.hpp | 30 ++++ 10 files changed, 273 insertions(+), 180 deletions(-) delete mode 100644 src/gui/ErrorWindow.cpp delete mode 100644 src/gui/gui.hpp rename src/{gui/MainWindow.cpp => modules/MainWindowSDL2.cpp} (84%) rename src/{gui/MainWindow.hpp => modules/MainWindowSDL2.hpp} (67%) create mode 100644 src/modules/MonoGameObjectSystem.cpp create mode 100644 src/modules/MonoGameObjectSystem.hpp diff --git a/src/Engine.cpp b/src/Engine.cpp index 01dc70e..322287b 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -1,23 +1,27 @@ #include "Engine.hpp" -#include "gui/gui.hpp" +#include "common/UsefulException.hpp" +#include "common/ougge_format.hpp" +#include "common/time.hpp" +#include +#include namespace ougge { -Engine::Engine(u32 game_object_pool_size) - : gameObjectPool(game_object_pool_size), textures(&resourceManager) +EngineModule::EngineModule(Engine& engine, const std::string& name) + : engine(engine), name(name) +{} + +void EngineModule::beginFrame() {} +void EngineModule::endFrame() {} + + + +void Engine::addModule(EngineModule* m){ + modules.push_back(m); +} + +void Engine::startLoop() { - engineManagedAssembly = mono.loadAssembly("Ougge.dll"); - gameObjectClass = engineManagedAssembly->getClass("Ougge", "GameObject"); - gameObjectCtor = Mono::Method(gameObjectClass, ".ctor"); - gameObjectInvokeUpdate = Mono::Method(gameObjectClass, "InvokeUpdate"); - gameObjectTryCreateComponent = Mono::Method(gameObjectClass, "TryCreateComponent_internal"); -} - -void Engine::openMainWindow(const std::string& window_title){ - mainWindow.open(window_title, resourceManager); -} - -void Engine::startLoop(){ if(loop_running) throw UsefulException("loop is running already"); @@ -25,87 +29,83 @@ void Engine::startLoop(){ loop_running=true; // main loop while(loop_running){ - mainWindow.pollEvents(&loop_running); - if(!loop_running) - break; - nsec_t update_time_ns = getMonotonicTimeNsec(); if(update_time_ns < prev_update_time_ns) throw UsefulException("monotonic clock returned unexpected value"); f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9; prev_update_time_ns = update_time_ns; - - tryDrawFrame(delta_time_s); + deltaTime = delta_time_s; + + tryDrawFrame(); nsec_t after_update_time_ns = getMonotonicTimeNsec(); - nsec_t frame_delay_ns = (nsec_t)1e9 / mainWindow.fps_max - (after_update_time_ns - update_time_ns); + nsec_t frame_delay_ns = (nsec_t)1e9 / fps_max - (after_update_time_ns - update_time_ns); if(frame_delay_ns > 0){ SDL_Delay(frame_delay_ns / 1e6); } } - - mainWindow.close(); } void Engine::stopLoop(){ loop_running = false; } -void Engine::tryDrawFrame(f64 deltaTime){ - static std::string error_message; - static bool draw_error_window = false; - try { - mainWindow.beginFrame(); - - if(draw_error_window) - gui::drawErrorWindow(error_message, &draw_error_window); - else { - updateGameObjects(deltaTime); - if(!updateCallback.isNull()) - updateCallback(deltaTime); +void Engine::handleModuleError(EngineModule* module, const char* type, const char* method, const char* error){ + std::string error_message = ougge_format( + "Catched %s at %s.%s(): %s", + type, module->name.c_str(), method, error); + std::cerr<beginFrame(); + } + catch(const std::exception& e){ + handleModuleError(module, "exception", "beginFrame", e.what()); + } + catch(const char* cstr){ + handleModuleError(module, "error message (const char*)", "beginFrame", cstr); + } + catch(const std::string& str){ + handleModuleError(module, "error message (std::string)", "beginFrame", str.c_str()); + } + catch(...){ + handleModuleError(module, "unknown", "beginFrame", "unknown"); } - mainWindow.endFrame(); } - catch(const std::exception& e){ - error_message = "Catched exception: " + std::string(e.what()); - draw_error_window = true; - std::cerr<endFrame(); + } + catch(const std::exception& e){ + handleModuleError(module, "exception", "endFrame", e.what()); + } + catch(const char* cstr){ + handleModuleError(module, "error message (const char*)", "endFrame", cstr); + } + catch(const std::string& str){ + handleModuleError(module, "error message (std::string)", "endFrame", str.c_str()); + } + catch(...){ + handleModuleError(module, "unknown", "endFrame", "unknown"); + } } } - -void Engine::updateGameObjects(f64 deltaTime){ - for(auto pair : gameObjectPool){ - gameObjectInvokeUpdate(pair.second.getObjectHandle().getObject(), deltaTime); - } -} - -game::GameObject& Engine::createGameObject(){ - auto pair = gameObjectPool.emplace(game::GameObject(mono.createObject(gameObjectClass))); - game::GameObject& obj = pair.second; - gameObjectCtor(obj.getObjectHandle().getObject(), ++obj_id, pair.first); - return obj; -} - -bool Engine::tryCreateComponent(game::GameObject& obj, const std::string& componentClassName){ - Mono::String componentClassNameManaged = mono.createString(componentClassName); - Mono::Bool created = gameObjectTryCreateComponent(obj.getObjectHandle().getObject(), componentClassNameManaged); - return created.wide_bool; -} - } diff --git a/src/Engine.hpp b/src/Engine.hpp index 9632235..ac1f07c 100644 --- a/src/Engine.hpp +++ b/src/Engine.hpp @@ -1,49 +1,56 @@ #pragma once -#include "common/function_shared_ptr.hpp" -#include "mono/mono.hpp" -#include "game/GameObjectPool.hpp" -#include "gui/MainWindow.hpp" -#include "resources/textures.hpp" +#include +#include "common/std.hpp" +#include "common/UsefulException.hpp" +#include "common/ougge_format.hpp" namespace ougge { -using UpdateFunc_t = function_shared_ptr; +class Engine; + +class EngineModule { +public: + EngineModule() = delete; + EngineModule(const EngineModule&) = delete; + EngineModule& operator=(const EngineModule&) = delete; + EngineModule(EngineModule&&) = delete; + EngineModule& operator=(EngineModule&&) = delete; + + Engine& engine; + const std::string name; + + virtual void beginFrame(); + virtual void endFrame(); + +protected: + EngineModule(Engine& engine, const std::string& name); +}; + class Engine { + std::vector modules; bool loop_running = false; - game::GameObjectPool gameObjectPool; - u64 obj_id = 0; - MonoClass* gameObjectClass; - Mono::Method gameObjectCtor; - Mono::Method gameObjectInvokeUpdate; - Mono::Method gameObjectTryCreateComponent; - public: - gui::MainWindow mainWindow; - UpdateFunc_t updateCallback; + u32 fps_max = 60; + f64 deltaTime; + std::vector error_messages; + + Engine() = default; + Engine(const Engine&) = delete; + Engine& operator=(const Engine&) = delete; + Engine(Engine&&) = delete; + Engine& operator=(Engine&&) = delete; - Mono::RuntimeJIT mono; - std::shared_ptr engineManagedAssembly; + void addModule(EngineModule* m); - resources::ResourceManager resourceManager; - resources::CacheStorage textures; - - Engine(u32 game_object_pool_size = 64*1024); - - void openMainWindow(const std::string& window_title); // start game loop on the current thread void startLoop(); void stopLoop(); - game::GameObject& createGameObject(); - bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName); - - private: - void tryDrawFrame(f64 deltaTime); - void updateGameObjects(f64 deltaTime); + void tryDrawFrame(); + void handleModuleError(EngineModule *module, const char *type, const char *method, const char *error); }; - } diff --git a/src/common/function_shared_ptr.hpp b/src/common/function_shared_ptr.hpp index ff7de84..ffd7159 100644 --- a/src/common/function_shared_ptr.hpp +++ b/src/common/function_shared_ptr.hpp @@ -30,8 +30,6 @@ public: bool isNull() const { return func_ptr == nullptr; } - //TODO: make inline - // ReturnT is not void template std::enable_if_t::value, RT> operator()(ArgTypes... args){ diff --git a/src/gui/ErrorWindow.cpp b/src/gui/ErrorWindow.cpp deleted file mode 100644 index 597bbc8..0000000 --- a/src/gui/ErrorWindow.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "gui.hpp" - -namespace ougge::gui { - -void drawErrorWindow(const std::string& msg, bool* draw_error_window){ - ImGui::Begin("ERROR", draw_error_window); - ImGui::Text("%s", msg.c_str()); - ImGui::End(); -} - -} diff --git a/src/gui/gui.hpp b/src/gui/gui.hpp deleted file mode 100644 index d626910..0000000 --- a/src/gui/gui.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include -#include "gui_exceptions.hpp" - -namespace ougge::gui { - -void drawErrorWindow(const std::string& msg, bool* draw_error_window); - -} diff --git a/src/main.cpp b/src/main.cpp index c995b9d..f2e6ab8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,46 +1,67 @@ #define SDL_MAIN_HANDLED #include #include -#include "common/UsefulException.hpp" -#include "common/ougge_format.hpp" #include "Engine.hpp" -#include "gui/gui.hpp" +#include "modules/MainWindowSDL2.hpp" +#include "modules/MonoGameObjectSystem.hpp" +#include "resources/textures.hpp" using namespace ougge; -void drawTutel(Engine& engine){ - resources::Texture* tutel = engine.textures.tryGetOrCreate("tutel.png", engine.mainWindow.sdl_renderer); - if(tutel == nullptr){ - throw new UsefulException("couldn't find resource 'tutel.png'"); +class TutelModule : public EngineModule { + resources::CacheStorage textures; + modules::MainWindowSDL2& mainWindow; +public: + TutelModule(Engine& engine, resources::ResourceManager& resourceManager, modules::MainWindowSDL2& mainWindow) : + EngineModule(engine, nameof(TutelModule)), + textures(&resourceManager), + mainWindow(mainWindow) + { } - resources::SDL_RenderCopyExF_Params params; - params.target_section = SDL_FRectConstruct(100, 100, 400, 400); - static i32 si = 1; - params.rotation_angle = M_PI_4 * (si++ / engine.mainWindow.fps_max); - tutel->render(params); -} + + virtual void beginFrame(){ + resources::Texture* tutel = textures.tryGetOrCreate("tutel.png", mainWindow.sdl_renderer); + if(tutel == nullptr){ + throw new UsefulException("couldn't find resource 'tutel.png'"); + } + resources::SDL_RenderCopyExF_Params params; + params.target_section = SDL_FRectConstruct(100, 100, 400, 400); + static i32 si = 1; + params.rotation_angle = M_PI_4 * (si++ / engine.fps_max); + tutel->render(params); + } + +}; int main(int argc, const char** argv){ try { - std::cout<<"initializing game engine... "; + std::cout<<"initializing ResourceManager"; + resources::ResourceManager resourceManager; + + std::cout<<"initializing Engine"; Engine engine; - std::cout<<"done"< void { - drawTutel(engine); - }; - - engine.openMainWindow("ougge"); - std::cout<<"created sdl window"< #include #include -#include "MainWindow.hpp" -#include "gui_exceptions.hpp" +#include "MainWindowSDL2.hpp" +#include "../gui/gui_exceptions.hpp" #include "../common/ougge_format.hpp" #include "../common/math.hpp" -namespace ougge::gui { +using namespace ougge::gui; -f32 MainWindow::getDPI(){ +namespace ougge::modules { + +f32 MainWindowSDL2::getDPI(){ i32 w=0, h=0; SDL_GetRendererOutputSize(sdl_renderer, &w, &h); i32 sim_w=0, sim_h=0; @@ -19,7 +21,11 @@ f32 MainWindow::getDPI(){ return dpi; } -void MainWindow::open(const std::string& window_title, resources::ResourceManager& resourceManager){ +MainWindowSDL2::MainWindowSDL2(Engine& engine, + const std::string& window_title, + resources::ResourceManager& resourceManager) + : EngineModule(engine, nameof(MainWindowSDL2)) +{ SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING)); SDL_version v; SDL_GetVersion(&v); @@ -78,7 +84,7 @@ void MainWindow::open(const std::string& window_title, resources::ResourceManage io.FontDefault = resources::ImFont_LoadFromResource(font_res, default_font_size, dpi); } -void MainWindow::close(){ +MainWindowSDL2::~MainWindowSDL2(){ ImGui_ImplSDLRenderer2_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); @@ -87,20 +93,22 @@ void MainWindow::close(){ SDL_Quit(); } -void MainWindow::pollEvents(bool* loopRunning){ +void MainWindowSDL2::pollEvents(){ SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); switch(event.type){ case SDL_QUIT: { - *loopRunning = false; + //TODO: log SDL_QUIT event + engine.stopLoop(); break; } case SDL_WINDOWEVENT: { if(event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)) { - *loopRunning = false; + //TODO: log SDL_WINDOWEVENT event + engine.stopLoop(); } break; } @@ -108,8 +116,10 @@ void MainWindow::pollEvents(bool* loopRunning){ } } +void MainWindowSDL2::beginFrame(){ + // process events happend since previous frame + pollEvents(); -void MainWindow::beginFrame(){ // Start the Dear ImGui frame ImGui_ImplSDLRenderer2_NewFrame(); ImGui_ImplSDL2_NewFrame(); @@ -126,16 +136,17 @@ void MainWindow::beginFrame(){ draw_bg_window(); draw_debug_window(); + drawErrorWindow(); } -void MainWindow::endFrame(){ +void MainWindowSDL2::endFrame(){ ImGui::Render(); ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer); // Swap buffers SDL_RenderPresent(sdl_renderer); } -void MainWindow::draw_bg_window(){ +void MainWindowSDL2::draw_bg_window(){ const ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode; const ImGuiWindowFlags window_flags = @@ -199,11 +210,11 @@ void MainWindow::draw_bg_window(){ ImGui::End(); } -void MainWindow::draw_debug_window(){ +void MainWindowSDL2::draw_debug_window(){ ImGuiIO& io = ImGui::GetIO(); if(ImGui::Begin("Debug Options", &show_debug_window)){ ImGui::ColorEdit3("clear_color", (float*)&clear_color); - ImGui::InputInt("fps_max", &fps_max); + ImGui::InputInt("fps_max", (int*)&engine.fps_max); ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Checkbox("Demo Window", &show_demo_window); ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window); @@ -217,4 +228,16 @@ void MainWindow::draw_debug_window(){ ImGui::ShowMetricsWindow(&show_metrics_window); } +void MainWindowSDL2::drawErrorWindow(){ + if(engine.error_messages.size() < 1) + return; + ImGui::Begin("ERRORS"); + if(ImGui::Button("Clear")) + engine.error_messages.clear(); + for(auto& msg : engine.error_messages){ + ImGui::TextWrapped("%s", msg.c_str()); + } + ImGui::End(); +} + } \ No newline at end of file diff --git a/src/gui/MainWindow.hpp b/src/modules/MainWindowSDL2.hpp similarity index 67% rename from src/gui/MainWindow.hpp rename to src/modules/MainWindowSDL2.hpp index c2c7b67..7de172a 100644 --- a/src/gui/MainWindow.hpp +++ b/src/modules/MainWindowSDL2.hpp @@ -2,23 +2,23 @@ #include #include +#include #include "../common/std.hpp" -#include "../common/time.hpp" #include "../resources/resources.hpp" #include "../resources/fonts.hpp" +#include "../Engine.hpp" + +namespace ougge::modules { /// converts hex color to float vector #define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f) /// converts float vector to hex color #define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)} -namespace ougge::gui { - #define default_font_path "fonts/DroidSans.ttf" -class MainWindow { +class MainWindowSDL2 : public EngineModule { public: - i32 fps_max = 60; f32 default_font_size = 14.0f; ImVec4 clear_color = RGBAHexToF(35,35,50,255); SDL_Window* sdl_window = nullptr; @@ -30,18 +30,19 @@ private: bool show_metrics_window = false; public: - void open(const std::string& window_title, resources::ResourceManager& resourceManager); - void close(); + MainWindowSDL2(Engine& engine, const std::string& window_title, + resources::ResourceManager& resourceManager); + ~MainWindowSDL2(); - /// process io events happened since previous frame - void pollEvents(bool* loopRunning); - void beginFrame(); - void endFrame(); + virtual void beginFrame(); + virtual void endFrame(); f32 getDPI(); private: + void pollEvents(); void draw_debug_window(); void draw_bg_window(); + void drawErrorWindow(); }; } diff --git a/src/modules/MonoGameObjectSystem.cpp b/src/modules/MonoGameObjectSystem.cpp new file mode 100644 index 0000000..abfe4e9 --- /dev/null +++ b/src/modules/MonoGameObjectSystem.cpp @@ -0,0 +1,35 @@ +#include "MonoGameObjectSystem.hpp" + +namespace ougge::modules { + +MonoGameObjectSystem::MonoGameObjectSystem(Engine& engine, u32 max_game_objects) : + EngineModule(engine, nameof(MonoGameObjectSystem)), + gameObjectPool(max_game_objects) +{ + engineManagedAssembly = mono.loadAssembly("Ougge.dll"); + gameObjectClass = engineManagedAssembly->getClass("Ougge", "GameObject"); + gameObjectCtor = Mono::Method(gameObjectClass, ".ctor"); + gameObjectInvokeUpdate = Mono::Method(gameObjectClass, "InvokeUpdate"); + gameObjectTryCreateComponent = Mono::Method(gameObjectClass, "TryCreateComponent_internal"); +} + +void MonoGameObjectSystem::beginFrame(){ + for(auto pair : gameObjectPool){ + gameObjectInvokeUpdate(pair.second.getObjectHandle().getObject(), engine.deltaTime); + } +} + +game::GameObject& MonoGameObjectSystem::createGameObject(){ + auto pair = gameObjectPool.emplace(game::GameObject(mono.createObject(gameObjectClass))); + game::GameObject& obj = pair.second; + gameObjectCtor(obj.getObjectHandle().getObject(), ++obj_id, pair.first); + return obj; +} + +bool MonoGameObjectSystem::tryCreateComponent(game::GameObject& obj, const std::string& componentClassName){ + Mono::String componentClassNameManaged = mono.createString(componentClassName); + Mono::Bool created = gameObjectTryCreateComponent(obj.getObjectHandle().getObject(), componentClassNameManaged); + return created.wide_bool; +} + +} diff --git a/src/modules/MonoGameObjectSystem.hpp b/src/modules/MonoGameObjectSystem.hpp new file mode 100644 index 0000000..6f7b82f --- /dev/null +++ b/src/modules/MonoGameObjectSystem.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "../Engine.hpp" +#include "../common/function_shared_ptr.hpp" +#include "../mono/mono.hpp" +#include "../game/GameObjectPool.hpp" + +namespace ougge::modules { + +class MonoGameObjectSystem : public EngineModule { + Mono::RuntimeJIT mono; + game::GameObjectPool gameObjectPool; + u64 obj_id = 0; + MonoClass* gameObjectClass; + Mono::Method gameObjectCtor; + Mono::Method gameObjectInvokeUpdate; + Mono::Method gameObjectTryCreateComponent; + +public: + std::shared_ptr engineManagedAssembly; + + MonoGameObjectSystem(Engine& engine, u32 max_game_objects); + + virtual void beginFrame(); + + game::GameObject& createGameObject(); + bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName); +}; + +}