diff --git a/config/gui_runtime.json b/config/gui_runtime.json index 1a94b30..c84b338 100644 --- a/config/gui_runtime.json +++ b/config/gui_runtime.json @@ -7,6 +7,9 @@ "window_width": 1024, "window_height": 768, "lua_script": "scripts/gui_demo.lua", + "runtime": { + "scene_source": "lua" + }, "scripts_directory": "scripts", "project_root": "../", "shaders_directory": "shaders", diff --git a/config/quake3_runtime.json b/config/quake3_runtime.json index f64303c..31f1267 100644 --- a/config/quake3_runtime.json +++ b/config/quake3_runtime.json @@ -7,6 +7,9 @@ "window_width": 1280, "window_height": 720, "lua_script": "scripts/quake3_arena.lua", + "runtime": { + "scene_source": "lua" + }, "scripts_directory": "scripts", "project_root": "../", "shaders_directory": "shaders", diff --git a/config/schema/runtime_config_v2.schema.json b/config/schema/runtime_config_v2.schema.json index 8960fc0..ab3bd13 100644 --- a/config/schema/runtime_config_v2.schema.json +++ b/config/schema/runtime_config_v2.schema.json @@ -37,6 +37,16 @@ }, "additionalProperties": true }, + "runtime": { + "type": "object", + "properties": { + "scene_source": { + "type": "string", + "enum": ["config", "lua"] + } + }, + "additionalProperties": true + }, "paths": { "type": "object", "properties": { diff --git a/config/seed_runtime.json b/config/seed_runtime.json index 8ed12be..d71ee30 100644 --- a/config/seed_runtime.json +++ b/config/seed_runtime.json @@ -27,6 +27,9 @@ "entry": "scripts/cube_logic.lua", "lua_debug": false }, + "runtime": { + "scene_source": "lua" + }, "paths": { "project_root": "../", "scripts": "scripts", diff --git a/config/seed_runtime_opengl.json b/config/seed_runtime_opengl.json index f6d29b7..5f5afaf 100644 --- a/config/seed_runtime_opengl.json +++ b/config/seed_runtime_opengl.json @@ -26,6 +26,9 @@ "entry": "scripts/cube_logic.lua", "lua_debug": false }, + "runtime": { + "scene_source": "lua" + }, "paths": { "project_root": "../", "scripts": "scripts", diff --git a/config/soundboard_runtime.json b/config/soundboard_runtime.json index d17b4ca..5f8856f 100644 --- a/config/soundboard_runtime.json +++ b/config/soundboard_runtime.json @@ -7,6 +7,9 @@ "window_width": 1024, "window_height": 768, "lua_script": "scripts/soundboard.lua", + "runtime": { + "scene_source": "lua" + }, "scripts_directory": "scripts", "project_root": "../", "shaders_directory": "shaders", diff --git a/config/vita_cube_runtime.json b/config/vita_cube_runtime.json index e1c0f90..cb8c670 100644 --- a/config/vita_cube_runtime.json +++ b/config/vita_cube_runtime.json @@ -7,6 +7,9 @@ "display_width": 960, "display_height": 544, "lua_script": "scripts/cube_logic.lua", + "runtime": { + "scene_source": "lua" + }, "scripts_directory": "scripts", "project_root": "../", "shaders_directory": "shaders", diff --git a/config/vita_gui_runtime.json b/config/vita_gui_runtime.json index 157a0b2..11a7ea2 100644 --- a/config/vita_gui_runtime.json +++ b/config/vita_gui_runtime.json @@ -7,6 +7,9 @@ "display_width": 960, "display_height": 544, "lua_script": "scripts/gui_demo.lua", + "runtime": { + "scene_source": "lua" + }, "scripts_directory": "scripts", "project_root": "../", "shaders_directory": "shaders", diff --git a/config/vita_soundboard_runtime.json b/config/vita_soundboard_runtime.json index 4d9fae2..95a7774 100644 --- a/config/vita_soundboard_runtime.json +++ b/config/vita_soundboard_runtime.json @@ -7,6 +7,9 @@ "display_width": 960, "display_height": 544, "lua_script": "scripts/soundboard.lua", + "runtime": { + "scene_source": "lua" + }, "scripts_directory": "scripts", "project_root": "../", "shaders_directory": "shaders", diff --git a/src/app/service_based_app.cpp b/src/app/service_based_app.cpp index b4b9d39..19df74f 100644 --- a/src/app/service_based_app.cpp +++ b/src/app/service_based_app.cpp @@ -340,6 +340,7 @@ void ServiceBasedApp::RegisterServices() { // Render coordinator service registry_.RegisterService( registry_.GetService(), + registry_.GetService(), registry_.GetService(), registry_.GetService(), registry_.GetService(), diff --git a/src/services/impl/json_config_service.cpp b/src/services/impl/json_config_service.cpp index 3d33141..1d837b3 100644 --- a/src/services/impl/json_config_service.cpp +++ b/src/services/impl/json_config_service.cpp @@ -26,6 +26,27 @@ constexpr const char* kConfigVersionKey = "configVersion"; constexpr const char* kExtendsKey = "extends"; constexpr const char* kDeleteKey = "@delete"; +const char* SceneSourceName(SceneSource source) { + switch (source) { + case SceneSource::Config: + return "config"; + case SceneSource::Lua: + return "lua"; + default: + return "config"; + } +} + +SceneSource ParseSceneSource(const std::string& value, const std::string& jsonPath) { + if (value == "config") { + return SceneSource::Config; + } + if (value == "lua") { + return SceneSource::Lua; + } + throw std::runtime_error("JSON member '" + jsonPath + "' must be 'config' or 'lua'"); +} + std::filesystem::path NormalizeConfigPath(const std::filesystem::path& path) { std::error_code ec; auto canonicalPath = std::filesystem::weakly_canonical(path, ec); @@ -463,6 +484,7 @@ RuntimeConfig JsonConfigService::LoadFromJson(std::shared_ptr logger, const auto* windowSizeValue = windowValue ? getObjectMember(*windowValue, "size", "window.size") : nullptr; + const auto* runtimeValue = getObjectMember(document, "runtime", "runtime"); const auto* inputValue = getObjectMember(document, "input", "input"); const auto* inputBindingsValue = inputValue ? getObjectMember(*inputValue, "bindings", "input.bindings") @@ -528,6 +550,14 @@ RuntimeConfig JsonConfigService::LoadFromJson(std::shared_ptr logger, } config.scriptPath = scriptPath; + if (runtimeValue && runtimeValue->HasMember("scene_source")) { + const auto& value = (*runtimeValue)["scene_source"]; + if (!value.IsString()) { + throw std::runtime_error("JSON member 'runtime.scene_source' must be a string"); + } + config.sceneSource = ParseSceneSource(value.GetString(), "runtime.scene_source"); + } + auto parseDimension = [&](const char* name, uint32_t defaultValue) -> uint32_t { if (!document.HasMember(name)) { return defaultValue; @@ -1176,6 +1206,10 @@ std::string JsonConfigService::BuildConfigJson(const RuntimeConfig& config, scriptsObject.AddMember("lua_debug", config.luaDebug, allocator); document.AddMember("scripts", scriptsObject, allocator); + rapidjson::Value runtimeObject(rapidjson::kObjectType); + addStringMember(runtimeObject, "scene_source", SceneSourceName(config.sceneSource)); + document.AddMember("runtime", runtimeObject, allocator); + rapidjson::Value windowObject(rapidjson::kObjectType); addStringMember(windowObject, "title", config.windowTitle); rapidjson::Value sizeObject(rapidjson::kObjectType); diff --git a/src/services/impl/json_config_service.hpp b/src/services/impl/json_config_service.hpp index 66cef28..040fd4d 100644 --- a/src/services/impl/json_config_service.hpp +++ b/src/services/impl/json_config_service.hpp @@ -86,6 +86,12 @@ public: } return config_.windowTitle; } + SceneSource GetSceneSource() const override { + if (logger_) { + logger_->Trace("JsonConfigService", "GetSceneSource"); + } + return config_.sceneSource; + } const InputBindings& GetInputBindings() const override { if (logger_) { logger_->Trace("JsonConfigService", "GetInputBindings"); diff --git a/src/services/impl/render_coordinator_service.cpp b/src/services/impl/render_coordinator_service.cpp index b210d25..b989d79 100644 --- a/src/services/impl/render_coordinator_service.cpp +++ b/src/services/impl/render_coordinator_service.cpp @@ -3,6 +3,7 @@ namespace sdl3cpp::services::impl { RenderCoordinatorService::RenderCoordinatorService(std::shared_ptr logger, + std::shared_ptr configService, std::shared_ptr graphicsService, std::shared_ptr sceneScriptService, std::shared_ptr shaderScriptService, @@ -10,6 +11,7 @@ RenderCoordinatorService::RenderCoordinatorService(std::shared_ptr logg std::shared_ptr guiService, std::shared_ptr sceneService) : logger_(std::move(logger)), + configService_(std::move(configService)), graphicsService_(std::move(graphicsService)), sceneScriptService_(std::move(sceneScriptService)), shaderScriptService_(std::move(shaderScriptService)), @@ -18,7 +20,8 @@ RenderCoordinatorService::RenderCoordinatorService(std::shared_ptr logg sceneService_(std::move(sceneService)) { if (logger_) { logger_->Trace("RenderCoordinatorService", "RenderCoordinatorService", - "graphicsService=" + std::string(graphicsService_ ? "set" : "null") + + "configService=" + std::string(configService_ ? "set" : "null") + + ", graphicsService=" + std::string(graphicsService_ ? "set" : "null") + ", sceneScriptService=" + std::string(sceneScriptService_ ? "set" : "null") + ", shaderScriptService=" + std::string(shaderScriptService_ ? "set" : "null") + ", guiScriptService=" + std::string(guiScriptService_ ? "set" : "null") + @@ -41,37 +44,49 @@ void RenderCoordinatorService::RenderFrame(float time) { return; } + const bool useLuaScene = !configService_ || configService_->GetSceneSource() == SceneSource::Lua; + if (!useLuaScene && !configFirstLogged_) { + if (logger_) { + logger_->Warn("RenderCoordinatorService::RenderFrame: config-first scene source selected; Lua scene path disabled"); + } + configFirstLogged_ = true; + } + if (!shadersLoaded_) { - if (!shaderScriptService_) { + if (!useLuaScene) { + shadersLoaded_ = true; + } else if (!shaderScriptService_) { if (logger_) { logger_->Error("RenderCoordinatorService::RenderFrame: Shader script service not available"); } return; } - if (logger_) { - logger_->Trace("RenderCoordinatorService", "RenderFrame", - "Priming bgfx with a dummy frame before shader load"); - } - if (!graphicsService_->BeginFrame()) { + if (!shadersLoaded_) { if (logger_) { - logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame"); + logger_->Trace("RenderCoordinatorService", "RenderFrame", + "Priming bgfx with a dummy frame before shader load"); + } + if (!graphicsService_->BeginFrame()) { + if (logger_) { + logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame"); + } + graphicsService_->RecreateSwapchain(); + return; + } + if (!graphicsService_->EndFrame()) { + if (logger_) { + logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame"); + } + graphicsService_->RecreateSwapchain(); + return; } - graphicsService_->RecreateSwapchain(); - return; - } - if (!graphicsService_->EndFrame()) { if (logger_) { - logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame"); + logger_->Trace("RenderCoordinatorService", "RenderFrame", "Loading shaders from Lua"); } - graphicsService_->RecreateSwapchain(); - return; + auto shaders = shaderScriptService_->LoadShaderPathsMap(); + graphicsService_->LoadShaders(shaders); + shadersLoaded_ = true; } - if (logger_) { - logger_->Trace("RenderCoordinatorService", "RenderFrame", "Loading shaders from Lua"); - } - auto shaders = shaderScriptService_->LoadShaderPathsMap(); - graphicsService_->LoadShaders(shaders); - shadersLoaded_ = true; } if (!graphicsService_->BeginFrame()) { @@ -88,7 +103,7 @@ void RenderCoordinatorService::RenderFrame(float time) { guiService_->PrepareFrame(guiCommands, extent.first, extent.second); } - if (sceneScriptService_ && sceneService_) { + if (useLuaScene && sceneScriptService_ && sceneService_) { auto sceneObjects = sceneScriptService_->LoadSceneObjects(); sceneService_->LoadScene(sceneObjects); diff --git a/src/services/impl/render_coordinator_service.hpp b/src/services/impl/render_coordinator_service.hpp index d85eab8..4f22488 100644 --- a/src/services/impl/render_coordinator_service.hpp +++ b/src/services/impl/render_coordinator_service.hpp @@ -1,6 +1,7 @@ #pragma once #include "../interfaces/i_render_coordinator_service.hpp" +#include "../interfaces/i_config_service.hpp" #include "../interfaces/i_graphics_service.hpp" #include "../interfaces/i_gui_script_service.hpp" #include "../interfaces/i_gui_service.hpp" @@ -15,6 +16,7 @@ namespace sdl3cpp::services::impl { class RenderCoordinatorService : public IRenderCoordinatorService { public: RenderCoordinatorService(std::shared_ptr logger, + std::shared_ptr configService, std::shared_ptr graphicsService, std::shared_ptr sceneScriptService, std::shared_ptr shaderScriptService, @@ -27,6 +29,7 @@ public: private: std::shared_ptr logger_; + std::shared_ptr configService_; std::shared_ptr graphicsService_; std::shared_ptr sceneScriptService_; std::shared_ptr shaderScriptService_; @@ -37,6 +40,7 @@ private: size_t lastIndexCount_ = 0; bool shadersLoaded_ = false; bool geometryUploaded_ = false; + bool configFirstLogged_ = false; }; } // namespace sdl3cpp::services::impl diff --git a/src/services/interfaces/config_types.hpp b/src/services/interfaces/config_types.hpp index ee24eb2..8797d72 100644 --- a/src/services/interfaces/config_types.hpp +++ b/src/services/interfaces/config_types.hpp @@ -86,6 +86,11 @@ struct BgfxConfig { std::string renderer = "vulkan"; }; +enum class SceneSource { + Config, + Lua +}; + struct MaterialXConfig { bool enabled = false; std::filesystem::path documentPath; @@ -152,6 +157,7 @@ struct RuntimeConfig { uint32_t height = 768; std::filesystem::path scriptPath; bool luaDebug = false; + SceneSource sceneSource = SceneSource::Config; std::string windowTitle = "SDL3 Bgfx Demo"; MouseGrabConfig mouseGrab{}; InputBindings inputBindings{}; diff --git a/src/services/interfaces/i_config_service.hpp b/src/services/interfaces/i_config_service.hpp index 2bcbcf1..8430d0c 100644 --- a/src/services/interfaces/i_config_service.hpp +++ b/src/services/interfaces/i_config_service.hpp @@ -48,6 +48,11 @@ public: */ virtual std::string GetWindowTitle() const = 0; + /** + * @brief Get the configured scene source. + * @return Scene source enum + */ + virtual SceneSource GetSceneSource() const = 0; /** * @brief Get configured input bindings. diff --git a/tests/test_bgfx_gui_service.cpp b/tests/test_bgfx_gui_service.cpp index b513e19..588591f 100644 --- a/tests/test_bgfx_gui_service.cpp +++ b/tests/test_bgfx_gui_service.cpp @@ -140,6 +140,9 @@ public: std::filesystem::path GetScriptPath() const override { return {}; } bool IsLuaDebugEnabled() const override { return false; } std::string GetWindowTitle() const override { return ""; } + sdl3cpp::services::SceneSource GetSceneSource() const override { + return sdl3cpp::services::SceneSource::Lua; + } const sdl3cpp::services::InputBindings& GetInputBindings() const override { return inputBindings_; } const sdl3cpp::services::MouseGrabConfig& GetMouseGrabConfig() const override { return mouseGrabConfig_; } const sdl3cpp::services::BgfxConfig& GetBgfxConfig() const override { return bgfxConfig_; } diff --git a/tests/test_cube_script.cpp b/tests/test_cube_script.cpp index 88d0ecd..6163376 100644 --- a/tests/test_cube_script.cpp +++ b/tests/test_cube_script.cpp @@ -103,6 +103,9 @@ public: std::filesystem::path GetScriptPath() const override { return {}; } bool IsLuaDebugEnabled() const override { return false; } std::string GetWindowTitle() const override { return ""; } + sdl3cpp::services::SceneSource GetSceneSource() const override { + return sdl3cpp::services::SceneSource::Lua; + } const sdl3cpp::services::InputBindings& GetInputBindings() const override { return inputBindings_; } const sdl3cpp::services::MouseGrabConfig& GetMouseGrabConfig() const override { return mouseGrabConfig_; } const sdl3cpp::services::BgfxConfig& GetBgfxConfig() const override { return bgfxConfig_; } @@ -150,6 +153,9 @@ public: std::filesystem::path GetScriptPath() const override { return scriptPath_; } bool IsLuaDebugEnabled() const override { return false; } std::string GetWindowTitle() const override { return ""; } + sdl3cpp::services::SceneSource GetSceneSource() const override { + return sdl3cpp::services::SceneSource::Lua; + } const sdl3cpp::services::InputBindings& GetInputBindings() const override { return inputBindings_; } const sdl3cpp::services::MouseGrabConfig& GetMouseGrabConfig() const override { return mouseGrabConfig_; } const sdl3cpp::services::BgfxConfig& GetBgfxConfig() const override { return bgfxConfig_; }