From a33f5bedc7f9590472bf936c1154e692b3f950af Mon Sep 17 00:00:00 2001 From: Alex Zhuohao He Date: Sun, 29 Dec 2024 15:25:11 -0500 Subject: [PATCH 1/3] add world shader commands implementation --- .../renderer/stages/world/CMakeLists.txt | 1 + .../stages/world/world_shader_commands.cpp | 73 ++++++++++++ .../stages/world/world_shader_commands.h | 108 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 libopenage/renderer/stages/world/world_shader_commands.cpp create mode 100644 libopenage/renderer/stages/world/world_shader_commands.h diff --git a/libopenage/renderer/stages/world/CMakeLists.txt b/libopenage/renderer/stages/world/CMakeLists.txt index 811fd929ed..d86d7aa44c 100644 --- a/libopenage/renderer/stages/world/CMakeLists.txt +++ b/libopenage/renderer/stages/world/CMakeLists.txt @@ -2,4 +2,5 @@ add_sources(libopenage object.cpp render_entity.cpp render_stage.cpp + world_shader_commands.cpp ) diff --git a/libopenage/renderer/stages/world/world_shader_commands.cpp b/libopenage/renderer/stages/world/world_shader_commands.cpp new file mode 100644 index 0000000000..e7b31f87c1 --- /dev/null +++ b/libopenage/renderer/stages/world/world_shader_commands.cpp @@ -0,0 +1,73 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#include "world_shader_commands.h" + +#include "error/error.h" +#include "log/log.h" + +namespace openage::renderer::world { + +bool WorldShaderCommands::add_command(uint8_t alpha, const std::string &code, const std::string &description) { + if (!validate_alpha(alpha)) { + log::log(ERR << "Invalid alpha value: " << int(alpha)); + return false; + } + if (!validate_code(code)) { + log::log(ERR << "Invalid command code"); + return false; + } + + commands_map[alpha] = {alpha, code, description}; + return true; +} + +bool WorldShaderCommands::remove_command(uint8_t alpha) { + if (!validate_alpha(alpha)) { + return false; + } + commands_map.erase(alpha); + return true; +} + +bool WorldShaderCommands::has_command(uint8_t alpha) const { + return commands_map.contains(alpha); +} + +std::string WorldShaderCommands::integrate_command(const std::string &base_shader) { + std::string final_shader = base_shader; + std::string commands_code = generate_command_code(); + + // Find the insertion point + size_t insert_point = final_shader.find(COMMAND_MARKER); + if (insert_point == std::string::npos) { + throw Error(MSG(err) << "Failed to find command insertion point in shader."); + } + + // Replace the insertion point with the generated command code + final_shader.replace(insert_point, strlen(COMMAND_MARKER), commands_code); + + return final_shader; +} + +std::string WorldShaderCommands::generate_command_code() const { + std::string result = ""; + + for (const auto &[alpha, command] : commands_map) { + result += " case " + std::to_string(alpha) + ":\n"; + result += " // " + command.description + "\n"; + result += " " + command.code + "\n"; + result += " break;\n\n"; + } + + return result; +} + +bool WorldShaderCommands::validate_alpha(uint8_t alpha) const { + return alpha % 2 == 0 && alpha >= 0 && alpha <= 254; +} + +bool WorldShaderCommands::validate_code(const std::string &code) const { + return !code.empty(); +} + +} // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/world_shader_commands.h b/libopenage/renderer/stages/world/world_shader_commands.h new file mode 100644 index 0000000000..d025735db2 --- /dev/null +++ b/libopenage/renderer/stages/world/world_shader_commands.h @@ -0,0 +1,108 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include + +namespace openage { +namespace renderer { +namespace world { + +/** + * Represents a single shader command that can be used in the world fragment shader. + * Commands are identified by their alpha values and contain GLSL code snippets + * that define custom rendering behavior. + */ +struct ShaderCommand { + // Command identifier ((must be even, range 0-254)) + uint8_t alpha; + // GLSL code snippet that defines the command's behavior + std::string code; + // Documentation (optional) + std::string description; +}; + +/** + * Manages shader commands for the world fragment shader. + * Provides functionality to add, remove, and integrate commands into the base shader. + * Commands are inserted at a predefined marker in the shader code. + */ +class WorldShaderCommands { +public: + // Marker in shader code where commands will be inserted + static constexpr const char *COMMAND_MARKER = "//@INSERT_COMMANDS@"; + + /** + * Add a new shader command. + * + * @param alpha Command identifier (must be even, range 0-254) + * @param code GLSL code snippet defining the command's behavior + * @param description Human-readable description of the command's purpose + * + * @return true if command was added successfully, false if validation failed + */ + bool add_command(uint8_t alpha, const std::string &code, const std::string &description = ""); + + /** + * Remove a command. + * + * @param alpha Command identifier (even values 0-254) + */ + bool remove_command(uint8_t alpha); + + /** + * Check if a command is registered. + * + * @param alpha Command identifier to check + * + * @return true if command is registered + */ + bool has_command(uint8_t alpha) const; + + /** + * Integrate registered commands into the base shader code. + * + * @param base_shader Original shader code containing the command marker + * + * @return Complete shader code with commands integrated at the marker position + * + * @throws Error if command marker is not found in the base shader + */ + std::string integrate_command(const std::string &base_shader); + +private: + /** + * Generate GLSL code for all registered commands. + * + * @return String containing case statements for each command + */ + std::string generate_command_code() const; + + /** + * Validate a command identifier. + * + * @param alpha Command identifier to validate + * + * @return true if alpha is even and within valid range (0-254) + */ + bool validate_alpha(uint8_t alpha) const; + + /** + * Validate command GLSL code. + * + * @param code GLSL code snippet to validate + * + * @return true if code is not empty (additional validation could be added) + */ + + bool validate_code(const std::string &code) const; + + // Map of command identifiers to their respective commands + std::map commands_map; +}; + +} // namespace world +} // namespace renderer +} // namespace openage From f69cd304db368813ff2f18da4aab15ed53898c48 Mon Sep 17 00:00:00 2001 From: Alex Zhuohao He Date: Sun, 29 Dec 2024 15:26:41 -0500 Subject: [PATCH 2/3] use world shader commands in render_stage & update world2d.frag --- assets/shaders/world2d.frag.glsl | 10 +------ .../renderer/stages/world/render_stage.cpp | 28 +++++++++++++++++-- .../renderer/stages/world/render_stage.h | 13 +++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/assets/shaders/world2d.frag.glsl b/assets/shaders/world2d.frag.glsl index 98d323e0f6..f9de0354ba 100644 --- a/assets/shaders/world2d.frag.glsl +++ b/assets/shaders/world2d.frag.glsl @@ -26,15 +26,7 @@ void main() { // do not save the ID return; - case 254: - col = vec4(1.0f, 0.0f, 0.0f, 1.0f); - break; - case 252: - col = vec4(0.0f, 1.0f, 0.0f, 1.0f); - break; - case 250: - col = vec4(0.0f, 0.0f, 1.0f, 1.0f); - break; + //@INSERT_COMMANDS@ default: col = tex_val; break; diff --git a/libopenage/renderer/stages/world/render_stage.cpp b/libopenage/renderer/stages/world/render_stage.cpp index d8d93279af..3962daa170 100644 --- a/libopenage/renderer/stages/world/render_stage.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -127,12 +127,18 @@ void WorldRenderStage::initialize_render_pass(size_t width, vert_shader_file.read()); vert_shader_file.close(); + // Initialize shader command system before loading fragment shader + this->shader_commands = std::make_unique(); + this->init_shader_commands(); + auto frag_shader_file = (shaderdir / "world2d.frag.glsl").open(); + auto base_shader = frag_shader_file.read(); + frag_shader_file.close(); + auto frag_shader_src = renderer::resources::ShaderSource( resources::shader_lang_t::glsl, resources::shader_stage_t::fragment, - frag_shader_file.read()); - frag_shader_file.close(); + this->shader_commands->integrate_command(base_shader)); this->output_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::rgba8)); this->depth_texture = renderer->add_texture(resources::Texture2dInfo(width, height, resources::pixel_format::depth24)); @@ -156,4 +162,22 @@ void WorldRenderStage::init_uniform_ids() { WorldObject::anchor_offset = this->display_shader->get_uniform_id("anchor_offset"); } +void WorldRenderStage::init_shader_commands() { + // Register default shader commands + this->shader_commands->add_command( + 254, + "col = vec4(1.0f, 0.0f, 0.0f, 1.0f);", + "Red tint command"); + this->shader_commands->add_command( + 252, + "col = vec4(0.0f, 1.0f, 0.0f, 1.0f);", + "Green tint command"); + this->shader_commands->add_command( + 250, + "col = vec4(0.0f, 0.0f, 1.0f, 1.0f);", + "Blue tint command"); + + // Additional commands can be added here +} + } // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/render_stage.h b/libopenage/renderer/stages/world/render_stage.h index f1256f3b57..f048a1cbee 100644 --- a/libopenage/renderer/stages/world/render_stage.h +++ b/libopenage/renderer/stages/world/render_stage.h @@ -7,6 +7,7 @@ #include #include "util/path.h" +#include "world_shader_commands.h" namespace openage { @@ -111,6 +112,12 @@ class WorldRenderStage { */ void init_uniform_ids(); + /** + * Initialize the shader command system and register default commands. + * This must be called before initializing the shader program. + */ + void init_shader_commands(); + /** * Reference to the openage renderer. */ @@ -174,6 +181,12 @@ class WorldRenderStage { * Mutex for protecting threaded access. */ std::shared_mutex mutex; + + /** + * Shader command system for the world fragment shader. + * Manages custom rendering behaviors through alpha channel commands. + */ + std::unique_ptr shader_commands; }; } // namespace world } // namespace renderer From 6045a96ee1e8dd3ef2f3fc4f3b29a5de460036aa Mon Sep 17 00:00:00 2001 From: Alex Zhuohao He Date: Sun, 29 Dec 2024 16:03:08 -0500 Subject: [PATCH 3/3] fix a cstring error --- libopenage/renderer/stages/world/world_shader_commands.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libopenage/renderer/stages/world/world_shader_commands.cpp b/libopenage/renderer/stages/world/world_shader_commands.cpp index e7b31f87c1..b734d8d86a 100644 --- a/libopenage/renderer/stages/world/world_shader_commands.cpp +++ b/libopenage/renderer/stages/world/world_shader_commands.cpp @@ -2,6 +2,8 @@ #include "world_shader_commands.h" +#include + #include "error/error.h" #include "log/log.h" @@ -44,7 +46,7 @@ std::string WorldShaderCommands::integrate_command(const std::string &base_shade } // Replace the insertion point with the generated command code - final_shader.replace(insert_point, strlen(COMMAND_MARKER), commands_code); + final_shader.replace(insert_point, std::strlen(COMMAND_MARKER), commands_code); return final_shader; }