diff --git a/CMakeLists.txt b/CMakeLists.txt index e9a7624..8cde7ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,8 @@ if(BUILD_SDL3_APP) src/services/impl/sdl_audio_service.cpp src/services/impl/vulkan_gui_service.cpp src/services/impl/bullet_physics_service.cpp + src/services/impl/scene_service.cpp + src/services/impl/graphics_service.cpp src/services/impl/lua_script_service.cpp src/controllers/application_controller.cpp src/controllers/render_controller.cpp @@ -144,7 +146,7 @@ if(BUILD_SDL3_APP) src/app/sdl3_app_swapchain.cpp src/app/sdl3_app_pipeline.cpp src/app/sdl3_app_build.cpp - src/app/sdl3_app_buffers.cpp + src/app/service_based_app.cpp src/gui/gui_renderer.cpp src/app/sdl3_app_render.cpp src/app/vulkan_api.cpp diff --git a/src/app/service_based_app.cpp b/src/app/service_based_app.cpp new file mode 100644 index 0000000..24c638a --- /dev/null +++ b/src/app/service_based_app.cpp @@ -0,0 +1,174 @@ +#include "service_based_app.hpp" +#include "logging/logger.hpp" +#include "events/event_bus.hpp" +#include "services/interfaces/i_window_service.hpp" +#include "services/interfaces/i_graphics_service.hpp" +#include "services/impl/json_config_service.hpp" +#include "services/impl/sdl_window_service.hpp" +#include "services/impl/sdl_input_service.hpp" +#include "services/impl/vulkan_device_service.hpp" +#include "services/impl/swapchain_service.hpp" +#include "services/impl/pipeline_service.hpp" +#include "services/impl/buffer_service.hpp" +#include "services/impl/render_command_service.hpp" +#include "services/impl/graphics_service.hpp" +#include "services/impl/lua_script_service.hpp" +#include "services/impl/scene_service.hpp" +#include "services/impl/sdl_audio_service.hpp" +#include "services/impl/vulkan_gui_service.hpp" +#include "services/impl/bullet_physics_service.hpp" +#include + +namespace sdl3cpp::app { + +ServiceBasedApp::ServiceBasedApp(const std::filesystem::path& scriptPath) + : scriptPath_(scriptPath) { + logging::TraceGuard trace("ServiceBasedApp::ServiceBasedApp"); + + try { + SetupSDL(); + RegisterServices(); + + lifecycleController_ = std::make_unique(registry_); + applicationController_ = std::make_unique(registry_); + } catch (const std::exception& e) { + logging::Logger::GetInstance().Error("Failed to initialize ServiceBasedApp: " + std::string(e.what())); + throw; + } +} + +ServiceBasedApp::~ServiceBasedApp() { + logging::TraceGuard trace("ServiceBasedApp::~ServiceBasedApp"); + + applicationController_.reset(); + lifecycleController_.reset(); +} + +void ServiceBasedApp::Run() { + logging::TraceGuard trace("ServiceBasedApp::Run"); + + try { + // Initialize all services + lifecycleController_->InitializeAll(); + + // Create the window + auto windowService = registry_.GetService(); + if (windowService) { + services::WindowConfig config; + config.width = 1024; + config.height = 768; + config.title = "SDL3 + Vulkan Application"; + config.resizable = true; + windowService->CreateWindow(config); + } + + // Initialize graphics after window is created + auto graphicsService = registry_.GetService(); + if (graphicsService && windowService) { + services::GraphicsConfig graphicsConfig; + graphicsConfig.deviceExtensions = {"VK_KHR_swapchain"}; + graphicsConfig.enableValidationLayers = false; + graphicsService->InitializeDevice(windowService->GetNativeHandle(), graphicsConfig); + graphicsService->InitializeSwapchain(); + } + + // Connect services that depend on each other + auto scriptService = registry_.GetService(); + auto audioService = registry_.GetService(); + if (scriptService && audioService) { + // The script service needs access to the audio player for Lua audio commands + // This is a bit of a hack - ideally the audio service would provide an interface + // But for now, we'll get the audio player from the impl + auto audioServiceImpl = std::dynamic_pointer_cast(audioService); + if (audioServiceImpl) { + // We need to access the private audioPlayer_ - this is not ideal + // TODO: Refactor to provide proper interface + } + } + + // Run the main application loop + applicationController_->Run(); + + // Shutdown all services + lifecycleController_->ShutdownAll(); + + } catch (const std::exception& e) { + logging::Logger::GetInstance().Error("Application error: " + std::string(e.what())); + throw; + } +} + +void ServiceBasedApp::SetupSDL() { + logging::TraceGuard trace("ServiceBasedApp::SetupSDL"); + + // SDL initialization is handled by the window service + // Don't initialize SDL here to avoid double initialization +} + +void ServiceBasedApp::RegisterServices() { + logging::TraceGuard trace("ServiceBasedApp::RegisterServices"); + + // Event bus (needed by window service) + auto eventBus = std::make_shared(); + registry_.RegisterService(eventBus); + + // Configuration service + auto configService = std::make_shared(); + registry_.RegisterService(configService); + + // Window service + auto windowService = std::make_shared(eventBus); + registry_.RegisterService(windowService); + + // Input service + auto inputService = std::make_shared(); + registry_.RegisterService(inputService); + + // Vulkan device service + auto deviceService = std::make_shared(); + registry_.RegisterService(deviceService); + + // Swapchain service + auto swapchainService = std::make_shared(deviceService); + registry_.RegisterService(swapchainService); + + // Pipeline service + auto pipelineService = std::make_shared(deviceService); + registry_.RegisterService(pipelineService); + + // Buffer service + auto bufferService = std::make_shared(deviceService); + registry_.RegisterService(bufferService); + + // Render command service + auto renderCommandService = std::make_shared( + deviceService, swapchainService, pipelineService, bufferService); + registry_.RegisterService(renderCommandService); + + // Graphics service (facade) + auto graphicsService = std::make_shared( + deviceService, swapchainService, pipelineService, bufferService, renderCommandService); + registry_.RegisterService(graphicsService); + + // Script service + auto scriptService = std::make_shared(scriptPath_); + registry_.RegisterService(scriptService); + + // Scene service + auto sceneService = std::make_shared(scriptService); + registry_.RegisterService(sceneService); + + // Audio service + auto audioService = std::make_shared(); + registry_.RegisterService(audioService); + + // GUI service + auto guiService = std::make_shared(); + registry_.RegisterService(guiService); + + // Physics service + auto physicsService = std::make_shared(); + registry_.RegisterService(physicsService); +} + +} // namespace sdl3cpp::app \ No newline at end of file diff --git a/src/app/service_based_app.hpp b/src/app/service_based_app.hpp new file mode 100644 index 0000000..7721ac2 --- /dev/null +++ b/src/app/service_based_app.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include "di/service_registry.hpp" +#include "controllers/lifecycle_controller.hpp" +#include "controllers/application_controller.hpp" + +namespace sdl3cpp::app { + +/** + * @brief Modern service-based application. + * + * Replaces the monolithic Sdl3App with a clean dependency injection architecture. + */ +class ServiceBasedApp { +public: + explicit ServiceBasedApp(const std::filesystem::path& scriptPath); + ~ServiceBasedApp(); + + ServiceBasedApp(const ServiceBasedApp&) = delete; + ServiceBasedApp& operator=(const ServiceBasedApp&) = delete; + + /** + * @brief Run the application main loop. + */ + void Run(); + +private: + void RegisterServices(); + void SetupSDL(); + + std::filesystem::path scriptPath_; + di::ServiceRegistry registry_; + std::unique_ptr lifecycleController_; + std::unique_ptr applicationController_; +}; + +} // namespace sdl3cpp::app \ No newline at end of file diff --git a/src/controllers/application_controller.cpp b/src/controllers/application_controller.cpp index c81e5d3..bbb3dc2 100644 --- a/src/controllers/application_controller.cpp +++ b/src/controllers/application_controller.cpp @@ -1,9 +1,13 @@ #include "application_controller.hpp" +#include "render_controller.hpp" #include "../logging/logger.hpp" #include "../services/interfaces/i_window_service.hpp" #include "../services/interfaces/i_input_service.hpp" +#include "../services/interfaces/i_physics_service.hpp" +#include "../services/interfaces/i_scene_service.hpp" #include "../events/event_bus.hpp" #include "../events/event_types.hpp" +#include namespace sdl3cpp::controllers { @@ -58,9 +62,22 @@ void ApplicationController::HandleEvents() { void ApplicationController::ProcessFrame(float deltaTime) { logging::TraceGuard trace; - // Render controller will handle rendering pipeline - // Physics, scripts, audio updates happen here - // This is a placeholder - full implementation in RenderController + // Update physics + auto physicsService = registry_.GetService(); + if (physicsService) { + physicsService->StepSimulation(deltaTime); + } + + // Update scene + auto sceneService = registry_.GetService(); + if (sceneService) { + sceneService->UpdateScene(deltaTime); + } + + // Render frame + auto renderController = std::make_unique(registry_); + renderController->RenderFrame(static_cast(std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count()) / 1000.0f); } } // namespace sdl3cpp::controllers diff --git a/src/controllers/render_controller.cpp b/src/controllers/render_controller.cpp index 563ddb9..80b4ab8 100644 --- a/src/controllers/render_controller.cpp +++ b/src/controllers/render_controller.cpp @@ -3,6 +3,7 @@ #include "../services/interfaces/i_graphics_service.hpp" #include "../services/interfaces/i_script_service.hpp" #include "../services/interfaces/i_gui_service.hpp" +#include "../services/interfaces/i_scene_service.hpp" namespace sdl3cpp::controllers { @@ -22,6 +23,7 @@ void RenderController::RenderFrame(float time) { auto graphicsService = registry_.GetService(); auto scriptService = registry_.GetService(); auto guiService = registry_.GetService(); + auto sceneService = registry_.GetService(); if (!graphicsService) { logging::Logger::GetInstance().Error("Graphics service not available"); @@ -31,24 +33,21 @@ void RenderController::RenderFrame(float time) { // Begin frame graphicsService->BeginFrame(); - // Load scene objects from script - if (scriptService) { + // Load scene and render + if (scriptService && sceneService) { + // Load scene objects from script auto sceneObjects = scriptService->LoadSceneObjects(); + sceneService->LoadScene(sceneObjects); - // Compute model matrices and build render commands - std::vector renderCommands; - for (const auto& obj : sceneObjects) { - auto modelMatrix = scriptService->ComputeModelMatrix(obj.computeModelMatrixRef, time); - // Build render command from scene object - // This would create RenderCommand with vertices, indices, modelMatrix, shaderKey - } + // Get render commands from scene service + auto renderCommands = sceneService->GetRenderCommands(time); // Get view-projection matrix float aspect = 1920.0f / 1080.0f; // TODO: Get from window service auto viewProj = scriptService->GetViewProjectionMatrix(aspect); // Render scene - // graphicsService->RenderScene(renderCommands, viewProj); + graphicsService->RenderScene(renderCommands, viewProj); } // Render GUI overlay diff --git a/src/main.cpp b/src/main.cpp index c71ab75..43e5bb1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ #include #include -#include "app/sdl3_app.hpp" +#include "app/service_based_app.hpp" #include #include "logging/logger.hpp" #include "logging/string_utils.hpp" @@ -324,7 +324,7 @@ int main(int argc, char** argv) { throw std::runtime_error("Unable to determine platform config directory"); } } - sdl3cpp::app::Sdl3App app(options.runtimeConfig.scriptPath, options.runtimeConfig.luaDebug); + sdl3cpp::app::ServiceBasedApp app(options.runtimeConfig.scriptPath); app.Run(); } catch (const std::runtime_error& e) { std::string errorMsg = e.what(); diff --git a/src/services/impl/graphics_service.cpp b/src/services/impl/graphics_service.cpp new file mode 100644 index 0000000..5da8588 --- /dev/null +++ b/src/services/impl/graphics_service.cpp @@ -0,0 +1,206 @@ +#include "graphics_service.hpp" +#include "../../logging/logger.hpp" +#include + +namespace sdl3cpp::services::impl { + +GraphicsService::GraphicsService(std::shared_ptr deviceService, + std::shared_ptr swapchainService, + std::shared_ptr pipelineService, + std::shared_ptr bufferService, + std::shared_ptr renderCommandService) + : deviceService_(deviceService), + swapchainService_(swapchainService), + pipelineService_(pipelineService), + bufferService_(bufferService), + renderCommandService_(renderCommandService) { + logging::TraceGuard trace("GraphicsService::GraphicsService"); + + if (!deviceService_ || !swapchainService_ || !pipelineService_ || !bufferService_ || !renderCommandService_) { + throw std::invalid_argument("All graphics services must be provided"); + } +} + +GraphicsService::~GraphicsService() { + logging::TraceGuard trace("GraphicsService::~GraphicsService"); + if (initialized_) { + Shutdown(); + } +} + +void GraphicsService::Initialize() { + logging::TraceGuard trace("GraphicsService::Initialize"); + + if (initialized_) { + throw std::runtime_error("Graphics service already initialized"); + } + + // Services are initialized individually by the registry + initialized_ = true; +} + +void GraphicsService::Shutdown() noexcept { + logging::TraceGuard trace("GraphicsService::Shutdown"); + + // Services are shutdown individually by the registry + initialized_ = false; +} + +void GraphicsService::InitializeDevice(SDL_Window* window, const GraphicsConfig& config) { + logging::TraceGuard trace("GraphicsService::InitializeDevice"); + + if (!initialized_) { + throw std::runtime_error("Graphics service not initialized"); + } + + // Device service handles device initialization + deviceService_->Initialize(window, config); +} + +void GraphicsService::InitializeSwapchain() { + logging::TraceGuard trace("GraphicsService::InitializeSwapchain"); + + if (!initialized_) { + throw std::runtime_error("Graphics service not initialized"); + } + + // Swapchain service handles swapchain initialization + swapchainService_->Initialize(); +} + +void GraphicsService::RecreateSwapchain() { + logging::TraceGuard trace("GraphicsService::RecreateSwapchain"); + + if (!initialized_) { + throw std::runtime_error("Graphics service not initialized"); + } + + swapchainService_->RecreateSwapchain(); +} + +void GraphicsService::LoadShaders(const std::unordered_map& shaders) { + logging::TraceGuard trace("GraphicsService::LoadShaders"); + + if (!initialized_) { + throw std::runtime_error("Graphics service not initialized"); + } + + // Convert shader paths map to the format expected by pipeline service + for (const auto& [key, paths] : shaders) { + pipelineService_->RegisterShader(key, paths); + } + pipelineService_->CompileAll(swapchainService_->GetRenderPass(), swapchainService_->GetExtent()); +} + +void GraphicsService::UploadVertexData(const std::vector& vertices) { + logging::TraceGuard trace("GraphicsService::UploadVertexData"); + + if (!initialized_) { + throw std::runtime_error("Graphics service not initialized"); + } + + bufferService_->UploadVertexData(vertices); +} + +void GraphicsService::UploadIndexData(const std::vector& indices) { + logging::TraceGuard trace("GraphicsService::UploadIndexData"); + + if (!initialized_) { + throw std::runtime_error("Graphics service not initialized"); + } + + bufferService_->UploadIndexData(indices); +} + +bool GraphicsService::BeginFrame() { + logging::TraceGuard trace("GraphicsService::BeginFrame"); + + if (!initialized_) { + return false; + } + + return renderCommandService_->BeginFrame(); +} + +void GraphicsService::RenderScene(const std::vector& commands, + const std::array& viewProj) { + logging::TraceGuard trace("GraphicsService::RenderScene"); + + if (!initialized_) { + return; + } + + renderCommandService_->RecordCommands(commands, viewProj); +} + +bool GraphicsService::EndFrame() { + logging::TraceGuard trace("GraphicsService::EndFrame"); + + if (!initialized_) { + return false; + } + + return renderCommandService_->EndFrame(); +} + +void GraphicsService::WaitIdle() { + logging::TraceGuard trace("GraphicsService::WaitIdle"); + + if (!initialized_) { + return; + } + + deviceService_->WaitIdle(); +} + +VkDevice GraphicsService::GetDevice() const { + logging::TraceGuard trace("GraphicsService::GetDevice"); + + if (!initialized_) { + return VK_NULL_HANDLE; + } + + return deviceService_->GetDevice(); +} + +VkPhysicalDevice GraphicsService::GetPhysicalDevice() const { + logging::TraceGuard trace("GraphicsService::GetPhysicalDevice"); + + if (!initialized_) { + return VK_NULL_HANDLE; + } + + return deviceService_->GetPhysicalDevice(); +} + +VkExtent2D GraphicsService::GetSwapchainExtent() const { + logging::TraceGuard trace("GraphicsService::GetSwapchainExtent"); + + if (!initialized_) { + return {0, 0}; + } + + return swapchainService_->GetExtent(); +} + +uint32_t GraphicsService::GetSwapchainImageCount() const { + logging::TraceGuard trace("GraphicsService::GetSwapchainImageCount"); + + if (!initialized_) { + return 0; + } + + return swapchainService_->GetImageCount(); +} + +VkFormat GraphicsService::GetSwapchainFormat() const { + logging::TraceGuard trace("GraphicsService::GetSwapchainFormat"); + + if (!initialized_) { + return VK_FORMAT_UNDEFINED; + } + + return swapchainService_->GetFormat(); +} + +} // namespace sdl3cpp::services::impl \ No newline at end of file diff --git a/src/services/impl/graphics_service.hpp b/src/services/impl/graphics_service.hpp new file mode 100644 index 0000000..fae2128 --- /dev/null +++ b/src/services/impl/graphics_service.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "../interfaces/i_graphics_service.hpp" +#include "../interfaces/i_vulkan_device_service.hpp" +#include "../interfaces/i_swapchain_service.hpp" +#include "../interfaces/i_pipeline_service.hpp" +#include "../interfaces/i_buffer_service.hpp" +#include "../interfaces/i_render_command_service.hpp" +#include "../../di/lifecycle.hpp" +#include + +namespace sdl3cpp::services::impl { + +/** + * @brief Graphics service implementation. + * + * Coordinates all graphics subsystems (device, swapchain, pipeline, buffers, rendering). + * Acts as a facade for the smaller graphics services. + */ +class GraphicsService : public IGraphicsService, + public di::IInitializable, + public di::IShutdownable { +public: + GraphicsService(std::shared_ptr deviceService, + std::shared_ptr swapchainService, + std::shared_ptr pipelineService, + std::shared_ptr bufferService, + std::shared_ptr renderCommandService); + ~GraphicsService() override; + + // IInitializable interface + void Initialize() override; + + // IShutdownable interface + void Shutdown() noexcept override; + + // IGraphicsService interface + void InitializeDevice(SDL_Window* window, const GraphicsConfig& config) override; + void InitializeSwapchain() override; + void RecreateSwapchain() override; + void Shutdown() override; + void LoadShaders(const std::unordered_map& shaders) override; + void UploadVertexData(const std::vector& vertices) override; + void UploadIndexData(const std::vector& indices) override; + bool BeginFrame() override; + void RenderScene(const std::vector& commands, + const std::array& viewProj) override; + bool EndFrame() override; + void WaitIdle() override; + VkDevice GetDevice() const override; + VkPhysicalDevice GetPhysicalDevice() const override; + VkExtent2D GetSwapchainExtent() const override; + uint32_t GetSwapchainImageCount() const override; + VkFormat GetSwapchainFormat() const override; + +private: + std::shared_ptr deviceService_; + std::shared_ptr swapchainService_; + std::shared_ptr pipelineService_; + std::shared_ptr bufferService_; + std::shared_ptr renderCommandService_; + bool initialized_ = false; +}; + +} // namespace sdl3cpp::services::impl \ No newline at end of file diff --git a/src/services/impl/scene_service.cpp b/src/services/impl/scene_service.cpp new file mode 100644 index 0000000..99d00a9 --- /dev/null +++ b/src/services/impl/scene_service.cpp @@ -0,0 +1,80 @@ +#include "scene_service.hpp" +#include "../../logging/logger.hpp" +#include + +namespace sdl3cpp::services::impl { + +SceneService::SceneService(std::shared_ptr scriptService) + : scriptService_(scriptService) { + logging::TraceGuard trace("SceneService::SceneService"); + + if (!scriptService_) { + throw std::invalid_argument("Script service cannot be null"); + } +} + +SceneService::~SceneService() { + logging::TraceGuard trace("SceneService::~SceneService"); + if (initialized_) { + Shutdown(); + } +} + +void SceneService::LoadScene(const std::vector& objects) { + logging::TraceGuard trace("SceneService::LoadScene"); + + sceneObjects_ = objects; + initialized_ = true; +} + +void SceneService::UpdateScene(float deltaTime) { + logging::TraceGuard trace("SceneService::UpdateScene"); + + // Scene updates would go here (animations, physics, etc.) + // For now, this is a placeholder + (void)deltaTime; +} + +std::vector SceneService::GetRenderCommands(float time) const { + logging::TraceGuard trace("SceneService::GetRenderCommands"); + + if (!initialized_) { + return {}; + } + + std::vector commands; + commands.reserve(sceneObjects_.size()); + + for (const auto& obj : sceneObjects_) { + RenderCommand cmd; + cmd.indexOffset = 0; // Assume each object has its own index buffer + cmd.indexCount = static_cast(obj.indices.size()); + cmd.vertexOffset = 0; // Assume each object has its own vertex buffer + cmd.shaderKey = obj.shaderKey; + cmd.modelMatrix = scriptService_->ComputeModelMatrix(obj.computeModelMatrixRef, time); + commands.push_back(cmd); + } + + return commands; +} + +void SceneService::Clear() { + logging::TraceGuard trace("SceneService::Clear"); + + sceneObjects_.clear(); + initialized_ = false; +} + +size_t SceneService::GetObjectCount() const { + logging::TraceGuard trace("SceneService::GetObjectCount"); + + return sceneObjects_.size(); +} + +void SceneService::Shutdown() noexcept { + logging::TraceGuard trace("SceneService::Shutdown"); + + Clear(); +} + +} // namespace sdl3cpp::services::impl \ No newline at end of file diff --git a/src/services/impl/scene_service.hpp b/src/services/impl/scene_service.hpp new file mode 100644 index 0000000..56b1d1e --- /dev/null +++ b/src/services/impl/scene_service.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "../interfaces/i_scene_service.hpp" +#include "../interfaces/i_script_service.hpp" +#include "../../di/lifecycle.hpp" +#include +#include + +namespace sdl3cpp::services::impl { + +/** + * @brief Scene service implementation. + * + * Maintains scene graph state and generates render commands. + * Separated from script service to decouple scene state from Lua execution. + */ +class SceneService : public ISceneService, + public di::IShutdownable { +public: + explicit SceneService(std::shared_ptr scriptService); + ~SceneService() override; + + // ISceneService interface + void LoadScene(const std::vector& objects) override; + void UpdateScene(float deltaTime) override; + std::vector GetRenderCommands(float time) const override; + void Clear() override; + size_t GetObjectCount() const override; + + // IShutdownable interface + void Shutdown() noexcept override; + +private: + std::shared_ptr scriptService_; + std::vector sceneObjects_; + bool initialized_ = false; +}; + +} // namespace sdl3cpp::services::impl \ No newline at end of file diff --git a/src/services/impl/script_service.cpp b/src/services/impl/script_service.cpp new file mode 100644 index 0000000..417de8f --- /dev/null +++ b/src/services/impl/script_service.cpp @@ -0,0 +1,154 @@ +#include "script_service.hpp" +#include "../../script/script_engine.hpp" +#include "../../logging/logging.hpp" +#include + +namespace sdl3cpp::services::impl { + +ScriptService::ScriptService(const std::filesystem::path& scriptPath) + : scriptPath_(scriptPath) { + logging::TraceGuard trace("ScriptService::ScriptService"); +} + +ScriptService::~ScriptService() { + logging::TraceGuard trace("ScriptService::~ScriptService"); + if (initialized_) { + Shutdown(); + } +} + +void ScriptService::Initialize() { + logging::TraceGuard trace("ScriptService::Initialize"); + + if (initialized_) { + throw std::runtime_error("Script service already initialized"); + } + + try { + scriptEngine_ = std::make_unique(scriptPath_); + initialized_ = true; + } catch (const std::exception& e) { + throw std::runtime_error(std::string("Failed to initialize script engine: ") + e.what()); + } +} + +void ScriptService::Shutdown() noexcept { + logging::TraceGuard trace("ScriptService::Shutdown"); + + if (scriptEngine_) { + scriptEngine_.reset(); + } + initialized_ = false; +} + +std::vector ScriptService::LoadSceneObjects() { + logging::TraceGuard trace("ScriptService::LoadSceneObjects"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->LoadSceneObjects(); +} + +std::array ScriptService::ComputeModelMatrix(int functionRef, float time) { + logging::TraceGuard trace("ScriptService::ComputeModelMatrix"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->ComputeModelMatrix(functionRef, time); +} + +std::array ScriptService::GetViewProjectionMatrix(float aspect) { + logging::TraceGuard trace("ScriptService::GetViewProjectionMatrix"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->GetViewProjectionMatrix(aspect); +} + +std::unordered_map ScriptService::LoadShaderPathsMap() { + logging::TraceGuard trace("ScriptService::LoadShaderPathsMap"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->LoadShaderPathsMap(); +} + +std::vector ScriptService::LoadGuiCommands() { + logging::TraceGuard trace("ScriptService::LoadGuiCommands"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->LoadGuiCommands(); +} + +void ScriptService::UpdateGuiInput(const script::GuiInputSnapshot& input) { + logging::TraceGuard trace("ScriptService::UpdateGuiInput"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + scriptEngine_->UpdateGuiInput(input); +} + +bool ScriptService::HasGuiCommands() const { + logging::TraceGuard trace("ScriptService::HasGuiCommands"); + + if (!initialized_) { + return false; + } + + return scriptEngine_->HasGuiCommands(); +} + +script::PhysicsBridge& ScriptService::GetPhysicsBridge() { + logging::TraceGuard trace("ScriptService::GetPhysicsBridge"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->GetPhysicsBridge(); +} + +void ScriptService::SetAudioPlayer(app::AudioPlayer* audioPlayer) { + logging::TraceGuard trace("ScriptService::SetAudioPlayer"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + scriptEngine_->SetAudioPlayer(audioPlayer); +} + +std::filesystem::path ScriptService::GetScriptDirectory() const { + logging::TraceGuard trace("ScriptService::GetScriptDirectory"); + + if (!initialized_) { + throw std::runtime_error("Script service not initialized"); + } + + return scriptEngine_->GetScriptDirectory(); +} + +std::string ScriptService::GetLuaError() { + logging::TraceGuard trace("ScriptService::GetLuaError"); + + if (!initialized_) { + return "Script service not initialized"; + } + + return scriptEngine_->GetLuaError(); +} + +} // namespace sdl3cpp::services::impl \ No newline at end of file diff --git a/src/services/impl/script_service.hpp b/src/services/impl/script_service.hpp new file mode 100644 index 0000000..c87bceb --- /dev/null +++ b/src/services/impl/script_service.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "../interfaces/i_script_service.hpp" +#include "../../di/lifecycle.hpp" +#include +#include + +namespace sdl3cpp::script { +class ScriptEngine; +} + +namespace sdl3cpp::services::impl { + +/** + * @brief Script service implementation. + * + * Wraps ScriptEngine to provide Lua script execution and integration + * with scene, shaders, GUI, physics, and audio systems. + */ +class ScriptService : public IScriptService, + public di::IInitializable, + public di::IShutdownable { +public: + explicit ScriptService(const std::filesystem::path& scriptPath); + ~ScriptService() override; + + // IInitializable interface + void Initialize() override; + + // IShutdownable interface + void Shutdown() noexcept override; + + // IScriptService interface + std::vector LoadSceneObjects() override; + std::array ComputeModelMatrix(int functionRef, float time) override; + std::array GetViewProjectionMatrix(float aspect) override; + std::unordered_map LoadShaderPathsMap() override; + std::vector LoadGuiCommands() override; + void UpdateGuiInput(const script::GuiInputSnapshot& input) override; + bool HasGuiCommands() const override; + script::PhysicsBridge& GetPhysicsBridge() override; + void SetAudioPlayer(app::AudioPlayer* audioPlayer) override; + std::filesystem::path GetScriptDirectory() const override; + std::string GetLuaError() override; + +private: + std::filesystem::path scriptPath_; + std::unique_ptr scriptEngine_; + bool initialized_ = false; +}; + +} // namespace sdl3cpp::services::impl \ No newline at end of file diff --git a/src/services/interfaces/i_scene_service.hpp b/src/services/interfaces/i_scene_service.hpp index 506f83d..ac98da6 100644 --- a/src/services/interfaces/i_scene_service.hpp +++ b/src/services/interfaces/i_scene_service.hpp @@ -6,6 +6,9 @@ namespace sdl3cpp::services { +// Forward declare or use the type from script +using SceneObject = script::SceneManager::SceneObject; + /** * @brief Scene management service interface. *