diff --git a/scripts/cube_logic.lua b/scripts/cube_logic.lua index 9421354..054af14 100644 --- a/scripts/cube_logic.lua +++ b/scripts/cube_logic.lua @@ -741,7 +741,7 @@ local function create_static_cube(position, scale, color, shader_key) vertices = vertices, indices = cube_indices, compute_model_matrix = compute_model_matrix, - shader_key = resolved_shader, + shader_keys = {resolved_shader}, } end @@ -758,7 +758,7 @@ local function create_skybox() vertices = apply_color_to_vertices(skybox_color), indices = (#cube_indices_double_sided > 0) and cube_indices_double_sided or cube_indices, compute_model_matrix = compute_model_matrix, - shader_key = "skybox", + shader_keys = {"skybox"}, } end @@ -791,7 +791,7 @@ local function create_physics_cube() vertices = apply_color_to_vertices(physics_state.cube_color), indices = (#cube_indices_double_sided > 0) and cube_indices_double_sided or cube_indices, compute_model_matrix = compute_model_matrix, - shader_key = shader_key, + shader_keys = {shader_key}, } end @@ -809,7 +809,7 @@ local function create_spinning_cube() vertices = cube_vertices, indices = (#cube_indices_double_sided > 0) and cube_indices_double_sided or cube_indices, compute_model_matrix = compute_model_matrix, - shader_key = shader_key, + shader_keys = {shader_key}, } end diff --git a/scripts/quake3_arena.lua b/scripts/quake3_arena.lua index 604f7f6..9c1fda5 100644 --- a/scripts/quake3_arena.lua +++ b/scripts/quake3_arena.lua @@ -590,7 +590,7 @@ function get_scene_objects() { vertices = map_mesh.vertices, indices = map_mesh.indices, - shader_key = map_shader_key, + shader_keys = {map_shader_key}, compute_model_matrix = function() return map_model_matrix end, diff --git a/scripts/soundboard.lua b/scripts/soundboard.lua index 9333e1d..dadf518 100644 --- a/scripts/soundboard.lua +++ b/scripts/soundboard.lua @@ -251,7 +251,7 @@ local function createCube(position) vertices = cubeVertices, indices = cubeIndices, compute_model_matrix = computeModel, - shader_key = "default", + shader_keys = {"default"}, } end diff --git a/src/services/impl/graphics_service.cpp b/src/services/impl/graphics_service.cpp index 16dc59f..872318d 100644 --- a/src/services/impl/graphics_service.cpp +++ b/src/services/impl/graphics_service.cpp @@ -155,6 +155,42 @@ void GraphicsService::RenderScene(const std::vector& commands, // Set the view-projection matrix for the frame backend_->SetViewState(viewState); + + if (commands.empty()) { + return; + } + if (!vertexBuffer_ || !indexBuffer_) { + logger_->Error("GraphicsService::RenderScene: Vertex/index buffers not uploaded"); + return; + } + + for (size_t commandIndex = 0; commandIndex < commands.size(); ++commandIndex) { + const auto& command = commands[commandIndex]; + if (command.shaderKeys.empty()) { + logger_->Error("GraphicsService::RenderScene: Render command missing shader keys"); + continue; + } + if (logger_) { + logger_->Trace("GraphicsService", "RenderScene", + "commandIndex=" + std::to_string(commandIndex) + + ", shaderKeyCount=" + std::to_string(command.shaderKeys.size())); + } + for (const auto& shaderKey : command.shaderKeys) { + auto it = pipelines_.find(shaderKey); + if (it == pipelines_.end()) { + logger_->Error("GraphicsService::RenderScene: Missing pipeline for shaderKey=" + shaderKey); + continue; + } + backend_->Draw(device_, + it->second, + vertexBuffer_, + indexBuffer_, + command.indexOffset, + command.indexCount, + command.vertexOffset, + command.modelMatrix); + } + } } bool GraphicsService::EndFrame() { diff --git a/src/services/impl/graphics_service.hpp b/src/services/impl/graphics_service.hpp index 83d473e..340ef23 100644 --- a/src/services/impl/graphics_service.hpp +++ b/src/services/impl/graphics_service.hpp @@ -52,10 +52,10 @@ private: std::shared_ptr logger_; std::shared_ptr backend_; std::shared_ptr windowService_; - GraphicsDeviceHandle device_; + GraphicsDeviceHandle device_ = nullptr; std::unordered_map pipelines_; - GraphicsBufferHandle vertexBuffer_; - GraphicsBufferHandle indexBuffer_; + GraphicsBufferHandle vertexBuffer_ = nullptr; + GraphicsBufferHandle indexBuffer_ = nullptr; // Other state bool initialized_ = false; }; diff --git a/src/services/impl/scene_script_service.cpp b/src/services/impl/scene_script_service.cpp index 226a25c..336c93b 100644 --- a/src/services/impl/scene_script_service.cpp +++ b/src/services/impl/scene_script_service.cpp @@ -237,12 +237,29 @@ std::vector SceneScriptService::LoadSceneObjects() { object.computeModelMatrixRef = -1; } - lua_getfield(L, -1, "shader_key"); - if (lua_isstring(L, -1)) { - object.shaderKey = lua_tostring(L, -1); + object.shaderKeys.clear(); + lua_getfield(L, -1, "shader_keys"); + if (lua_istable(L, -1)) { + const size_t count = lua_rawlen(L, -1); + object.shaderKeys.reserve(count); + for (size_t keyIndex = 1; keyIndex <= count; ++keyIndex) { + lua_rawgeti(L, -1, static_cast(keyIndex)); + if (lua_isstring(L, -1)) { + object.shaderKeys.emplace_back(lua_tostring(L, -1)); + } + lua_pop(L, 1); + } } lua_pop(L, 1); + if (object.shaderKeys.empty()) { + lua_getfield(L, -1, "shader_key"); + if (lua_isstring(L, -1)) { + object.shaderKeys.emplace_back(lua_tostring(L, -1)); + } + lua_pop(L, 1); + } + objects.push_back(std::move(object)); lua_pop(L, 1); } diff --git a/src/services/impl/scene_service.cpp b/src/services/impl/scene_service.cpp index 09d5d97..f6253ef 100644 --- a/src/services/impl/scene_service.cpp +++ b/src/services/impl/scene_service.cpp @@ -73,12 +73,18 @@ void SceneService::LoadScene(const std::vector& objects) { } throw std::runtime_error("Scene object missing vertex or index data"); } + if (obj.shaderKeys.empty()) { + if (logger_) { + logger_->Error("Scene object missing shader keys"); + } + throw std::runtime_error("Scene object missing shader keys"); + } auto entity = registry_->create(); sceneEntities_.push_back(entity); registry_->emplace(entity); registry_->emplace(entity, obj.vertices, obj.indices); - registry_->emplace(entity, obj.computeModelMatrixRef, obj.shaderKey); + registry_->emplace(entity, obj.computeModelMatrixRef, obj.shaderKeys); } for (const auto entity : sceneEntities_) { @@ -118,7 +124,7 @@ void SceneService::LoadScene(const std::vector& objects) { drawInfo.indexCount = static_cast(mesh.indices.size()); drawInfo.vertexOffset = static_cast(vertexOffset); drawInfo.computeModelMatrixRef = render.computeModelMatrixRef; - drawInfo.shaderKey = render.shaderKey; + drawInfo.shaderKeys = render.shaderKeys; drawInfos_.push_back(std::move(drawInfo)); } @@ -156,7 +162,7 @@ std::vector SceneService::GetRenderCommands(float time) const { cmd.indexOffset = drawInfo.indexOffset; cmd.indexCount = drawInfo.indexCount; cmd.vertexOffset = drawInfo.vertexOffset; - cmd.shaderKey = drawInfo.shaderKey; + cmd.shaderKeys = drawInfo.shaderKeys; cmd.modelMatrix = scriptService_->ComputeModelMatrix(drawInfo.computeModelMatrixRef, time); commands.push_back(cmd); } diff --git a/src/services/impl/scene_service.hpp b/src/services/impl/scene_service.hpp index 4c74f68..8f4d3cf 100644 --- a/src/services/impl/scene_service.hpp +++ b/src/services/impl/scene_service.hpp @@ -48,7 +48,7 @@ private: struct RenderComponent { int computeModelMatrixRef = -1; - std::string shaderKey; + std::vector shaderKeys; }; struct SceneDrawInfo { @@ -56,7 +56,7 @@ private: uint32_t indexCount = 0; int32_t vertexOffset = 0; int computeModelMatrixRef = -1; - std::string shaderKey; + std::vector shaderKeys; }; void ClearSceneEntities(); diff --git a/src/services/impl/shader_script_service.cpp b/src/services/impl/shader_script_service.cpp index d17d3f8..3524e06 100644 --- a/src/services/impl/shader_script_service.cpp +++ b/src/services/impl/shader_script_service.cpp @@ -34,43 +34,44 @@ std::unordered_map ShaderScriptService::LoadShaderPath if (configService_) { const auto& materialConfig = configService_->GetMaterialXConfig(); const auto& materialOverrides = configService_->GetMaterialXMaterialConfigs(); - if (!materialOverrides.empty()) { - for (const auto& overrideConfig : materialOverrides) { - if (!overrideConfig.enabled) { - continue; - } - MaterialXConfig resolvedConfig = materialConfig; - resolvedConfig.enabled = true; - resolvedConfig.documentPath = overrideConfig.documentPath; - resolvedConfig.shaderKey = overrideConfig.shaderKey; - resolvedConfig.materialName = overrideConfig.materialName; - resolvedConfig.useConstantColor = overrideConfig.useConstantColor; - resolvedConfig.constantColor = overrideConfig.constantColor; - try { - ShaderPaths materialShader = materialxGenerator_.Generate( - resolvedConfig, - engineService_ ? engineService_->GetScriptDirectory() : std::filesystem::path{}); - if (!resolvedConfig.shaderKey.empty()) { - shaderMap[resolvedConfig.shaderKey] = std::move(materialShader); - } - } catch (const std::exception& ex) { - if (logger_) { - logger_->Error("MaterialX shader generation failed for key=" + - overrideConfig.shaderKey + ": " + std::string(ex.what())); - } - } + if (materialOverrides.empty()) { + if (logger_) { + logger_->Error("MaterialX shader generation requires materialx_materials entries"); + } + throw std::runtime_error("MaterialX shader generation requires materialx_materials entries"); + } + if (logger_) { + logger_->Trace("ShaderScriptService", "LoadShaderPathsMap", + "materialOverrides=" + std::to_string(materialOverrides.size())); + } + for (const auto& overrideConfig : materialOverrides) { + if (!overrideConfig.enabled) { + continue; + } + MaterialXConfig resolvedConfig = materialConfig; + resolvedConfig.enabled = true; + resolvedConfig.documentPath = overrideConfig.documentPath; + resolvedConfig.shaderKey = overrideConfig.shaderKey; + resolvedConfig.materialName = overrideConfig.materialName; + resolvedConfig.useConstantColor = overrideConfig.useConstantColor; + resolvedConfig.constantColor = overrideConfig.constantColor; + if (logger_) { + logger_->Trace("ShaderScriptService", "LoadShaderPathsMap", + "materialKey=" + resolvedConfig.shaderKey + + ", document=" + resolvedConfig.documentPath.string() + + ", material=" + resolvedConfig.materialName); } - } else if (materialConfig.enabled) { try { ShaderPaths materialShader = materialxGenerator_.Generate( - materialConfig, + resolvedConfig, engineService_ ? engineService_->GetScriptDirectory() : std::filesystem::path{}); - if (!materialConfig.shaderKey.empty()) { - shaderMap[materialConfig.shaderKey] = std::move(materialShader); + if (!resolvedConfig.shaderKey.empty()) { + shaderMap[resolvedConfig.shaderKey] = std::move(materialShader); } } catch (const std::exception& ex) { if (logger_) { - logger_->Error("MaterialX shader generation failed: " + std::string(ex.what())); + logger_->Error("MaterialX shader generation failed for key=" + + overrideConfig.shaderKey + ": " + std::string(ex.what())); } } } diff --git a/src/services/interfaces/graphics_types.hpp b/src/services/interfaces/graphics_types.hpp index 34e3742..d7b0d1d 100644 --- a/src/services/interfaces/graphics_types.hpp +++ b/src/services/interfaces/graphics_types.hpp @@ -56,7 +56,7 @@ struct RenderCommand { uint32_t indexOffset; uint32_t indexCount; int32_t vertexOffset; - std::string shaderKey; + std::vector shaderKeys; std::array modelMatrix; }; diff --git a/src/services/interfaces/scene_types.hpp b/src/services/interfaces/scene_types.hpp index 0529ea0..e796776 100644 --- a/src/services/interfaces/scene_types.hpp +++ b/src/services/interfaces/scene_types.hpp @@ -11,7 +11,7 @@ struct SceneObject { std::vector vertices; std::vector indices; int computeModelMatrixRef = -1; - std::string shaderKey = "default"; + std::vector shaderKeys; }; } // namespace sdl3cpp::services diff --git a/tests/scripts/unit_cube_logic.lua b/tests/scripts/unit_cube_logic.lua index eb4cdfb..90c9610 100644 --- a/tests/scripts/unit_cube_logic.lua +++ b/tests/scripts/unit_cube_logic.lua @@ -19,7 +19,7 @@ function get_scene_objects() compute_model_matrix = function(time) return identity_matrix() end, - shader_key = "test", + shader_keys = {"test"}, }, } end diff --git a/tests/test_cube_script.cpp b/tests/test_cube_script.cpp index a867541..883a757 100644 --- a/tests/test_cube_script.cpp +++ b/tests/test_cube_script.cpp @@ -78,7 +78,10 @@ int main() { const auto& object = objects.front(); Assert(object.vertices.size() == 3, "scene object should yield three vertices", failures); Assert(object.indices.size() == 3, "scene object should yield three indices", failures); - Assert(object.shaderKey == "test", "shader key should match fixture", failures); + Assert(object.shaderKeys.size() == 1, "shader keys should contain one entry", failures); + if (!object.shaderKeys.empty()) { + Assert(object.shaderKeys.front() == "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 >= 0,