From 19c369aac1fdf45c88d2cba4d69ee4aafc0c347e Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 4 Jan 2026 23:46:46 +0000 Subject: [PATCH] Refactor script services to improve Lua integration and logging - Updated MeshService to use MeshPayload directly instead of script::MeshPayload. - Enhanced SceneScriptService with detailed logging and error handling for Lua interactions. - Introduced ILogger dependency in SceneScriptService and ShaderScriptService for better traceability. - Implemented Lua state management in ScriptEngineService, allowing direct access to Lua functions. - Added new types for mesh and scene management, including MeshPayload and SceneObject. - Refactored shader loading logic in ShaderScriptService to utilize Lua for shader path retrieval. - Created GuiInputSnapshot and GuiCommand structures for GUI input handling. - Updated input and GUI script services to use new types and improved interfaces. - Enhanced test_cube_script to validate new service implementations and Lua interactions. --- CMakeLists.txt | 19 +- src/app/service_based_app.cpp | 9 +- src/gui/gui_renderer.cpp | 40 +-- src/gui/gui_renderer.hpp | 6 +- src/script/gui_manager.cpp | 253 --------------- src/script/gui_manager.hpp | 37 --- src/script/lua_bindings.cpp | 251 --------------- src/script/lua_bindings.hpp | 40 --- src/script/scene_manager.cpp | 257 --------------- src/script/scene_manager.hpp | 45 --- src/script/script_engine.cpp | 151 --------- src/script/script_engine.hpp | 62 ---- src/script/shader_manager.cpp | 101 ------ src/script/shader_manager.hpp | 30 -- src/services/impl/bullet_physics_service.cpp | 4 +- src/services/impl/gui_renderer_service.cpp | 2 +- src/services/impl/gui_renderer_service.hpp | 2 +- src/services/impl/gui_script_service.cpp | 293 ++++++++++++++++- src/services/impl/gui_script_service.hpp | 27 +- src/{script => services/impl}/lua_helpers.cpp | 10 +- src/{script => services/impl}/lua_helpers.hpp | 9 +- src/services/impl/mesh_service.cpp | 4 +- src/services/impl/mesh_service.hpp | 4 +- src/services/impl/scene_script_service.cpp | 265 +++++++++++++++- src/services/impl/scene_script_service.hpp | 11 +- src/services/impl/script_engine_service.cpp | 295 +++++++++++++++++- src/services/impl/script_engine_service.hpp | 29 +- src/services/impl/sdl_input_service.hpp | 2 +- src/services/impl/shader_script_service.cpp | 103 +++++- src/services/impl/shader_script_service.hpp | 10 +- .../interfaces}/gui_types.hpp | 23 +- .../interfaces/i_gui_renderer_service.hpp | 4 +- .../interfaces/i_gui_script_service.hpp | 6 +- src/services/interfaces/i_input_service.hpp | 1 - src/services/interfaces/i_mesh_service.hpp | 6 +- .../interfaces/i_scene_script_service.hpp | 4 +- src/services/interfaces/i_scene_service.hpp | 5 +- .../interfaces/i_script_engine_service.hpp | 6 +- src/services/interfaces/i_shader_service.hpp | 2 +- .../interfaces/mesh_types.hpp} | 9 +- src/services/interfaces/scene_types.hpp | 17 + tests/test_cube_script.cpp | 35 ++- 42 files changed, 1115 insertions(+), 1374 deletions(-) delete mode 100644 src/script/gui_manager.cpp delete mode 100644 src/script/gui_manager.hpp delete mode 100644 src/script/lua_bindings.cpp delete mode 100644 src/script/lua_bindings.hpp delete mode 100644 src/script/scene_manager.cpp delete mode 100644 src/script/scene_manager.hpp delete mode 100644 src/script/script_engine.cpp delete mode 100644 src/script/script_engine.hpp delete mode 100644 src/script/shader_manager.cpp delete mode 100644 src/script/shader_manager.hpp rename src/{script => services/impl}/lua_helpers.cpp (95%) rename src/{script => services/impl}/lua_helpers.hpp (66%) rename src/{script => services/interfaces}/gui_types.hpp (72%) rename src/{script/mesh_payload.hpp => services/interfaces/mesh_types.hpp} (53%) create mode 100644 src/services/interfaces/scene_types.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4899a29..d8c0af3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ if(BUILD_SDL3_APP) src/services/impl/logger_service.cpp src/services/impl/platform_service.cpp src/services/impl/script_engine_service.cpp + src/services/impl/lua_helpers.cpp src/services/impl/scene_script_service.cpp src/services/impl/shader_script_service.cpp src/services/impl/gui_script_service.cpp @@ -151,13 +152,7 @@ if(BUILD_SDL3_APP) src/services/impl/graphics_service.cpp src/app/service_based_app.cpp src/gui/gui_renderer.cpp - src/script/script_engine.cpp src/script/physics_bridge.cpp - src/script/lua_helpers.cpp - src/script/lua_bindings.cpp - src/script/scene_manager.cpp - src/script/shader_manager.cpp - src/script/gui_manager.cpp ) target_include_directories(sdl3_app PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") target_link_libraries(sdl3_app PRIVATE ${SDL_TARGET} Vulkan::Vulkan lua::lua CLI11::CLI11 rapidjson assimp::assimp Bullet::Bullet glm::glm Vorbis::vorbisfile Vorbis::vorbis) @@ -180,14 +175,12 @@ enable_testing() add_executable(script_engine_tests tests/test_cube_script.cpp - src/services/impl/platform_service.cpp - src/script/script_engine.cpp + src/services/impl/logger_service.cpp + src/services/impl/script_engine_service.cpp + src/services/impl/lua_helpers.cpp + src/services/impl/scene_script_service.cpp + src/services/impl/shader_script_service.cpp src/script/physics_bridge.cpp - src/script/scene_manager.cpp - src/script/shader_manager.cpp - src/script/gui_manager.cpp - src/script/lua_helpers.cpp - src/script/lua_bindings.cpp ) target_include_directories(script_engine_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") target_link_libraries(script_engine_tests PRIVATE ${SDL_TARGET} lua::lua assimp::assimp Bullet::Bullet glm::glm Vorbis::vorbisfile Vorbis::vorbis) diff --git a/src/app/service_based_app.cpp b/src/app/service_based_app.cpp index 462a0ed..679448a 100644 --- a/src/app/service_based_app.cpp +++ b/src/app/service_based_app.cpp @@ -248,11 +248,14 @@ void ServiceBasedApp::RegisterServices() { // Script-facing services registry_.RegisterService( - registry_.GetService()); + registry_.GetService(), + registry_.GetService()); registry_.RegisterService( - registry_.GetService()); + registry_.GetService(), + registry_.GetService()); registry_.RegisterService( - registry_.GetService()); + registry_.GetService(), + registry_.GetService()); // Connect input service to GUI script service for GUI input processing auto inputService = registry_.GetService(); diff --git a/src/gui/gui_renderer.cpp b/src/gui/gui_renderer.cpp index 0deb239..cc16263 100644 --- a/src/gui/gui_renderer.cpp +++ b/src/gui/gui_renderer.cpp @@ -14,7 +14,7 @@ #include "../../third_party/font8x8_basic.h" -namespace script = sdl3cpp::script; +namespace services = sdl3cpp::services; namespace sdl3cpp::gui { namespace { @@ -61,7 +61,7 @@ float ParseFloatValue(const std::string& text) { } } -script::GuiColor ParseColorString(const std::string& text, const script::GuiColor& fallback) { +services::GuiColor ParseColorString(const std::string& text, const services::GuiColor& fallback) { if (text.empty() || text[0] != '#') { return fallback; } @@ -141,9 +141,9 @@ ParsedSvg ParseSvgFile(const std::filesystem::path& path) { return result; } -script::GuiCommand::RectData IntersectRect(const script::GuiCommand::RectData& a, - const script::GuiCommand::RectData& b) { - script::GuiCommand::RectData result; +services::GuiCommand::RectData IntersectRect(const services::GuiCommand::RectData& a, + const services::GuiCommand::RectData& b) { + services::GuiCommand::RectData result; result.x = std::max(a.x, b.x); result.y = std::max(a.y, b.y); float right = std::min(a.x + a.width, b.x + b.width); @@ -161,7 +161,7 @@ int ClampToRange(int value, int minimum, int maximum) { class GuiRenderer::Canvas { public: - using RectData = script::GuiCommand::RectData; + using RectData = services::GuiCommand::RectData; void Resize(uint32_t width, uint32_t height) { width_ = width; @@ -187,8 +187,8 @@ public: } } - void FillRect(const RectData& rect, const script::GuiColor& fillColor, - const script::GuiColor& borderColor, float borderWidth) { + void FillRect(const RectData& rect, const services::GuiColor& fillColor, + const services::GuiColor& borderColor, float borderWidth) { DrawFilledRect(rect, fillColor); if (borderWidth > 0.0f && borderColor.a > 0.0f) { DrawFilledRect({rect.x, rect.y, rect.width, borderWidth}, borderColor); @@ -199,7 +199,7 @@ public: } } - void DrawText(const std::string& text, const script::GuiColor& color, const RectData& bounds, + void DrawText(const std::string& text, const services::GuiColor& color, const RectData& bounds, const std::string& alignX, const std::string& alignY, float fontSize) { if (text.empty() || width_ == 0 || height_ == 0) { return; @@ -244,7 +244,7 @@ public: } } - void DrawSvg(const ParsedSvg& svg, const RectData& target, const script::GuiColor& tint) { + void DrawSvg(const ParsedSvg& svg, const RectData& target, const services::GuiColor& tint) { if (svg.circles.empty() || svg.viewWidth <= 0.0f || svg.viewHeight <= 0.0f || width_ == 0 || height_ == 0) { return; @@ -260,7 +260,7 @@ public: float cx = clipped.x + circle.cx * scaleX; float cy = clipped.y + circle.cy * scaleY; float radius = circle.r * scale; - script::GuiColor color = circle.color; + services::GuiColor color = circle.color; if (tint.a > 0.0f) { color.r *= tint.r; color.g *= tint.g; @@ -300,7 +300,7 @@ private: return clipped; } - void DrawFilledRect(const RectData& rect, const script::GuiColor& color) { + void DrawFilledRect(const RectData& rect, const services::GuiColor& color) { if (rect.width <= 0.0f || rect.height <= 0.0f) { return; } @@ -319,7 +319,7 @@ private: } } - void BlendPixel(int x, int y, const script::GuiColor& color) { + void BlendPixel(int x, int y, const services::GuiColor& color) { size_t index = (static_cast(y) * width_ + static_cast(x)) * 4; auto clampByte = [](float value) -> uint8_t { return static_cast(std::clamp(value, 0.0f, 1.0f) * 255.0f); @@ -365,7 +365,7 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor return canvasWidth_ > 0 && canvasHeight_ > 0 && stagingBuffer_ != VK_NULL_HANDLE; } - void GuiRenderer::Prepare(const std::vector& commands, uint32_t width, + void GuiRenderer::Prepare(const std::vector& commands, uint32_t width, uint32_t height) { if (width == 0 || height == 0 || !canvas_) { return; @@ -374,10 +374,10 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor canvas_->Clear(); for (const auto& command : commands) { switch (command.type) { - case script::GuiCommand::Type::Rect: + case services::GuiCommand::Type::Rect: canvas_->FillRect(command.rect, command.color, command.borderColor, command.borderWidth); break; - case script::GuiCommand::Type::Text: { + case services::GuiCommand::Type::Text: { if (command.hasClipRect) { canvas_->PushClip(command.clipRect); } @@ -385,7 +385,7 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor canvas_->DrawText(command.text, command.color, command.bounds, command.alignX, command.alignY, command.fontSize); } else { - script::GuiCommand::RectData fallback{ + services::GuiCommand::RectData fallback{ command.rect.x, command.rect.y, command.fontSize * static_cast(std::max(1, command.text.size())), command.fontSize}; canvas_->DrawText(command.text, command.color, fallback, command.alignX, @@ -396,13 +396,13 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor } break; } - case script::GuiCommand::Type::ClipPush: + case services::GuiCommand::Type::ClipPush: canvas_->PushClip(command.rect); break; - case script::GuiCommand::Type::ClipPop: + case services::GuiCommand::Type::ClipPop: canvas_->PopClip(); break; - case script::GuiCommand::Type::Svg: + case services::GuiCommand::Type::Svg: if (command.svgPath.empty()) { break; } diff --git a/src/gui/gui_renderer.hpp b/src/gui/gui_renderer.hpp index 72fe0b4..e4e88b5 100644 --- a/src/gui/gui_renderer.hpp +++ b/src/gui/gui_renderer.hpp @@ -8,7 +8,7 @@ #include -#include "script/gui_types.hpp" +#include "services/interfaces/gui_types.hpp" #include "services/interfaces/i_buffer_service.hpp" namespace sdl3cpp::gui { @@ -17,7 +17,7 @@ struct SvgCircle { float cx = 0.0f; float cy = 0.0f; float r = 0.0f; - script::GuiColor color{1.0f, 1.0f, 1.0f, 1.0f}; + services::GuiColor color{1.0f, 1.0f, 1.0f, 1.0f}; }; struct ParsedSvg { @@ -36,7 +36,7 @@ public: GuiRenderer(const GuiRenderer&) = delete; GuiRenderer& operator=(const GuiRenderer&) = delete; - void Prepare(const std::vector& commands, uint32_t width, + void Prepare(const std::vector& commands, uint32_t width, uint32_t height); void BlitToSwapchain(VkCommandBuffer commandBuffer, VkImage image); void Resize(uint32_t width, uint32_t height, VkFormat format); diff --git a/src/script/gui_manager.cpp b/src/script/gui_manager.cpp deleted file mode 100644 index 7d55e8a..0000000 --- a/src/script/gui_manager.cpp +++ /dev/null @@ -1,253 +0,0 @@ -#include "script/gui_manager.hpp" -#include "services/interfaces/i_logger.hpp" - -#include - -#include -#include -#include -#include - -namespace sdl3cpp::script { - -GuiManager::GuiManager(lua_State* L, std::shared_ptr logger) - : L_(L), logger_(logger) { - if (logger_) { - logger_->Trace("GuiManager", "GuiManager"); - } - lua_getglobal(L_, "gui_input"); - if (!lua_isnil(L_, -1)) { - guiInputRef_ = luaL_ref(L_, LUA_REGISTRYINDEX); - } else { - lua_pop(L_, 1); - } - - lua_getglobal(L_, "get_gui_commands"); - if (lua_isfunction(L_, -1)) { - guiCommandsFnRef_ = luaL_ref(L_, LUA_REGISTRYINDEX); - } else { - lua_pop(L_, 1); - } -} - -std::vector GuiManager::LoadGuiCommands() { - if (logger_) { - logger_->Trace("GuiManager", "LoadGuiCommands"); - } - std::vector commands; - if (guiCommandsFnRef_ == LUA_REFNIL) { - return commands; - } - lua_rawgeti(L_, LUA_REGISTRYINDEX, guiCommandsFnRef_); - if (lua_pcall(L_, 0, 1, 0) != LUA_OK) { - std::string message = GetLuaError(); - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua get_gui_commands failed: " + message); - } - throw std::runtime_error("Lua get_gui_commands failed: " + message); - } - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("'get_gui_commands' did not return a table"); - } - throw std::runtime_error("'get_gui_commands' did not return a table"); - } - - size_t count = lua_rawlen(L_, -1); - commands.reserve(count); - - for (size_t i = 1; i <= count; ++i) { - lua_rawgeti(L_, -1, static_cast(i)); - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("GUI command at index " + std::to_string(i) + " is not a table"); - } - throw std::runtime_error("GUI command at index " + std::to_string(i) + " is not a table"); - } - int commandIndex = lua_gettop(L_); - lua_getfield(L_, commandIndex, "type"); - const char* typeName = lua_tostring(L_, -1); - if (!typeName) { - lua_pop(L_, 2); - if (logger_) { - logger_->Error("GUI command at index " + std::to_string(i) + " is missing a type"); - } - throw std::runtime_error("GUI command at index " + std::to_string(i) + " is missing a type"); - } - GuiCommand command{}; - if (std::strcmp(typeName, "rect") == 0) { - command.type = GuiCommand::Type::Rect; - command.rect = ReadRect(commandIndex); - command.color = ReadColor(commandIndex, GuiColor{0.0f, 0.0f, 0.0f, 1.0f}); - command.borderColor = ReadColor(commandIndex, GuiColor{0.0f, 0.0f, 0.0f, 0.0f}); - lua_getfield(L_, commandIndex, "borderWidth"); - if (lua_isnumber(L_, -1)) { - command.borderWidth = static_cast(lua_tonumber(L_, -1)); - } - lua_pop(L_, 1); - } else if (std::strcmp(typeName, "text") == 0) { - command.type = GuiCommand::Type::Text; - ReadStringField(commandIndex, "text", command.text); - lua_getfield(L_, commandIndex, "fontSize"); - if (lua_isnumber(L_, -1)) { - command.fontSize = static_cast(lua_tonumber(L_, -1)); - } - lua_pop(L_, 1); - std::string align; - if (ReadStringField(commandIndex, "alignX", align)) { - command.alignX = align; - } - if (ReadStringField(commandIndex, "alignY", align)) { - command.alignY = align; - } - lua_getfield(L_, commandIndex, "clipRect"); - if (lua_istable(L_, -1)) { - command.clipRect = ReadRect(-1); - command.hasClipRect = true; - } - lua_pop(L_, 1); - lua_getfield(L_, commandIndex, "bounds"); - if (lua_istable(L_, -1)) { - command.bounds = ReadRect(-1); - command.hasBounds = true; - } - lua_pop(L_, 1); - command.color = ReadColor(commandIndex, GuiColor{1.0f, 1.0f, 1.0f, 1.0f}); - } else if (std::strcmp(typeName, "clip_push") == 0) { - command.type = GuiCommand::Type::ClipPush; - command.rect = ReadRect(commandIndex); - } else if (std::strcmp(typeName, "clip_pop") == 0) { - command.type = GuiCommand::Type::ClipPop; - } else if (std::strcmp(typeName, "svg") == 0) { - command.type = GuiCommand::Type::Svg; - ReadStringField(commandIndex, "path", command.svgPath); - command.rect = ReadRect(commandIndex); - command.svgTint = ReadColor(commandIndex, GuiColor{1.0f, 1.0f, 1.0f, 0.0f}); - lua_getfield(L_, commandIndex, "tint"); - if (lua_istable(L_, -1)) { - command.svgTint = ReadColor(-1, command.svgTint); - } - lua_pop(L_, 1); - } - lua_pop(L_, 1); - lua_pop(L_, 1); - commands.push_back(std::move(command)); - } - - lua_pop(L_, 1); - return commands; -} - -void GuiManager::UpdateGuiInput(const GuiInputSnapshot& input) { - if (logger_) { - logger_->Trace("GuiManager", "UpdateGuiInput"); - } - if (guiInputRef_ == LUA_REFNIL) { - return; - } - lua_rawgeti(L_, LUA_REGISTRYINDEX, guiInputRef_); - int stateIndex = lua_gettop(L_); - - lua_getfield(L_, stateIndex, "resetTransient"); - lua_pushvalue(L_, stateIndex); - lua_call(L_, 1, 0); - - lua_getfield(L_, stateIndex, "setMouse"); - lua_pushvalue(L_, stateIndex); - lua_pushnumber(L_, input.mouseX); - lua_pushnumber(L_, input.mouseY); - lua_pushboolean(L_, input.mouseDown); - lua_call(L_, 4, 0); - - lua_getfield(L_, stateIndex, "setWheel"); - lua_pushvalue(L_, stateIndex); - lua_pushnumber(L_, input.wheel); - lua_call(L_, 2, 0); - - if (!input.textInput.empty()) { - lua_getfield(L_, stateIndex, "addTextInput"); - lua_pushvalue(L_, stateIndex); - lua_pushstring(L_, input.textInput.c_str()); - lua_call(L_, 2, 0); - } - - for (const auto& [key, pressed] : input.keyStates) { - lua_getfield(L_, stateIndex, "setKey"); - lua_pushvalue(L_, stateIndex); - lua_pushstring(L_, key.c_str()); - lua_pushboolean(L_, pressed); - lua_call(L_, 3, 0); - } - - lua_pop(L_, 1); -} - -bool GuiManager::HasGuiCommands() const { - return guiCommandsFnRef_ != LUA_REFNIL; -} - -GuiCommand::RectData GuiManager::ReadRect(int index) { - GuiCommand::RectData rect{}; - if (!lua_istable(L_, index)) { - return rect; - } - int absIndex = lua_absindex(L_, index); - auto readField = [&](const char* name, float defaultValue) -> float { - lua_getfield(L_, absIndex, name); - float value = defaultValue; - if (lua_isnumber(L_, -1)) { - value = static_cast(lua_tonumber(L_, -1)); - } - lua_pop(L_, 1); - return value; - }; - rect.x = readField("x", rect.x); - rect.y = readField("y", rect.y); - rect.width = readField("width", rect.width); - rect.height = readField("height", rect.height); - return rect; -} - -GuiColor GuiManager::ReadColor(int index, const GuiColor& defaultColor) { - GuiColor color = defaultColor; - if (!lua_istable(L_, index)) { - return color; - } - int absIndex = lua_absindex(L_, index); - for (int component = 0; component < 4; ++component) { - lua_rawgeti(L_, absIndex, component + 1); - if (lua_isnumber(L_, -1)) { - float value = static_cast(lua_tonumber(L_, -1)); - switch (component) { - case 0: color.r = value; break; - case 1: color.g = value; break; - case 2: color.b = value; break; - case 3: color.a = value; break; - } - } - lua_pop(L_, 1); - } - return color; -} - -bool GuiManager::ReadStringField(int index, const char* name, std::string& outString) { - int absIndex = lua_absindex(L_, index); - lua_getfield(L_, absIndex, name); - if (lua_isstring(L_, -1)) { - outString = lua_tostring(L_, -1); - lua_pop(L_, 1); - return true; - } - lua_pop(L_, 1); - return false; -} - -std::string GuiManager::GetLuaError() { - const char* message = lua_tostring(L_, -1); - return message ? message : "unknown lua error"; -} - -} // namespace sdl3cpp::script diff --git a/src/script/gui_manager.hpp b/src/script/gui_manager.hpp deleted file mode 100644 index d6cace4..0000000 --- a/src/script/gui_manager.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "script/gui_types.hpp" -#include "script/lua_helpers.hpp" - -#include - -#include -#include - -namespace sdl3cpp::services { -class ILogger; -} - -namespace sdl3cpp::script { - -class GuiManager { -public: - GuiManager(lua_State* L, std::shared_ptr logger); - - std::vector LoadGuiCommands(); - void UpdateGuiInput(const GuiInputSnapshot& input); - bool HasGuiCommands() const; - -private: - lua_State* L_; - int guiInputRef_ = LUA_REFNIL; - int guiCommandsFnRef_ = LUA_REFNIL; - std::shared_ptr logger_; - - GuiCommand::RectData ReadRect(int index); - GuiColor ReadColor(int index, const GuiColor& defaultColor); - bool ReadStringField(int index, const char* name, std::string& outString); - std::string GetLuaError(); -}; - -} // namespace sdl3cpp::script diff --git a/src/script/lua_bindings.cpp b/src/script/lua_bindings.cpp deleted file mode 100644 index 3e471b1..0000000 --- a/src/script/lua_bindings.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include "script/lua_bindings.hpp" -#include "script/lua_helpers.hpp" -#include "services/interfaces/i_audio_command_service.hpp" -#include "services/interfaces/i_mesh_service.hpp" -#include "services/interfaces/i_physics_bridge_service.hpp" -#include "services/interfaces/i_logger.hpp" - -#include -#include -#include - -namespace sdl3cpp::script { - -void LuaBindings::RegisterBindings(lua_State* L, LuaBindingContext* context) { - auto logger = context ? context->logger : nullptr; - if (logger) { - logger->Trace("LuaBindings", "RegisterBindings"); - } - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &LoadMeshFromFile, 1); - lua_setglobal(L, "load_mesh_from_file"); - - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &PhysicsCreateBox, 1); - lua_setglobal(L, "physics_create_box"); - - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &PhysicsStepSimulation, 1); - lua_setglobal(L, "physics_step_simulation"); - - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &PhysicsGetTransform, 1); - lua_setglobal(L, "physics_get_transform"); - - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &GlmMatrixFromTransform, 1); - lua_setglobal(L, "glm_matrix_from_transform"); - - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &AudioPlayBackground, 1); - lua_setglobal(L, "audio_play_background"); - - lua_pushlightuserdata(L, context); - lua_pushcclosure(L, &AudioPlaySound, 1); - lua_setglobal(L, "audio_play_sound"); -} - -int LuaBindings::LoadMeshFromFile(lua_State* L) { - auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); - auto logger = context ? context->logger : nullptr; - if (!context || !context->meshService) { - lua_pushnil(L); - lua_pushstring(L, "Mesh service not available"); - return 2; - } - - const char* path = luaL_checkstring(L, 1); - if (logger) { - logger->Trace("LuaBindings", "LoadMeshFromFile"); - logger->TraceVariable("path", std::string(path)); - } - - MeshPayload payload; - std::string error; - if (!context->meshService->LoadFromFile(path, payload, error)) { - lua_pushnil(L); - lua_pushstring(L, error.c_str()); - return 2; - } - - context->meshService->PushMeshToLua(L, payload); - lua_pushnil(L); - return 2; -} - -int LuaBindings::PhysicsCreateBox(lua_State* L) { - auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); - auto logger = context ? context->logger : nullptr; - if (!context || !context->physicsBridgeService) { - lua_pushnil(L); - lua_pushstring(L, "Physics service not available"); - return 2; - } - - const char* name = luaL_checkstring(L, 1); - if (logger) { - logger->Trace("LuaBindings", "PhysicsCreateBox"); - logger->TraceVariable("name", std::string(name)); - } - - if (!lua_istable(L, 2) || !lua_istable(L, 4) || !lua_istable(L, 5)) { - luaL_error(L, "physics_create_box expects vector tables for half extents, origin, and rotation"); - } - - std::array halfExtents = ReadVector3(L, 2); - float mass = static_cast(luaL_checknumber(L, 3)); - std::array origin = ReadVector3(L, 4); - std::array rotation = ReadQuaternion(L, 5); - - btTransform transform; - transform.setIdentity(); - transform.setOrigin(btVector3(origin[0], origin[1], origin[2])); - transform.setRotation(btQuaternion(rotation[0], rotation[1], rotation[2], rotation[3])); - - std::string error; - if (!context->physicsBridgeService->AddBoxRigidBody( - name, - btVector3(halfExtents[0], halfExtents[1], halfExtents[2]), - mass, - transform, - error)) { - lua_pushnil(L); - lua_pushstring(L, error.c_str()); - return 2; - } - - lua_pushboolean(L, 1); - return 1; -} - -int LuaBindings::PhysicsStepSimulation(lua_State* L) { - auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); - auto logger = context ? context->logger : nullptr; - if (!context || !context->physicsBridgeService) { - lua_pushinteger(L, 0); - return 1; - } - if (logger) { - logger->Trace("LuaBindings", "PhysicsStepSimulation"); - } - float deltaTime = static_cast(luaL_checknumber(L, 1)); - int steps = context->physicsBridgeService->StepSimulation(deltaTime); - lua_pushinteger(L, steps); - return 1; -} - -int LuaBindings::PhysicsGetTransform(lua_State* L) { - auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); - auto logger = context ? context->logger : nullptr; - if (!context || !context->physicsBridgeService) { - lua_pushnil(L); - lua_pushstring(L, "Physics service not available"); - return 2; - } - const char* name = luaL_checkstring(L, 1); - if (logger) { - logger->Trace("LuaBindings", "PhysicsGetTransform"); - logger->TraceVariable("name", std::string(name)); - } - - btTransform transform; - std::string error; - if (!context->physicsBridgeService->GetRigidBodyTransform(name, transform, error)) { - lua_pushnil(L); - lua_pushstring(L, error.c_str()); - return 2; - } - - lua_newtable(L); - lua_newtable(L); - const btVector3& origin = transform.getOrigin(); - lua_pushnumber(L, origin.x()); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, origin.y()); - lua_rawseti(L, -2, 2); - lua_pushnumber(L, origin.z()); - lua_rawseti(L, -2, 3); - lua_setfield(L, -2, "position"); - - lua_newtable(L); - const btQuaternion& orientation = transform.getRotation(); - lua_pushnumber(L, orientation.x()); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, orientation.y()); - lua_rawseti(L, -2, 2); - lua_pushnumber(L, orientation.z()); - lua_rawseti(L, -2, 3); - lua_pushnumber(L, orientation.w()); - lua_rawseti(L, -2, 4); - lua_setfield(L, -2, "rotation"); - - return 1; -} - -int LuaBindings::AudioPlayBackground(lua_State* L) { - auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); - auto logger = context ? context->logger : nullptr; - if (!context || !context->audioCommandService) { - lua_pushnil(L); - lua_pushstring(L, "Audio service not available"); - return 2; - } - const char* path = luaL_checkstring(L, 1); - bool loop = true; - if (lua_gettop(L) >= 2 && lua_isboolean(L, 2)) { - loop = lua_toboolean(L, 2); - } - if (logger) { - logger->Trace("LuaBindings", "AudioPlayBackground"); - logger->TraceVariable("path", std::string(path)); - logger->TraceVariable("loop", loop); - } - - std::string error; - if (!context->audioCommandService->QueueAudioCommand( - services::AudioCommandType::Background, path, loop, error)) { - lua_pushnil(L); - lua_pushstring(L, error.c_str()); - return 2; - } - - lua_pushboolean(L, 1); - return 1; -} - -int LuaBindings::AudioPlaySound(lua_State* L) { - auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); - auto logger = context ? context->logger : nullptr; - if (!context || !context->audioCommandService) { - lua_pushnil(L); - lua_pushstring(L, "Audio service not available"); - return 2; - } - const char* path = luaL_checkstring(L, 1); - bool loop = false; - if (lua_gettop(L) >= 2 && lua_isboolean(L, 2)) { - loop = lua_toboolean(L, 2); - } - if (logger) { - logger->Trace("LuaBindings", "AudioPlaySound"); - logger->TraceVariable("path", std::string(path)); - logger->TraceVariable("loop", loop); - } - - std::string error; - if (!context->audioCommandService->QueueAudioCommand( - services::AudioCommandType::Effect, path, loop, error)) { - lua_pushnil(L); - lua_pushstring(L, error.c_str()); - return 2; - } - - lua_pushboolean(L, 1); - return 1; -} - -int LuaBindings::GlmMatrixFromTransform(lua_State* L) { - return LuaGlmMatrixFromTransform(L); -} - -} // namespace sdl3cpp::script diff --git a/src/script/lua_bindings.hpp b/src/script/lua_bindings.hpp deleted file mode 100644 index ffba78c..0000000 --- a/src/script/lua_bindings.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef SDL3CPP_SCRIPT_LUA_BINDINGS_HPP -#define SDL3CPP_SCRIPT_LUA_BINDINGS_HPP - -#include - -struct lua_State; - -namespace sdl3cpp::services { -class IAudioCommandService; -class IMeshService; -class IPhysicsBridgeService; -class ILogger; -} - -namespace sdl3cpp::script { - -struct LuaBindingContext { - std::shared_ptr meshService; - std::shared_ptr audioCommandService; - std::shared_ptr physicsBridgeService; - std::shared_ptr logger; -}; - -class LuaBindings { -public: - static void RegisterBindings(lua_State* L, LuaBindingContext* context); - -private: - static int LoadMeshFromFile(lua_State* L); - static int PhysicsCreateBox(lua_State* L); - static int PhysicsStepSimulation(lua_State* L); - static int PhysicsGetTransform(lua_State* L); - static int AudioPlayBackground(lua_State* L); - static int AudioPlaySound(lua_State* L); - static int GlmMatrixFromTransform(lua_State* L); -}; - -} // namespace sdl3cpp::script - -#endif // SDL3CPP_SCRIPT_LUA_BINDINGS_HPP diff --git a/src/script/scene_manager.cpp b/src/script/scene_manager.cpp deleted file mode 100644 index b3f9b9f..0000000 --- a/src/script/scene_manager.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "script/scene_manager.hpp" -#include "services/interfaces/i_logger.hpp" - -#include - -#include -#include -#include - -namespace sdl3cpp::script { - -SceneManager::SceneManager(lua_State* L, std::shared_ptr logger) - : L_(L), logger_(logger) { - if (logger_) { - logger_->Trace("SceneManager", "SceneManager"); - } -} - -std::vector SceneManager::LoadSceneObjects() { - if (logger_) { - logger_->Trace("SceneManager", "LoadSceneObjects"); - } - lua_getglobal(L_, "get_scene_objects"); - if (!lua_isfunction(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua function 'get_scene_objects' is missing"); - } - throw std::runtime_error("Lua function 'get_scene_objects' is missing"); - } - if (lua_pcall(L_, 0, 1, 0) != LUA_OK) { - std::string message = GetLuaError(); - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua get_scene_objects failed: " + message); - } - throw std::runtime_error("Lua get_scene_objects failed: " + message); - } - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("'get_scene_objects' did not return a table"); - } - throw std::runtime_error("'get_scene_objects' did not return a table"); - } - - size_t count = lua_rawlen(L_, -1); - std::vector objects; - objects.reserve(count); - - for (size_t i = 1; i <= count; ++i) { - lua_rawgeti(L_, -1, static_cast(i)); - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Scene object at index " + std::to_string(i) + " is not a table"); - } - throw std::runtime_error("Scene object at index " + std::to_string(i) + " is not a table"); - } - - SceneObject object; - lua_getfield(L_, -1, "vertices"); - object.vertices = ReadVertexArray(-1); - lua_pop(L_, 1); - if (object.vertices.empty()) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Scene object " + std::to_string(i) + " must supply at least one vertex"); - } - throw std::runtime_error("Scene object " + std::to_string(i) + " must supply at least one vertex"); - } - - lua_getfield(L_, -1, "indices"); - object.indices = ReadIndexArray(-1); - lua_pop(L_, 1); - if (object.indices.empty()) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Scene object " + std::to_string(i) + " must supply indices"); - } - throw std::runtime_error("Scene object " + std::to_string(i) + " must supply indices"); - } - - lua_getfield(L_, -1, "compute_model_matrix"); - if (lua_isfunction(L_, -1)) { - object.computeModelMatrixRef = luaL_ref(L_, LUA_REGISTRYINDEX); - } else { - lua_pop(L_, 1); - object.computeModelMatrixRef = LUA_REFNIL; - } - - lua_getfield(L_, -1, "shader_key"); - if (lua_isstring(L_, -1)) { - object.shaderKey = lua_tostring(L_, -1); - } - lua_pop(L_, 1); - - objects.push_back(std::move(object)); - lua_pop(L_, 1); - } - - lua_pop(L_, 1); - return objects; -} - -std::array SceneManager::ComputeModelMatrix(int functionRef, float time) { - if (logger_) { - logger_->Trace("SceneManager", "ComputeModelMatrix", "time=" + std::to_string(time)); - } - if (functionRef == LUA_REFNIL) { - lua_getglobal(L_, "compute_model_matrix"); - if (!lua_isfunction(L_, -1)) { - lua_pop(L_, 1); - return IdentityMatrix(); - } - } else { - lua_rawgeti(L_, LUA_REGISTRYINDEX, functionRef); - } - - lua_pushnumber(L_, time); - if (lua_pcall(L_, 1, 1, 0) != LUA_OK) { - std::string message = GetLuaError(); - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua compute_model_matrix failed: " + message); - } - throw std::runtime_error("Lua compute_model_matrix failed: " + message); - } - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("'compute_model_matrix' did not return a table"); - } - throw std::runtime_error("'compute_model_matrix' did not return a table"); - } - - std::array matrix = ReadMatrix(L_, -1); - lua_pop(L_, 1); - return matrix; -} - -std::array SceneManager::GetViewProjectionMatrix(float aspect) { - if (logger_) { - logger_->Trace("SceneManager", "GetViewProjectionMatrix", "aspect=" + std::to_string(aspect)); - } - lua_getglobal(L_, "get_view_projection"); - if (!lua_isfunction(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua function 'get_view_projection' is missing"); - } - throw std::runtime_error("Lua function 'get_view_projection' is missing"); - } - lua_pushnumber(L_, aspect); - if (lua_pcall(L_, 1, 1, 0) != LUA_OK) { - std::string message = GetLuaError(); - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua get_view_projection failed: " + message); - } - throw std::runtime_error("Lua get_view_projection failed: " + message); - } - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("'get_view_projection' did not return a table"); - } - throw std::runtime_error("'get_view_projection' did not return a table"); - } - std::array matrix = ReadMatrix(L_, -1); - lua_pop(L_, 1); - return matrix; -} - -std::vector SceneManager::ReadVertexArray(int index) { - int absIndex = lua_absindex(L_, index); - if (!lua_istable(L_, absIndex)) { - if (logger_) { - logger_->Error("Expected table for vertex data"); - } - throw std::runtime_error("Expected table for vertex data"); - } - - size_t count = lua_rawlen(L_, absIndex); - std::vector vertices; - vertices.reserve(count); - - for (size_t i = 1; i <= count; ++i) { - lua_rawgeti(L_, absIndex, static_cast(i)); - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Vertex entry at index " + std::to_string(i) + " is not a table"); - } - throw std::runtime_error("Vertex entry at index " + std::to_string(i) + " is not a table"); - } - - int vertexIndex = lua_gettop(L_); - core::Vertex vertex{}; - - lua_getfield(L_, vertexIndex, "position"); - vertex.position = ReadVector3(L_, -1); - lua_pop(L_, 1); - - lua_getfield(L_, vertexIndex, "color"); - vertex.color = ReadVector3(L_, -1); - lua_pop(L_, 1); - - lua_pop(L_, 1); - vertices.push_back(vertex); - } - - return vertices; -} - -std::vector SceneManager::ReadIndexArray(int index) { - int absIndex = lua_absindex(L_, index); - if (!lua_istable(L_, absIndex)) { - if (logger_) { - logger_->Error("Expected table for index data"); - } - throw std::runtime_error("Expected table for index data"); - } - - size_t count = lua_rawlen(L_, absIndex); - std::vector indices; - indices.reserve(count); - - for (size_t i = 1; i <= count; ++i) { - lua_rawgeti(L_, absIndex, static_cast(i)); - if (!lua_isinteger(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Index entry at position " + std::to_string(i) + " is not an integer"); - } - throw std::runtime_error("Index entry at position " + std::to_string(i) + " is not an integer"); - } - lua_Integer value = lua_tointeger(L_, -1); - lua_pop(L_, 1); - if (value < 1) { - if (logger_) { - logger_->Error("Index values must be 1 or greater"); - } - throw std::runtime_error("Index values must be 1 or greater"); - } - indices.push_back(static_cast(value - 1)); - } - - return indices; -} - -std::string SceneManager::GetLuaError() { - const char* message = lua_tostring(L_, -1); - return message ? message : "unknown lua error"; -} - -} // namespace sdl3cpp::script diff --git a/src/script/scene_manager.hpp b/src/script/scene_manager.hpp deleted file mode 100644 index 20a8122..0000000 --- a/src/script/scene_manager.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include "script/lua_helpers.hpp" -#include "core/vertex.hpp" - -#include -#include -#include -#include -#include - -namespace sdl3cpp::services { -class ILogger; -} - -namespace sdl3cpp::script { - -class SceneManager { -public: - struct SceneObject { - std::vector vertices; - std::vector indices; - int computeModelMatrixRef = LUA_REFNIL; - std::string shaderKey = "default"; - }; - - SceneManager(lua_State* L, std::shared_ptr logger); - - std::vector LoadSceneObjects(); - std::array ComputeModelMatrix(int functionRef, float time); - std::array GetViewProjectionMatrix(float aspect); - -private: - lua_State* L_; - - std::vector ReadVertexArray(int index); - std::vector ReadIndexArray(int index); - std::string GetLuaError(); - - std::shared_ptr logger_; -}; - -} // namespace sdl3cpp::script diff --git a/src/script/script_engine.cpp b/src/script/script_engine.cpp deleted file mode 100644 index 3d8b3cd..0000000 --- a/src/script/script_engine.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "script/script_engine.hpp" -#include "script/scene_manager.hpp" -#include "script/shader_manager.hpp" -#include "script/gui_manager.hpp" -#include "script/lua_bindings.hpp" -#include "services/interfaces/i_logger.hpp" - -#include - -#include - -namespace sdl3cpp::script { - -ScriptEngine::ScriptEngine(const std::filesystem::path& scriptPath, bool debugEnabled) - : ScriptEngine(scriptPath, nullptr, debugEnabled) { -} - -ScriptEngine::ScriptEngine(const std::filesystem::path& scriptPath, LuaBindingContext* bindingContext, bool debugEnabled) - : L_(luaL_newstate()), - scriptDirectory_(scriptPath.parent_path()), - debugEnabled_(debugEnabled) { - LuaBindingContext* resolvedContext = bindingContext; - if (!resolvedContext) { - ownedBindingContext_ = std::make_unique(); - resolvedContext = ownedBindingContext_.get(); - } - if (resolvedContext) { - logger_ = resolvedContext->logger; - } - if (logger_) { - logger_->Trace("ScriptEngine", "ScriptEngine"); - } - if (!L_) { - if (logger_) { - logger_->Error("Failed to create Lua state"); - } - throw std::runtime_error("Failed to create Lua state"); - } - - if (logger_) { - logger_->Debug("Lua state created successfully"); - } - luaL_openlibs(L_); - - LuaBindings::RegisterBindings(L_, resolvedContext); - - lua_pushboolean(L_, debugEnabled_); - lua_setglobal(L_, "lua_debug"); - - auto scriptDir = scriptPath.parent_path(); - if (!scriptDir.empty()) { - lua_getglobal(L_, "package"); - if (lua_istable(L_, -1)) { - lua_getfield(L_, -1, "path"); - const char* currentPath = lua_tostring(L_, -1); - std::string newPath = scriptDir.string() + "/?.lua;"; - if (currentPath) { - newPath += currentPath; - } - lua_pop(L_, 1); - lua_pushstring(L_, newPath.c_str()); - lua_setfield(L_, -2, "path"); - } - lua_pop(L_, 1); - } - - if (luaL_dofile(L_, scriptPath.string().c_str()) != LUA_OK) { - std::string message = sdl3cpp::script::GetLuaError(L_); - lua_pop(L_, 1); - lua_close(L_); - L_ = nullptr; - if (logger_) { - logger_->Error("Failed to load Lua script: " + message); - } - throw std::runtime_error("Failed to load Lua script: " + message); - } - - if (logger_) { - logger_->Info("Lua script loaded successfully: " + scriptPath.string()); - } - - sceneManager_ = std::make_unique(L_, logger_); - shaderManager_ = std::make_unique(L_, logger_); - guiManager_ = std::make_unique(L_, logger_); -} - -ScriptEngine::~ScriptEngine() { - if (L_) { - lua_close(L_); - } -} - -std::vector ScriptEngine::LoadSceneObjects() { - if (!sceneManager_) { - throw std::runtime_error("Scene manager not initialized"); - } - return sceneManager_->LoadSceneObjects(); -} - -std::array ScriptEngine::ComputeModelMatrix(int functionRef, float time) { - if (!sceneManager_) { - throw std::runtime_error("Scene manager not initialized"); - } - return sceneManager_->ComputeModelMatrix(functionRef, time); -} - -std::array ScriptEngine::GetViewProjectionMatrix(float aspect) { - if (!sceneManager_) { - throw std::runtime_error("Scene manager not initialized"); - } - return sceneManager_->GetViewProjectionMatrix(aspect); -} - -std::unordered_map ScriptEngine::LoadShaderPathsMap() { - if (!shaderManager_) { - throw std::runtime_error("Shader manager not initialized"); - } - return shaderManager_->LoadShaderPathsMap(); -} - -std::vector ScriptEngine::LoadGuiCommands() { - if (!guiManager_) { - throw std::runtime_error("Gui manager not initialized"); - } - return guiManager_->LoadGuiCommands(); -} - -void ScriptEngine::UpdateGuiInput(const GuiInputSnapshot& input) { - if (!guiManager_) { - throw std::runtime_error("Gui manager not initialized"); - } - guiManager_->UpdateGuiInput(input); -} - -bool ScriptEngine::HasGuiCommands() const { - if (!guiManager_) { - return false; - } - return guiManager_->HasGuiCommands(); -} - -std::filesystem::path ScriptEngine::GetScriptDirectory() const { - return scriptDirectory_; -} - -std::string ScriptEngine::GetLuaError() { - const char* message = lua_tostring(L_, -1); - return message ? message : "unknown lua error"; -} - -} // namespace sdl3cpp::script diff --git a/src/script/script_engine.hpp b/src/script/script_engine.hpp deleted file mode 100644 index 46a0be9..0000000 --- a/src/script/script_engine.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef SDL3CPP_SCRIPT_SCRIPT_ENGINE_HPP -#define SDL3CPP_SCRIPT_SCRIPT_ENGINE_HPP - -#include -#include -#include -#include -#include -#include - -#include - -#include "script/gui_types.hpp" -#include "script/scene_manager.hpp" -#include "script/shader_manager.hpp" -#include "script/gui_manager.hpp" - -namespace sdl3cpp::script { - -struct LuaBindingContext; - -} - -namespace sdl3cpp::services { -class ILogger; -} - -namespace sdl3cpp::script { - -class ScriptEngine { -public: - explicit ScriptEngine(const std::filesystem::path& scriptPath, bool debugEnabled = false); - ScriptEngine(const std::filesystem::path& scriptPath, LuaBindingContext* bindingContext, bool debugEnabled = false); - ~ScriptEngine(); - - ScriptEngine(const ScriptEngine&) = delete; - ScriptEngine& operator=(const ScriptEngine&) = delete; - - std::vector LoadSceneObjects(); - std::array ComputeModelMatrix(int functionRef, float time); - std::array GetViewProjectionMatrix(float aspect); - std::unordered_map LoadShaderPathsMap(); - std::vector LoadGuiCommands(); - void UpdateGuiInput(const GuiInputSnapshot& input); - bool HasGuiCommands() const; - std::filesystem::path GetScriptDirectory() const; - std::string GetLuaError(); - -private: - lua_State* L_ = nullptr; - std::filesystem::path scriptDirectory_; - bool debugEnabled_ = false; - std::unique_ptr ownedBindingContext_; - std::unique_ptr sceneManager_; - std::unique_ptr shaderManager_; - std::unique_ptr guiManager_; - std::shared_ptr logger_; -}; - -} // namespace sdl3cpp::script - -#endif // SDL3CPP_SCRIPT_SCRIPT_ENGINE_HPP diff --git a/src/script/shader_manager.cpp b/src/script/shader_manager.cpp deleted file mode 100644 index 5780f31..0000000 --- a/src/script/shader_manager.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "script/shader_manager.hpp" -#include "services/interfaces/i_logger.hpp" - -#include - -#include -#include -#include - -namespace sdl3cpp::script { - -ShaderManager::ShaderManager(lua_State* L, std::shared_ptr logger) - : L_(L), logger_(logger) { - if (logger_) { - logger_->Trace("ShaderManager", "ShaderManager"); - } -} - -std::unordered_map ShaderManager::LoadShaderPathsMap() { - if (logger_) { - logger_->Trace("ShaderManager", "LoadShaderPathsMap"); - } - lua_getglobal(L_, "get_shader_paths"); - if (!lua_isfunction(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua function 'get_shader_paths' is missing"); - } - throw std::runtime_error("Lua function 'get_shader_paths' is missing"); - } - if (lua_pcall(L_, 0, 1, 0) != LUA_OK) { - std::string message = GetLuaError(); - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Lua get_shader_paths failed: " + message); - } - throw std::runtime_error("Lua get_shader_paths failed: " + message); - } - if (!lua_istable(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("'get_shader_paths' did not return a table"); - } - throw std::runtime_error("'get_shader_paths' did not return a table"); - } - - std::unordered_map shaderMap; - lua_pushnil(L_); - while (lua_next(L_, -2) != 0) { - if (lua_isstring(L_, -2) && lua_istable(L_, -1)) { - std::string key = lua_tostring(L_, -2); - shaderMap.emplace(key, ReadShaderPathsTable(-1)); - } - lua_pop(L_, 1); - } - - lua_pop(L_, 1); - if (shaderMap.empty()) { - if (logger_) { - logger_->Error("'get_shader_paths' did not return any shader variants"); - } - throw std::runtime_error("'get_shader_paths' did not return any shader variants"); - } - return shaderMap; -} - -sdl3cpp::services::ShaderPaths ShaderManager::ReadShaderPathsTable(int index) { - sdl3cpp::services::ShaderPaths paths; - int absIndex = lua_absindex(L_, index); - - lua_getfield(L_, absIndex, "vertex"); - if (!lua_isstring(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Shader path 'vertex' must be a string"); - } - throw std::runtime_error("Shader path 'vertex' must be a string"); - } - paths.vertex = lua_tostring(L_, -1); - lua_pop(L_, 1); - - lua_getfield(L_, absIndex, "fragment"); - if (!lua_isstring(L_, -1)) { - lua_pop(L_, 1); - if (logger_) { - logger_->Error("Shader path 'fragment' must be a string"); - } - throw std::runtime_error("Shader path 'fragment' must be a string"); - } - paths.fragment = lua_tostring(L_, -1); - lua_pop(L_, 1); - - return paths; -} - -std::string ShaderManager::GetLuaError() { - const char* message = lua_tostring(L_, -1); - return message ? message : "unknown lua error"; -} - -} // namespace sdl3cpp::script diff --git a/src/script/shader_manager.hpp b/src/script/shader_manager.hpp deleted file mode 100644 index 7e14af9..0000000 --- a/src/script/shader_manager.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "../services/interfaces/graphics_types.hpp" -#include - -#include -#include -#include - -namespace sdl3cpp::services { -class ILogger; -} - -namespace sdl3cpp::script { - -class ShaderManager { -public: - ShaderManager(lua_State* L, std::shared_ptr logger); - - std::unordered_map LoadShaderPathsMap(); - -private: - lua_State* L_; - std::shared_ptr logger_; - - sdl3cpp::services::ShaderPaths ReadShaderPathsTable(int index); - std::string GetLuaError(); -}; - -} // namespace sdl3cpp::script diff --git a/src/services/impl/bullet_physics_service.cpp b/src/services/impl/bullet_physics_service.cpp index f44b6cf..d45b6cc 100644 --- a/src/services/impl/bullet_physics_service.cpp +++ b/src/services/impl/bullet_physics_service.cpp @@ -21,7 +21,7 @@ void BulletPhysicsService::Initialize(const btVector3& gravity) { return; } - physicsBridge_ = std::make_unique(); + physicsBridge_ = std::make_unique(logger_); initialized_ = true; logger_->Info("Physics service initialized"); @@ -140,7 +140,7 @@ void BulletPhysicsService::Clear() { // PhysicsBridge doesn't expose Clear in current implementation // Shutdown and reinitialize to clear all bodies physicsBridge_.reset(); - physicsBridge_ = std::make_unique(); + physicsBridge_ = std::make_unique(logger_); } } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/gui_renderer_service.cpp b/src/services/impl/gui_renderer_service.cpp index 75afd6d..045daab 100644 --- a/src/services/impl/gui_renderer_service.cpp +++ b/src/services/impl/gui_renderer_service.cpp @@ -22,7 +22,7 @@ void GuiRendererService::Initialize(VkDevice device, device, physicalDevice, format, resourcePath, bufferService_); } -void GuiRendererService::PrepareFrame(const std::vector& commands, +void GuiRendererService::PrepareFrame(const std::vector& commands, uint32_t width, uint32_t height) { if (logger_) { diff --git a/src/services/impl/gui_renderer_service.hpp b/src/services/impl/gui_renderer_service.hpp index a1df583..420f34e 100644 --- a/src/services/impl/gui_renderer_service.hpp +++ b/src/services/impl/gui_renderer_service.hpp @@ -20,7 +20,7 @@ public: VkFormat format, const std::filesystem::path& resourcePath) override; - void PrepareFrame(const std::vector& commands, + void PrepareFrame(const std::vector& commands, uint32_t width, uint32_t height) override; diff --git a/src/services/impl/gui_script_service.cpp b/src/services/impl/gui_script_service.cpp index e351b36..1f821b3 100644 --- a/src/services/impl/gui_script_service.cpp +++ b/src/services/impl/gui_script_service.cpp @@ -1,22 +1,301 @@ #include "gui_script_service.hpp" + +#include "lua_helpers.hpp" +#include "services/interfaces/i_logger.hpp" + +#include + +#include +#include +#include #include +#include namespace sdl3cpp::services::impl { -GuiScriptService::GuiScriptService(std::shared_ptr engineService) - : engineService_(std::move(engineService)) { +GuiScriptService::GuiScriptService(std::shared_ptr engineService, + std::shared_ptr logger) + : engineService_(std::move(engineService)), + logger_(std::move(logger)) { } -std::vector GuiScriptService::LoadGuiCommands() { - return engineService_->GetEngine().LoadGuiCommands(); +void GuiScriptService::Initialize() { + if (logger_) { + logger_->Trace("GuiScriptService", "Initialize"); + } + lua_State* L = GetLuaState(); + + lua_getglobal(L, "gui_input"); + if (!lua_isnil(L, -1)) { + guiInputRef_ = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } + + lua_getglobal(L, "get_gui_commands"); + if (lua_isfunction(L, -1)) { + guiCommandsFnRef_ = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } } -void GuiScriptService::UpdateGuiInput(const script::GuiInputSnapshot& input) { - engineService_->GetEngine().UpdateGuiInput(input); +void GuiScriptService::Shutdown() noexcept { + if (logger_) { + logger_->Trace("GuiScriptService", "Shutdown"); + } + + if (!engineService_ || !engineService_->IsInitialized()) { + guiInputRef_ = -1; + guiCommandsFnRef_ = -1; + return; + } + + lua_State* L = engineService_->GetLuaState(); + if (!L) { + guiInputRef_ = -1; + guiCommandsFnRef_ = -1; + return; + } + + if (guiInputRef_ >= 0) { + luaL_unref(L, LUA_REGISTRYINDEX, guiInputRef_); + } + if (guiCommandsFnRef_ >= 0) { + luaL_unref(L, LUA_REGISTRYINDEX, guiCommandsFnRef_); + } + guiInputRef_ = -1; + guiCommandsFnRef_ = -1; +} + +std::vector GuiScriptService::LoadGuiCommands() { + if (logger_) { + logger_->Trace("GuiScriptService", "LoadGuiCommands"); + } + if (guiCommandsFnRef_ < 0) { + return {}; + } + lua_State* L = GetLuaState(); + + std::vector commands; + lua_rawgeti(L, LUA_REGISTRYINDEX, guiCommandsFnRef_); + if (lua_pcall(L, 0, 1, 0) != LUA_OK) { + std::string message = lua::GetLuaError(L); + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua get_gui_commands failed: " + message); + } + throw std::runtime_error("Lua get_gui_commands failed: " + message); + } + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("'get_gui_commands' did not return a table"); + } + throw std::runtime_error("'get_gui_commands' did not return a table"); + } + + size_t count = lua_rawlen(L, -1); + commands.reserve(count); + + for (size_t i = 1; i <= count; ++i) { + lua_rawgeti(L, -1, static_cast(i)); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("GUI command at index " + std::to_string(i) + " is not a table"); + } + throw std::runtime_error("GUI command at index " + std::to_string(i) + " is not a table"); + } + int commandIndex = lua_gettop(L); + lua_getfield(L, commandIndex, "type"); + const char* typeName = lua_tostring(L, -1); + if (!typeName) { + lua_pop(L, 2); + if (logger_) { + logger_->Error("GUI command at index " + std::to_string(i) + " is missing a type"); + } + throw std::runtime_error("GUI command at index " + std::to_string(i) + " is missing a type"); + } + GuiCommand command{}; + if (std::strcmp(typeName, "rect") == 0) { + command.type = GuiCommand::Type::Rect; + command.rect = ReadRect(L, commandIndex); + command.color = ReadColor(L, commandIndex, GuiColor{0.0f, 0.0f, 0.0f, 1.0f}); + command.borderColor = ReadColor(L, commandIndex, GuiColor{0.0f, 0.0f, 0.0f, 0.0f}); + lua_getfield(L, commandIndex, "borderWidth"); + if (lua_isnumber(L, -1)) { + command.borderWidth = static_cast(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + } else if (std::strcmp(typeName, "text") == 0) { + command.type = GuiCommand::Type::Text; + ReadStringField(L, commandIndex, "text", command.text); + lua_getfield(L, commandIndex, "fontSize"); + if (lua_isnumber(L, -1)) { + command.fontSize = static_cast(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + std::string align; + if (ReadStringField(L, commandIndex, "alignX", align)) { + command.alignX = align; + } + if (ReadStringField(L, commandIndex, "alignY", align)) { + command.alignY = align; + } + lua_getfield(L, commandIndex, "clipRect"); + if (lua_istable(L, -1)) { + command.clipRect = ReadRect(L, -1); + command.hasClipRect = true; + } + lua_pop(L, 1); + lua_getfield(L, commandIndex, "bounds"); + if (lua_istable(L, -1)) { + command.bounds = ReadRect(L, -1); + command.hasBounds = true; + } + lua_pop(L, 1); + command.color = ReadColor(L, commandIndex, GuiColor{1.0f, 1.0f, 1.0f, 1.0f}); + } else if (std::strcmp(typeName, "clip_push") == 0) { + command.type = GuiCommand::Type::ClipPush; + command.rect = ReadRect(L, commandIndex); + } else if (std::strcmp(typeName, "clip_pop") == 0) { + command.type = GuiCommand::Type::ClipPop; + } else if (std::strcmp(typeName, "svg") == 0) { + command.type = GuiCommand::Type::Svg; + ReadStringField(L, commandIndex, "path", command.svgPath); + command.rect = ReadRect(L, commandIndex); + command.svgTint = ReadColor(L, commandIndex, GuiColor{1.0f, 1.0f, 1.0f, 0.0f}); + lua_getfield(L, commandIndex, "tint"); + if (lua_istable(L, -1)) { + command.svgTint = ReadColor(L, -1, command.svgTint); + } + lua_pop(L, 1); + } + lua_pop(L, 1); + lua_pop(L, 1); + commands.push_back(std::move(command)); + } + + lua_pop(L, 1); + return commands; +} + +void GuiScriptService::UpdateGuiInput(const GuiInputSnapshot& input) { + if (logger_) { + logger_->Trace("GuiScriptService", "UpdateGuiInput"); + } + if (guiInputRef_ < 0) { + return; + } + lua_State* L = GetLuaState(); + + lua_rawgeti(L, LUA_REGISTRYINDEX, guiInputRef_); + int stateIndex = lua_gettop(L); + + lua_getfield(L, stateIndex, "resetTransient"); + lua_pushvalue(L, stateIndex); + lua_call(L, 1, 0); + + lua_getfield(L, stateIndex, "setMouse"); + lua_pushvalue(L, stateIndex); + lua_pushnumber(L, input.mouseX); + lua_pushnumber(L, input.mouseY); + lua_pushboolean(L, input.mouseDown); + lua_call(L, 4, 0); + + lua_getfield(L, stateIndex, "setWheel"); + lua_pushvalue(L, stateIndex); + lua_pushnumber(L, input.wheel); + lua_call(L, 2, 0); + + if (!input.textInput.empty()) { + lua_getfield(L, stateIndex, "addTextInput"); + lua_pushvalue(L, stateIndex); + lua_pushstring(L, input.textInput.c_str()); + lua_call(L, 2, 0); + } + + for (const auto& [key, pressed] : input.keyStates) { + lua_getfield(L, stateIndex, "setKey"); + lua_pushvalue(L, stateIndex); + lua_pushstring(L, key.c_str()); + lua_pushboolean(L, pressed); + lua_call(L, 3, 0); + } + + lua_pop(L, 1); } bool GuiScriptService::HasGuiCommands() const { - return engineService_->GetEngine().HasGuiCommands(); + return guiCommandsFnRef_ >= 0; +} + +GuiCommand::RectData GuiScriptService::ReadRect(lua_State* L, int index) const { + GuiCommand::RectData rect{}; + if (!lua_istable(L, index)) { + return rect; + } + int absIndex = lua_absindex(L, index); + auto readField = [&](const char* name, float defaultValue) -> float { + lua_getfield(L, absIndex, name); + float value = defaultValue; + if (lua_isnumber(L, -1)) { + value = static_cast(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + return value; + }; + rect.x = readField("x", rect.x); + rect.y = readField("y", rect.y); + rect.width = readField("width", rect.width); + rect.height = readField("height", rect.height); + return rect; +} + +GuiColor GuiScriptService::ReadColor(lua_State* L, int index, const GuiColor& defaultColor) const { + GuiColor color = defaultColor; + if (!lua_istable(L, index)) { + return color; + } + int absIndex = lua_absindex(L, index); + for (int component = 0; component < 4; ++component) { + lua_rawgeti(L, absIndex, component + 1); + if (lua_isnumber(L, -1)) { + float value = static_cast(lua_tonumber(L, -1)); + switch (component) { + case 0: color.r = value; break; + case 1: color.g = value; break; + case 2: color.b = value; break; + case 3: color.a = value; break; + } + } + lua_pop(L, 1); + } + return color; +} + +bool GuiScriptService::ReadStringField(lua_State* L, int index, const char* name, std::string& outString) const { + int absIndex = lua_absindex(L, index); + lua_getfield(L, absIndex, name); + if (lua_isstring(L, -1)) { + outString = lua_tostring(L, -1); + lua_pop(L, 1); + return true; + } + lua_pop(L, 1); + return false; +} + +lua_State* GuiScriptService::GetLuaState() const { + if (!engineService_) { + throw std::runtime_error("GUI script service is missing script engine service"); + } + lua_State* state = engineService_->GetLuaState(); + if (!state) { + throw std::runtime_error("Lua state is not initialized"); + } + return state; } } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/gui_script_service.hpp b/src/services/impl/gui_script_service.hpp index 2646264..035fbb0 100644 --- a/src/services/impl/gui_script_service.hpp +++ b/src/services/impl/gui_script_service.hpp @@ -2,23 +2,42 @@ #include "../interfaces/i_gui_script_service.hpp" #include "../interfaces/i_script_engine_service.hpp" +#include "../interfaces/i_logger.hpp" +#include "../../di/lifecycle.hpp" #include +#include + +struct lua_State; namespace sdl3cpp::services::impl { /** * @brief Script-facing GUI service implementation. */ -class GuiScriptService : public IGuiScriptService { +class GuiScriptService : public IGuiScriptService, + public di::IInitializable, + public di::IShutdownable { public: - explicit GuiScriptService(std::shared_ptr engineService); + GuiScriptService(std::shared_ptr engineService, + std::shared_ptr logger); - std::vector LoadGuiCommands() override; - void UpdateGuiInput(const script::GuiInputSnapshot& input) override; + void Initialize() override; + void Shutdown() noexcept override; + + std::vector LoadGuiCommands() override; + void UpdateGuiInput(const GuiInputSnapshot& input) override; bool HasGuiCommands() const override; private: + lua_State* GetLuaState() const; + GuiCommand::RectData ReadRect(lua_State* L, int index) const; + GuiColor ReadColor(lua_State* L, int index, const GuiColor& defaultColor) const; + bool ReadStringField(lua_State* L, int index, const char* name, std::string& outString) const; + std::shared_ptr engineService_; + std::shared_ptr logger_; + int guiInputRef_ = -1; + int guiCommandsFnRef_ = -1; }; } // namespace sdl3cpp::services::impl diff --git a/src/script/lua_helpers.cpp b/src/services/impl/lua_helpers.cpp similarity index 95% rename from src/script/lua_helpers.cpp rename to src/services/impl/lua_helpers.cpp index 8f25574..d477779 100644 --- a/src/script/lua_helpers.cpp +++ b/src/services/impl/lua_helpers.cpp @@ -1,4 +1,4 @@ -#include "script/lua_helpers.hpp" +#include "lua_helpers.hpp" #include #include @@ -7,7 +7,7 @@ #include #include -namespace sdl3cpp::script { +namespace sdl3cpp::services::impl::lua { std::array ReadVector3(lua_State* L, int index) { std::array result{}; @@ -78,6 +78,8 @@ std::array IdentityMatrix() { 0.0f, 0.0f, 0.0f, 1.0f}; } +namespace { + glm::vec3 ToVec3(const std::array& value) { return glm::vec3(value[0], value[1], value[2]); } @@ -95,6 +97,8 @@ void PushMatrix(lua_State* L, const glm::mat4& matrix) { } } +} // namespace + int LuaGlmMatrixFromTransform(lua_State* L) { std::array translation = ReadVector3(L, 1); std::array rotation = ReadQuaternion(L, 2); @@ -105,4 +109,4 @@ int LuaGlmMatrixFromTransform(lua_State* L) { return 1; } -} // namespace sdl3cpp::script +} // namespace sdl3cpp::services::impl::lua diff --git a/src/script/lua_helpers.hpp b/src/services/impl/lua_helpers.hpp similarity index 66% rename from src/script/lua_helpers.hpp rename to src/services/impl/lua_helpers.hpp index cba590b..ad18666 100644 --- a/src/script/lua_helpers.hpp +++ b/src/services/impl/lua_helpers.hpp @@ -1,12 +1,11 @@ -#ifndef SDL3CPP_SCRIPT_LUA_HELPERS_HPP -#define SDL3CPP_SCRIPT_LUA_HELPERS_HPP +#pragma once #include #include struct lua_State; -namespace sdl3cpp::script { +namespace sdl3cpp::services::impl::lua { std::array ReadVector3(lua_State* L, int index); std::array ReadQuaternion(lua_State* L, int index); @@ -15,6 +14,4 @@ std::string GetLuaError(lua_State* L); std::array IdentityMatrix(); int LuaGlmMatrixFromTransform(lua_State* L); -} // namespace sdl3cpp::script - -#endif // SDL3CPP_SCRIPT_LUA_HELPERS_HPP +} // namespace sdl3cpp::services::impl::lua diff --git a/src/services/impl/mesh_service.cpp b/src/services/impl/mesh_service.cpp index d5ab1d8..9a4e525 100644 --- a/src/services/impl/mesh_service.cpp +++ b/src/services/impl/mesh_service.cpp @@ -16,7 +16,7 @@ MeshService::MeshService(std::shared_ptr configService) } bool MeshService::LoadFromFile(const std::string& requestedPath, - script::MeshPayload& outPayload, + MeshPayload& outPayload, std::string& outError) { if (!configService_) { outError = "Config service not available"; @@ -109,7 +109,7 @@ bool MeshService::LoadFromFile(const std::string& requestedPath, return true; } -void MeshService::PushMeshToLua(lua_State* L, const script::MeshPayload& payload) { +void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) { lua_newtable(L); lua_newtable(L); diff --git a/src/services/impl/mesh_service.hpp b/src/services/impl/mesh_service.hpp index 10ee895..b7a1aa6 100644 --- a/src/services/impl/mesh_service.hpp +++ b/src/services/impl/mesh_service.hpp @@ -14,9 +14,9 @@ public: explicit MeshService(std::shared_ptr configService); bool LoadFromFile(const std::string& requestedPath, - script::MeshPayload& outPayload, + MeshPayload& outPayload, std::string& outError) override; - void PushMeshToLua(lua_State* L, const script::MeshPayload& payload) override; + void PushMeshToLua(lua_State* L, const MeshPayload& payload) override; private: std::shared_ptr configService_; diff --git a/src/services/impl/scene_script_service.cpp b/src/services/impl/scene_script_service.cpp index 5cf106d..1a78cf8 100644 --- a/src/services/impl/scene_script_service.cpp +++ b/src/services/impl/scene_script_service.cpp @@ -1,22 +1,275 @@ #include "scene_script_service.hpp" + +#include "lua_helpers.hpp" +#include "services/interfaces/i_logger.hpp" + +#include + +#include +#include +#include #include +#include namespace sdl3cpp::services::impl { +namespace { -SceneScriptService::SceneScriptService(std::shared_ptr engineService) - : engineService_(std::move(engineService)) { +std::vector ReadVertexArray(lua_State* L, int index, const std::shared_ptr& logger) { + int absIndex = lua_absindex(L, index); + if (!lua_istable(L, absIndex)) { + if (logger) { + logger->Error("Expected table for vertex data"); + } + throw std::runtime_error("Expected table for vertex data"); + } + + size_t count = lua_rawlen(L, absIndex); + std::vector vertices; + vertices.reserve(count); + + for (size_t i = 1; i <= count; ++i) { + lua_rawgeti(L, absIndex, static_cast(i)); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger) { + logger->Error("Vertex entry at index " + std::to_string(i) + " is not a table"); + } + throw std::runtime_error("Vertex entry at index " + std::to_string(i) + " is not a table"); + } + + int vertexIndex = lua_gettop(L); + core::Vertex vertex{}; + + lua_getfield(L, vertexIndex, "position"); + vertex.position = lua::ReadVector3(L, -1); + lua_pop(L, 1); + + lua_getfield(L, vertexIndex, "color"); + vertex.color = lua::ReadVector3(L, -1); + lua_pop(L, 1); + + lua_pop(L, 1); + vertices.push_back(vertex); + } + + return vertices; } -std::vector SceneScriptService::LoadSceneObjects() { - return engineService_->GetEngine().LoadSceneObjects(); +std::vector ReadIndexArray(lua_State* L, int index, const std::shared_ptr& logger) { + int absIndex = lua_absindex(L, index); + if (!lua_istable(L, absIndex)) { + if (logger) { + logger->Error("Expected table for index data"); + } + throw std::runtime_error("Expected table for index data"); + } + + size_t count = lua_rawlen(L, absIndex); + std::vector indices; + indices.reserve(count); + + for (size_t i = 1; i <= count; ++i) { + lua_rawgeti(L, absIndex, static_cast(i)); + if (!lua_isinteger(L, -1)) { + lua_pop(L, 1); + if (logger) { + logger->Error("Index entry at position " + std::to_string(i) + " is not an integer"); + } + throw std::runtime_error("Index entry at position " + std::to_string(i) + " is not an integer"); + } + lua_Integer value = lua_tointeger(L, -1); + lua_pop(L, 1); + if (value < 1) { + if (logger) { + logger->Error("Index values must be 1 or greater"); + } + throw std::runtime_error("Index values must be 1 or greater"); + } + indices.push_back(static_cast(value - 1)); + } + + return indices; +} + +} // namespace + +SceneScriptService::SceneScriptService(std::shared_ptr engineService, + std::shared_ptr logger) + : engineService_(std::move(engineService)), + logger_(std::move(logger)) { +} + +std::vector SceneScriptService::LoadSceneObjects() { + if (logger_) { + logger_->Trace("SceneScriptService", "LoadSceneObjects"); + } + lua_State* L = GetLuaState(); + + lua_getglobal(L, "get_scene_objects"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua function 'get_scene_objects' is missing"); + } + throw std::runtime_error("Lua function 'get_scene_objects' is missing"); + } + if (lua_pcall(L, 0, 1, 0) != LUA_OK) { + std::string message = lua::GetLuaError(L); + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua get_scene_objects failed: " + message); + } + throw std::runtime_error("Lua get_scene_objects failed: " + message); + } + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("'get_scene_objects' did not return a table"); + } + throw std::runtime_error("'get_scene_objects' did not return a table"); + } + + size_t count = lua_rawlen(L, -1); + std::vector objects; + objects.reserve(count); + + for (size_t i = 1; i <= count; ++i) { + lua_rawgeti(L, -1, static_cast(i)); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Scene object at index " + std::to_string(i) + " is not a table"); + } + throw std::runtime_error("Scene object at index " + std::to_string(i) + " is not a table"); + } + + SceneObject object; + lua_getfield(L, -1, "vertices"); + object.vertices = ReadVertexArray(L, -1, logger_); + lua_pop(L, 1); + if (object.vertices.empty()) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Scene object " + std::to_string(i) + " must supply at least one vertex"); + } + throw std::runtime_error("Scene object " + std::to_string(i) + " must supply at least one vertex"); + } + + lua_getfield(L, -1, "indices"); + object.indices = ReadIndexArray(L, -1, logger_); + lua_pop(L, 1); + if (object.indices.empty()) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Scene object " + std::to_string(i) + " must supply indices"); + } + throw std::runtime_error("Scene object " + std::to_string(i) + " must supply indices"); + } + + lua_getfield(L, -1, "compute_model_matrix"); + if (lua_isfunction(L, -1)) { + object.computeModelMatrixRef = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + object.computeModelMatrixRef = -1; + } + + lua_getfield(L, -1, "shader_key"); + if (lua_isstring(L, -1)) { + object.shaderKey = lua_tostring(L, -1); + } + lua_pop(L, 1); + + objects.push_back(std::move(object)); + lua_pop(L, 1); + } + + lua_pop(L, 1); + return objects; } std::array SceneScriptService::ComputeModelMatrix(int functionRef, float time) { - return engineService_->GetEngine().ComputeModelMatrix(functionRef, time); + if (logger_) { + logger_->Trace("SceneScriptService", "ComputeModelMatrix", "time=" + std::to_string(time)); + } + lua_State* L = GetLuaState(); + + if (functionRef < 0) { + lua_getglobal(L, "compute_model_matrix"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 1); + return lua::IdentityMatrix(); + } + } else { + lua_rawgeti(L, LUA_REGISTRYINDEX, functionRef); + } + + lua_pushnumber(L, time); + if (lua_pcall(L, 1, 1, 0) != LUA_OK) { + std::string message = lua::GetLuaError(L); + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua compute_model_matrix failed: " + message); + } + throw std::runtime_error("Lua compute_model_matrix failed: " + message); + } + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("'compute_model_matrix' did not return a table"); + } + throw std::runtime_error("'compute_model_matrix' did not return a table"); + } + + std::array matrix = lua::ReadMatrix(L, -1); + lua_pop(L, 1); + return matrix; } std::array SceneScriptService::GetViewProjectionMatrix(float aspect) { - return engineService_->GetEngine().GetViewProjectionMatrix(aspect); + if (logger_) { + logger_->Trace("SceneScriptService", "GetViewProjectionMatrix", "aspect=" + std::to_string(aspect)); + } + lua_State* L = GetLuaState(); + + lua_getglobal(L, "get_view_projection"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua function 'get_view_projection' is missing"); + } + throw std::runtime_error("Lua function 'get_view_projection' is missing"); + } + lua_pushnumber(L, aspect); + if (lua_pcall(L, 1, 1, 0) != LUA_OK) { + std::string message = lua::GetLuaError(L); + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua get_view_projection failed: " + message); + } + throw std::runtime_error("Lua get_view_projection failed: " + message); + } + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("'get_view_projection' did not return a table"); + } + throw std::runtime_error("'get_view_projection' did not return a table"); + } + std::array matrix = lua::ReadMatrix(L, -1); + lua_pop(L, 1); + return matrix; +} + +lua_State* SceneScriptService::GetLuaState() const { + if (!engineService_) { + throw std::runtime_error("Scene script service is missing script engine service"); + } + lua_State* state = engineService_->GetLuaState(); + if (!state) { + throw std::runtime_error("Lua state is not initialized"); + } + return state; } } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/scene_script_service.hpp b/src/services/impl/scene_script_service.hpp index ec4a049..963350f 100644 --- a/src/services/impl/scene_script_service.hpp +++ b/src/services/impl/scene_script_service.hpp @@ -2,8 +2,11 @@ #include "../interfaces/i_scene_script_service.hpp" #include "../interfaces/i_script_engine_service.hpp" +#include "../interfaces/i_logger.hpp" #include +struct lua_State; + namespace sdl3cpp::services::impl { /** @@ -11,14 +14,18 @@ namespace sdl3cpp::services::impl { */ class SceneScriptService : public ISceneScriptService { public: - explicit SceneScriptService(std::shared_ptr engineService); + SceneScriptService(std::shared_ptr engineService, + std::shared_ptr logger); - std::vector LoadSceneObjects() override; + std::vector LoadSceneObjects() override; std::array ComputeModelMatrix(int functionRef, float time) override; std::array GetViewProjectionMatrix(float aspect) override; private: + lua_State* GetLuaState() const; + std::shared_ptr engineService_; + std::shared_ptr logger_; }; } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/script_engine_service.cpp b/src/services/impl/script_engine_service.cpp index 96b6ec3..d722d5e 100644 --- a/src/services/impl/script_engine_service.cpp +++ b/src/services/impl/script_engine_service.cpp @@ -1,8 +1,14 @@ #include "script_engine_service.hpp" -#include "../../script/lua_bindings.hpp" -#include "../../script/script_engine.hpp" +#include "lua_helpers.hpp" +#include "services/interfaces/i_logger.hpp" + +#include +#include + +#include #include +#include #include namespace sdl3cpp::services::impl { @@ -33,15 +39,53 @@ void ScriptEngineService::Initialize() { } logger_->TraceFunction(__func__); - bindingContext_ = std::make_shared(); + bindingContext_ = std::make_shared(); bindingContext_->meshService = meshService_; bindingContext_->audioCommandService = audioCommandService_; bindingContext_->physicsBridgeService = physicsBridgeService_; bindingContext_->logger = logger_; - engine_ = std::make_unique(scriptPath_, bindingContext_.get(), debugEnabled_); - initialized_ = true; + luaState_ = luaL_newstate(); + if (!luaState_) { + logger_->Error("Failed to create Lua state"); + throw std::runtime_error("Failed to create Lua state"); + } + luaL_openlibs(luaState_); + RegisterBindings(luaState_); + + lua_pushboolean(luaState_, debugEnabled_); + lua_setglobal(luaState_, "lua_debug"); + + scriptDirectory_ = scriptPath_.parent_path(); + if (!scriptDirectory_.empty()) { + lua_getglobal(luaState_, "package"); + if (lua_istable(luaState_, -1)) { + lua_getfield(luaState_, -1, "path"); + const char* currentPath = lua_tostring(luaState_, -1); + std::string newPath = scriptDirectory_.string() + "/?.lua;"; + if (currentPath) { + newPath += currentPath; + } + lua_pop(luaState_, 1); + lua_pushstring(luaState_, newPath.c_str()); + lua_setfield(luaState_, -2, "path"); + } + lua_pop(luaState_, 1); + } + + if (luaL_dofile(luaState_, scriptPath_.string().c_str()) != LUA_OK) { + std::string message = lua::GetLuaError(luaState_); + lua_pop(luaState_, 1); + lua_close(luaState_); + luaState_ = nullptr; + if (logger_) { + logger_->Error("Failed to load Lua script: " + message); + } + throw std::runtime_error("Failed to load Lua script: " + message); + } + + initialized_ = true; logger_->Info("Script engine service initialized"); } @@ -51,25 +95,254 @@ void ScriptEngineService::Shutdown() noexcept { } logger_->TraceFunction(__func__); - engine_.reset(); + if (luaState_) { + lua_close(luaState_); + luaState_ = nullptr; + } bindingContext_.reset(); initialized_ = false; logger_->Info("Script engine service shutdown"); } -script::ScriptEngine& ScriptEngineService::GetEngine() { - if (!engine_) { +lua_State* ScriptEngineService::GetLuaState() const { + if (!luaState_) { throw std::runtime_error("Script engine service not initialized"); } - return *engine_; + return luaState_; } std::filesystem::path ScriptEngineService::GetScriptDirectory() const { - if (!engine_) { + if (!luaState_) { throw std::runtime_error("Script engine service not initialized"); } - return engine_->GetScriptDirectory(); + return scriptDirectory_; +} + +void ScriptEngineService::RegisterBindings(lua_State* L) { + if (logger_) { + logger_->Trace("ScriptEngineService", "RegisterBindings"); + } + if (!bindingContext_) { + throw std::runtime_error("Lua binding context not initialized"); + } + + auto bind = [&](const char* name, lua_CFunction fn) { + lua_pushlightuserdata(L, bindingContext_.get()); + lua_pushcclosure(L, fn, 1); + lua_setglobal(L, name); + }; + + bind("load_mesh_from_file", &ScriptEngineService::LoadMeshFromFile); + bind("physics_create_box", &ScriptEngineService::PhysicsCreateBox); + bind("physics_step_simulation", &ScriptEngineService::PhysicsStepSimulation); + bind("physics_get_transform", &ScriptEngineService::PhysicsGetTransform); + bind("glm_matrix_from_transform", &ScriptEngineService::GlmMatrixFromTransform); + bind("audio_play_background", &ScriptEngineService::AudioPlayBackground); + bind("audio_play_sound", &ScriptEngineService::AudioPlaySound); +} + +int ScriptEngineService::LoadMeshFromFile(lua_State* L) { + auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + auto logger = context ? context->logger : nullptr; + if (!context || !context->meshService) { + lua_pushnil(L); + lua_pushstring(L, "Mesh service not available"); + return 2; + } + + const char* path = luaL_checkstring(L, 1); + if (logger) { + logger->Trace("LuaBindings", "LoadMeshFromFile"); + logger->TraceVariable("path", std::string(path)); + } + + MeshPayload payload; + std::string error; + if (!context->meshService->LoadFromFile(path, payload, error)) { + lua_pushnil(L); + lua_pushstring(L, error.c_str()); + return 2; + } + + context->meshService->PushMeshToLua(L, payload); + lua_pushnil(L); + return 2; +} + +int ScriptEngineService::PhysicsCreateBox(lua_State* L) { + auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + auto logger = context ? context->logger : nullptr; + if (!context || !context->physicsBridgeService) { + lua_pushnil(L); + lua_pushstring(L, "Physics service not available"); + return 2; + } + + const char* name = luaL_checkstring(L, 1); + if (logger) { + logger->Trace("LuaBindings", "PhysicsCreateBox"); + logger->TraceVariable("name", std::string(name)); + } + + if (!lua_istable(L, 2) || !lua_istable(L, 4) || !lua_istable(L, 5)) { + luaL_error(L, "physics_create_box expects vector tables for half extents, origin, and rotation"); + } + + std::array halfExtents = lua::ReadVector3(L, 2); + float mass = static_cast(luaL_checknumber(L, 3)); + std::array origin = lua::ReadVector3(L, 4); + std::array rotation = lua::ReadQuaternion(L, 5); + + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(origin[0], origin[1], origin[2])); + transform.setRotation(btQuaternion(rotation[0], rotation[1], rotation[2], rotation[3])); + + std::string error; + if (!context->physicsBridgeService->AddBoxRigidBody( + name, + btVector3(halfExtents[0], halfExtents[1], halfExtents[2]), + mass, + transform, + error)) { + lua_pushnil(L); + lua_pushstring(L, error.c_str()); + return 2; + } + + lua_pushboolean(L, 1); + return 1; +} + +int ScriptEngineService::PhysicsStepSimulation(lua_State* L) { + auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + auto logger = context ? context->logger : nullptr; + if (!context || !context->physicsBridgeService) { + lua_pushinteger(L, 0); + return 1; + } + if (logger) { + logger->Trace("LuaBindings", "PhysicsStepSimulation"); + } + float deltaTime = static_cast(luaL_checknumber(L, 1)); + int steps = context->physicsBridgeService->StepSimulation(deltaTime); + lua_pushinteger(L, steps); + return 1; +} + +int ScriptEngineService::PhysicsGetTransform(lua_State* L) { + auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + auto logger = context ? context->logger : nullptr; + if (!context || !context->physicsBridgeService) { + lua_pushnil(L); + lua_pushstring(L, "Physics service not available"); + return 2; + } + const char* name = luaL_checkstring(L, 1); + if (logger) { + logger->Trace("LuaBindings", "PhysicsGetTransform"); + logger->TraceVariable("name", std::string(name)); + } + + btTransform transform; + std::string error; + if (!context->physicsBridgeService->GetRigidBodyTransform(name, transform, error)) { + lua_pushnil(L); + lua_pushstring(L, error.c_str()); + return 2; + } + + lua_newtable(L); + lua_newtable(L); + const btVector3& origin = transform.getOrigin(); + lua_pushnumber(L, origin.x()); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, origin.y()); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, origin.z()); + lua_rawseti(L, -2, 3); + lua_setfield(L, -2, "position"); + + lua_newtable(L); + const btQuaternion& orientation = transform.getRotation(); + lua_pushnumber(L, orientation.x()); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, orientation.y()); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, orientation.z()); + lua_rawseti(L, -2, 3); + lua_pushnumber(L, orientation.w()); + lua_rawseti(L, -2, 4); + lua_setfield(L, -2, "rotation"); + + return 1; +} + +int ScriptEngineService::AudioPlayBackground(lua_State* L) { + auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + auto logger = context ? context->logger : nullptr; + if (!context || !context->audioCommandService) { + lua_pushnil(L); + lua_pushstring(L, "Audio service not available"); + return 2; + } + const char* path = luaL_checkstring(L, 1); + bool loop = true; + if (lua_gettop(L) >= 2 && lua_isboolean(L, 2)) { + loop = lua_toboolean(L, 2); + } + if (logger) { + logger->Trace("LuaBindings", "AudioPlayBackground"); + logger->TraceVariable("path", std::string(path)); + logger->TraceVariable("loop", loop); + } + + std::string error; + if (!context->audioCommandService->QueueAudioCommand( + AudioCommandType::Background, path, loop, error)) { + lua_pushnil(L); + lua_pushstring(L, error.c_str()); + return 2; + } + + lua_pushboolean(L, 1); + return 1; +} + +int ScriptEngineService::AudioPlaySound(lua_State* L) { + auto* context = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + auto logger = context ? context->logger : nullptr; + if (!context || !context->audioCommandService) { + lua_pushnil(L); + lua_pushstring(L, "Audio service not available"); + return 2; + } + const char* path = luaL_checkstring(L, 1); + bool loop = false; + if (lua_gettop(L) >= 2 && lua_isboolean(L, 2)) { + loop = lua_toboolean(L, 2); + } + if (logger) { + logger->Trace("LuaBindings", "AudioPlaySound"); + logger->TraceVariable("path", std::string(path)); + logger->TraceVariable("loop", loop); + } + + std::string error; + if (!context->audioCommandService->QueueAudioCommand( + AudioCommandType::Effect, path, loop, error)) { + lua_pushnil(L); + lua_pushstring(L, error.c_str()); + return 2; + } + + lua_pushboolean(L, 1); + return 1; +} + +int ScriptEngineService::GlmMatrixFromTransform(lua_State* L) { + return lua::LuaGlmMatrixFromTransform(L); } } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/script_engine_service.hpp b/src/services/impl/script_engine_service.hpp index 758583d..e23e6ac 100644 --- a/src/services/impl/script_engine_service.hpp +++ b/src/services/impl/script_engine_service.hpp @@ -9,14 +9,12 @@ #include #include -namespace sdl3cpp::script { -struct LuaBindingContext; -} +struct lua_State; namespace sdl3cpp::services::impl { /** - * @brief Service wrapper around ScriptEngine. + * @brief Service that owns the Lua runtime and script bindings. */ class ScriptEngineService : public IScriptEngineService, public di::IInitializable, @@ -35,20 +33,37 @@ public: void Shutdown() noexcept override; // IScriptEngineService interface - script::ScriptEngine& GetEngine() override; + lua_State* GetLuaState() const override; std::filesystem::path GetScriptDirectory() const override; bool IsInitialized() const override { return initialized_; } private: + struct LuaBindingContext { + std::shared_ptr meshService; + std::shared_ptr audioCommandService; + std::shared_ptr physicsBridgeService; + std::shared_ptr logger; + }; + + void RegisterBindings(lua_State* L); + static int LoadMeshFromFile(lua_State* L); + static int PhysicsCreateBox(lua_State* L); + static int PhysicsStepSimulation(lua_State* L); + static int PhysicsGetTransform(lua_State* L); + static int AudioPlayBackground(lua_State* L); + static int AudioPlaySound(lua_State* L); + static int GlmMatrixFromTransform(lua_State* L); + std::shared_ptr logger_; std::shared_ptr meshService_; std::shared_ptr audioCommandService_; std::shared_ptr physicsBridgeService_; std::filesystem::path scriptPath_; + std::filesystem::path scriptDirectory_; bool debugEnabled_ = false; bool initialized_ = false; - std::unique_ptr engine_; - std::shared_ptr bindingContext_; + lua_State* luaState_ = nullptr; + std::shared_ptr bindingContext_; }; } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/sdl_input_service.hpp b/src/services/impl/sdl_input_service.hpp index 452a6f7..b094a6a 100644 --- a/src/services/impl/sdl_input_service.hpp +++ b/src/services/impl/sdl_input_service.hpp @@ -39,7 +39,7 @@ private: std::shared_ptr eventBus_; std::shared_ptr logger_; InputState state_; - script::GuiInputSnapshot guiInputSnapshot_; + GuiInputSnapshot guiInputSnapshot_; IGuiScriptService* guiScriptService_ = nullptr; // Event bus listeners diff --git a/src/services/impl/shader_script_service.cpp b/src/services/impl/shader_script_service.cpp index 5493f00..d78126d 100644 --- a/src/services/impl/shader_script_service.cpp +++ b/src/services/impl/shader_script_service.cpp @@ -1,14 +1,111 @@ #include "shader_script_service.hpp" + +#include "lua_helpers.hpp" +#include "services/interfaces/i_logger.hpp" + +#include + +#include +#include +#include #include namespace sdl3cpp::services::impl { -ShaderScriptService::ShaderScriptService(std::shared_ptr engineService) - : engineService_(std::move(engineService)) { +ShaderScriptService::ShaderScriptService(std::shared_ptr engineService, + std::shared_ptr logger) + : engineService_(std::move(engineService)), + logger_(std::move(logger)) { } std::unordered_map ShaderScriptService::LoadShaderPathsMap() { - return engineService_->GetEngine().LoadShaderPathsMap(); + if (logger_) { + logger_->Trace("ShaderScriptService", "LoadShaderPathsMap"); + } + lua_State* L = GetLuaState(); + + lua_getglobal(L, "get_shader_paths"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua function 'get_shader_paths' is missing"); + } + throw std::runtime_error("Lua function 'get_shader_paths' is missing"); + } + if (lua_pcall(L, 0, 1, 0) != LUA_OK) { + std::string message = lua::GetLuaError(L); + lua_pop(L, 1); + if (logger_) { + logger_->Error("Lua get_shader_paths failed: " + message); + } + throw std::runtime_error("Lua get_shader_paths failed: " + message); + } + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("'get_shader_paths' did not return a table"); + } + throw std::runtime_error("'get_shader_paths' did not return a table"); + } + + std::unordered_map shaderMap; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_isstring(L, -2) && lua_istable(L, -1)) { + std::string key = lua_tostring(L, -2); + shaderMap.emplace(key, ReadShaderPathsTable(L, -1)); + } + lua_pop(L, 1); + } + + lua_pop(L, 1); + if (shaderMap.empty()) { + if (logger_) { + logger_->Error("'get_shader_paths' did not return any shader variants"); + } + throw std::runtime_error("'get_shader_paths' did not return any shader variants"); + } + return shaderMap; +} + +ShaderPaths ShaderScriptService::ReadShaderPathsTable(lua_State* L, int index) const { + ShaderPaths paths; + int absIndex = lua_absindex(L, index); + + lua_getfield(L, absIndex, "vertex"); + if (!lua_isstring(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Shader path 'vertex' must be a string"); + } + throw std::runtime_error("Shader path 'vertex' must be a string"); + } + paths.vertex = lua_tostring(L, -1); + lua_pop(L, 1); + + lua_getfield(L, absIndex, "fragment"); + if (!lua_isstring(L, -1)) { + lua_pop(L, 1); + if (logger_) { + logger_->Error("Shader path 'fragment' must be a string"); + } + throw std::runtime_error("Shader path 'fragment' must be a string"); + } + paths.fragment = lua_tostring(L, -1); + lua_pop(L, 1); + + return paths; +} + +lua_State* ShaderScriptService::GetLuaState() const { + if (!engineService_) { + throw std::runtime_error("Shader script service is missing script engine service"); + } + lua_State* state = engineService_->GetLuaState(); + if (!state) { + throw std::runtime_error("Lua state is not initialized"); + } + return state; } } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/shader_script_service.hpp b/src/services/impl/shader_script_service.hpp index da376a3..d40887e 100644 --- a/src/services/impl/shader_script_service.hpp +++ b/src/services/impl/shader_script_service.hpp @@ -2,8 +2,11 @@ #include "../interfaces/i_shader_script_service.hpp" #include "../interfaces/i_script_engine_service.hpp" +#include "../interfaces/i_logger.hpp" #include +struct lua_State; + namespace sdl3cpp::services::impl { /** @@ -11,12 +14,17 @@ namespace sdl3cpp::services::impl { */ class ShaderScriptService : public IShaderScriptService { public: - explicit ShaderScriptService(std::shared_ptr engineService); + ShaderScriptService(std::shared_ptr engineService, + std::shared_ptr logger); std::unordered_map LoadShaderPathsMap() override; private: + lua_State* GetLuaState() const; + ShaderPaths ReadShaderPathsTable(lua_State* L, int index) const; + std::shared_ptr engineService_; + std::shared_ptr logger_; }; } // namespace sdl3cpp::services::impl diff --git a/src/script/gui_types.hpp b/src/services/interfaces/gui_types.hpp similarity index 72% rename from src/script/gui_types.hpp rename to src/services/interfaces/gui_types.hpp index b2da88e..125b4b9 100644 --- a/src/script/gui_types.hpp +++ b/src/services/interfaces/gui_types.hpp @@ -1,10 +1,9 @@ -#ifndef SDL3CPP_SCRIPT_GUI_TYPES_HPP -#define SDL3CPP_SCRIPT_GUI_TYPES_HPP +#pragma once #include #include -namespace sdl3cpp::script { +namespace sdl3cpp::services { struct GuiInputSnapshot { float mouseX = 0.0f; @@ -16,9 +15,9 @@ struct GuiInputSnapshot { }; struct GuiColor { - float r = 0; - float g = 0; - float b = 0; + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; float a = 1.0f; }; @@ -32,10 +31,10 @@ struct GuiCommand { }; struct RectData { - float x = 0; - float y = 0; - float width = 0; - float height = 0; + float x = 0.0f; + float y = 0.0f; + float width = 0.0f; + float height = 0.0f; }; Type type = Type::Rect; @@ -55,6 +54,4 @@ struct GuiCommand { bool hasBounds = false; }; -} // namespace sdl3cpp::script - -#endif // SDL3CPP_SCRIPT_GUI_TYPES_HPP +} // namespace sdl3cpp::services diff --git a/src/services/interfaces/i_gui_renderer_service.hpp b/src/services/interfaces/i_gui_renderer_service.hpp index 941e7d5..f755c7a 100644 --- a/src/services/interfaces/i_gui_renderer_service.hpp +++ b/src/services/interfaces/i_gui_renderer_service.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../script/gui_types.hpp" +#include "gui_types.hpp" #include #include #include @@ -16,7 +16,7 @@ public: VkFormat format, const std::filesystem::path& resourcePath) = 0; - virtual void PrepareFrame(const std::vector& commands, + virtual void PrepareFrame(const std::vector& commands, uint32_t width, uint32_t height) = 0; diff --git a/src/services/interfaces/i_gui_script_service.hpp b/src/services/interfaces/i_gui_script_service.hpp index 9603c13..2b20a1f 100644 --- a/src/services/interfaces/i_gui_script_service.hpp +++ b/src/services/interfaces/i_gui_script_service.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../script/gui_types.hpp" +#include "gui_types.hpp" #include namespace sdl3cpp::services { @@ -12,8 +12,8 @@ class IGuiScriptService { public: virtual ~IGuiScriptService() = default; - virtual std::vector LoadGuiCommands() = 0; - virtual void UpdateGuiInput(const script::GuiInputSnapshot& input) = 0; + virtual std::vector LoadGuiCommands() = 0; + virtual void UpdateGuiInput(const GuiInputSnapshot& input) = 0; virtual bool HasGuiCommands() const = 0; }; diff --git a/src/services/interfaces/i_input_service.hpp b/src/services/interfaces/i_input_service.hpp index 0464bef..ab5664c 100644 --- a/src/services/interfaces/i_input_service.hpp +++ b/src/services/interfaces/i_input_service.hpp @@ -3,7 +3,6 @@ #include #include #include -#include "../../script/gui_types.hpp" namespace sdl3cpp::services { diff --git a/src/services/interfaces/i_mesh_service.hpp b/src/services/interfaces/i_mesh_service.hpp index 072eb25..398d4a4 100644 --- a/src/services/interfaces/i_mesh_service.hpp +++ b/src/services/interfaces/i_mesh_service.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../script/mesh_payload.hpp" +#include "mesh_types.hpp" #include struct lua_State; @@ -15,9 +15,9 @@ public: virtual ~IMeshService() = default; virtual bool LoadFromFile(const std::string& requestedPath, - script::MeshPayload& outPayload, + MeshPayload& outPayload, std::string& outError) = 0; - virtual void PushMeshToLua(lua_State* L, const script::MeshPayload& payload) = 0; + virtual void PushMeshToLua(lua_State* L, const MeshPayload& payload) = 0; }; } // namespace sdl3cpp::services diff --git a/src/services/interfaces/i_scene_script_service.hpp b/src/services/interfaces/i_scene_script_service.hpp index 8963067..8b50957 100644 --- a/src/services/interfaces/i_scene_script_service.hpp +++ b/src/services/interfaces/i_scene_script_service.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../script/scene_manager.hpp" +#include "scene_types.hpp" #include #include @@ -13,7 +13,7 @@ class ISceneScriptService { public: virtual ~ISceneScriptService() = default; - virtual std::vector LoadSceneObjects() = 0; + virtual std::vector LoadSceneObjects() = 0; virtual std::array ComputeModelMatrix(int functionRef, float time) = 0; virtual std::array GetViewProjectionMatrix(float aspect) = 0; }; diff --git a/src/services/interfaces/i_scene_service.hpp b/src/services/interfaces/i_scene_service.hpp index 4cd5511..b3ecaf2 100644 --- a/src/services/interfaces/i_scene_service.hpp +++ b/src/services/interfaces/i_scene_service.hpp @@ -1,14 +1,11 @@ #pragma once #include "i_graphics_service.hpp" -#include "../../script/scene_manager.hpp" +#include "scene_types.hpp" #include namespace sdl3cpp::services { -// Forward declare or use the type from script -using SceneObject = script::SceneManager::SceneObject; - /** * @brief Scene management service interface. * diff --git a/src/services/interfaces/i_script_engine_service.hpp b/src/services/interfaces/i_script_engine_service.hpp index 28897a7..22fef3b 100644 --- a/src/services/interfaces/i_script_engine_service.hpp +++ b/src/services/interfaces/i_script_engine_service.hpp @@ -2,9 +2,7 @@ #include -namespace sdl3cpp::script { -class ScriptEngine; -} +struct lua_State; namespace sdl3cpp::services { @@ -15,7 +13,7 @@ class IScriptEngineService { public: virtual ~IScriptEngineService() = default; - virtual script::ScriptEngine& GetEngine() = 0; + virtual lua_State* GetLuaState() const = 0; virtual std::filesystem::path GetScriptDirectory() const = 0; virtual bool IsInitialized() const = 0; }; diff --git a/src/services/interfaces/i_shader_service.hpp b/src/services/interfaces/i_shader_service.hpp index 3682b03..ec5ce3e 100644 --- a/src/services/interfaces/i_shader_service.hpp +++ b/src/services/interfaces/i_shader_service.hpp @@ -11,7 +11,7 @@ namespace sdl3cpp::services { * @brief Shader management service interface. * * Handles shader compilation, pipeline creation, and shader program management. - * Wraps the ShaderManager from the script module. + * Consumes shader paths from the shader script service. */ class IShaderService { public: diff --git a/src/script/mesh_payload.hpp b/src/services/interfaces/mesh_types.hpp similarity index 53% rename from src/script/mesh_payload.hpp rename to src/services/interfaces/mesh_types.hpp index 9c23671..7bdffa8 100644 --- a/src/script/mesh_payload.hpp +++ b/src/services/interfaces/mesh_types.hpp @@ -1,11 +1,10 @@ -#ifndef SDL3CPP_SCRIPT_MESH_PAYLOAD_HPP -#define SDL3CPP_SCRIPT_MESH_PAYLOAD_HPP +#pragma once #include #include #include -namespace sdl3cpp::script { +namespace sdl3cpp::services { struct MeshPayload { std::vector> positions; @@ -13,6 +12,4 @@ struct MeshPayload { std::vector indices; }; -} // namespace sdl3cpp::script - -#endif // SDL3CPP_SCRIPT_MESH_PAYLOAD_HPP +} // namespace sdl3cpp::services diff --git a/src/services/interfaces/scene_types.hpp b/src/services/interfaces/scene_types.hpp new file mode 100644 index 0000000..0529ea0 --- /dev/null +++ b/src/services/interfaces/scene_types.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "../../core/vertex.hpp" +#include +#include +#include + +namespace sdl3cpp::services { + +struct SceneObject { + std::vector vertices; + std::vector indices; + int computeModelMatrixRef = -1; + std::string shaderKey = "default"; +}; + +} // namespace sdl3cpp::services diff --git a/tests/test_cube_script.cpp b/tests/test_cube_script.cpp index 66ed854..1412501 100644 --- a/tests/test_cube_script.cpp +++ b/tests/test_cube_script.cpp @@ -1,9 +1,13 @@ -#include "script/script_engine.hpp" +#include "services/impl/logger_service.hpp" +#include "services/impl/script_engine_service.hpp" +#include "services/impl/scene_script_service.hpp" +#include "services/impl/shader_script_service.hpp" #include #include #include #include +#include #include #include @@ -50,8 +54,20 @@ int main() { std::cout << "Loading Lua fixture: " << scriptPath << '\n'; try { - sdl3cpp::script::ScriptEngine scriptEngine(scriptPath); - auto objects = scriptEngine.LoadSceneObjects(); + auto logger = std::make_shared(); + auto engineService = std::make_shared( + scriptPath, + logger, + nullptr, + nullptr, + nullptr, + false); + engineService->Initialize(); + + sdl3cpp::services::impl::SceneScriptService sceneService(engineService, logger); + sdl3cpp::services::impl::ShaderScriptService shaderService(engineService, logger); + + auto objects = sceneService.LoadSceneObjects(); Assert(objects.size() == 1, "expected exactly one scene object", failures); if (!objects.empty()) { const auto& object = objects.front(); @@ -60,24 +76,23 @@ int main() { Assert(object.shaderKey == "test", "shader key should match fixture", failures); const std::vector expectedIndices{0, 1, 2}; Assert(object.indices == expectedIndices, "indices should be zero-based", failures); - Assert(object.computeModelMatrixRef != LUA_REFNIL, + Assert(object.computeModelMatrixRef >= 0, "vertex object must keep a Lua reference", failures); - auto objectMatrix = scriptEngine.ComputeModelMatrix(object.computeModelMatrixRef, 0.5f); + auto objectMatrix = sceneService.ComputeModelMatrix(object.computeModelMatrixRef, 0.5f); ExpectIdentity(objectMatrix, "object compute_model_matrix", failures); } - auto fallbackMatrix = scriptEngine.ComputeModelMatrix(LUA_REFNIL, 1.0f); + auto fallbackMatrix = sceneService.ComputeModelMatrix(-1, 1.0f); ExpectIdentity(fallbackMatrix, "global compute_model_matrix", failures); - auto viewProjection = scriptEngine.GetViewProjectionMatrix(1.33f); + auto viewProjection = sceneService.GetViewProjectionMatrix(1.33f); ExpectIdentity(viewProjection, "view_projection matrix", failures); - auto shaderMap = scriptEngine.LoadShaderPathsMap(); + auto shaderMap = shaderService.LoadShaderPathsMap(); Assert(shaderMap.size() == 1, "expected a single shader variant", failures); auto testEntry = shaderMap.find("test"); - Assert(testEntry != shaderMap.end(), "shader map missing " - "test entry", failures); + Assert(testEntry != shaderMap.end(), "shader map missing test entry", failures); if (testEntry != shaderMap.end()) { Assert(testEntry->second.vertex == "shaders/test.vert.spv", "vertex shader path", failures); Assert(testEntry->second.fragment == "shaders/test.frag.spv", "fragment shader path", failures);