Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable World Shader Commands System #1738

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions assets/shaders/world2d.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions libopenage/renderer/stages/world/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ add_sources(libopenage
object.cpp
render_entity.cpp
render_stage.cpp
world_shader_commands.cpp
)
28 changes: 26 additions & 2 deletions libopenage/renderer/stages/world/render_stage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<WorldShaderCommands>();
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));
Expand All @@ -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
13 changes: 13 additions & 0 deletions libopenage/renderer/stages/world/render_stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

#include "util/path.h"
#include "world_shader_commands.h"

namespace openage {

Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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<WorldShaderCommands> shader_commands;
};
} // namespace world
} // namespace renderer
Expand Down
75 changes: 75 additions & 0 deletions libopenage/renderer/stages/world/world_shader_commands.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2024-2024 the openage authors. See copying.md for legal info.

#include "world_shader_commands.h"

#include <cstring>

#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, std::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
108 changes: 108 additions & 0 deletions libopenage/renderer/stages/world/world_shader_commands.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2024-2024 the openage authors. See copying.md for legal info.

#pragma once

#include <map>
#include <memory>
#include <vector>

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<uint8_t, ShaderCommand> commands_map;
};

} // namespace world
} // namespace renderer
} // namespace openage
Loading