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 10 commits into
base: master
Choose a base branch
from
12 changes: 12 additions & 0 deletions assets/test/shaders/demo_7_shader_command.frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#version 330

in vec2 tex_coord;
out vec4 frag_color;

uniform float time;

void main() {
// PLACEHOLDER: color
// PLACEHOLDER: alpha
frag_color = vec4(r, g, b, alpha);
}
9 changes: 9 additions & 0 deletions assets/test/shaders/demo_7_shader_command.vert.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#version 330

in vec2 position;
out vec2 tex_coord;

void main() {
tex_coord = position.xy * 0.5 + 0.5;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
1 change: 1 addition & 0 deletions assets/test/shaders/demo_7_snippets/alpha.snippet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
float alpha = (sin(time) + 1.0) * 0.5;
3 changes: 3 additions & 0 deletions assets/test/shaders/demo_7_snippets/color.snippet
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
float r = 0.5 + 0.5 * sin(time);
float g = 0.5 + 0.5 * sin(time + 2.0);
float b = 0.5 + 0.5 * sin(time + 4.0);
1 change: 1 addition & 0 deletions libopenage/renderer/demo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_sources(libopenage
demo_4.cpp
demo_5.cpp
demo_6.cpp
demo_7.cpp
stresstest_0.cpp
stresstest_1.cpp
tests.cpp
Expand Down
85 changes: 85 additions & 0 deletions libopenage/renderer/demo/demo_7.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2025-2025 the openage authors. See copying.md for legal info.

#include "demo_7.h"

#include "util/path.h"

#include "renderer/demo/util.h"
#include "renderer/gui/integration/public/gui_application_with_logger.h"
#include "renderer/opengl/window.h"
#include "renderer/render_pass.h"
#include "renderer/render_target.h"
#include "renderer/resources/mesh_data.h"
#include "renderer/resources/shader_source.h"
#include "renderer/shader_program.h"
#include "renderer/stages/world/shader_template.h"

namespace openage::renderer::tests {

void renderer_demo_7(const util::Path &path) {
// Basic setup
auto qtapp = std::make_shared<gui::GuiApplicationWithLogger>();
window_settings settings;
settings.width = 800;
settings.height = 600;
settings.debug = true;

opengl::GlWindow window("Shader Commands Demo", settings);
auto renderer = window.make_renderer();

auto shaderdir = path / "assets" / "test" / "shaders";

// Initialize shader templlalte
world::ShaderTemplate frag_template(shaderdir / "demo_7_shader_command.frag.glsl");

// Load snippets from a snippet directory
frag_template.load_snippets(shaderdir / "demo_7_snippets");

auto vert_shader_file = (shaderdir / "demo_7_shader_command.vert.glsl").open();
auto vert_shader_src = resources::ShaderSource(
resources::shader_lang_t::glsl,
resources::shader_stage_t::vertex,
vert_shader_file.read());
vert_shader_file.close();

auto frag_shader_src = resources::ShaderSource(
resources::shader_lang_t::glsl,
resources::shader_stage_t::fragment,
frag_template.generate_source());

auto shader = renderer->add_shader({vert_shader_src, frag_shader_src});

// Create a simple quad for rendering
auto quad = renderer->add_mesh_geometry(resources::MeshData::make_quad());

auto uniforms = shader->new_uniform_input("time", 0.0f);

Renderable display_obj{
uniforms,
quad,
false,
false,
};

if (not check_uniform_completeness({display_obj})) {
log::log(WARN << "Uniforms not complete.");
}

auto pass = renderer->add_render_pass({display_obj}, renderer->get_display_target());

// Main loop
float time = 0.0f;
while (not window.should_close()) {
time += 0.016f;
uniforms->update("time", time);

renderer->render(pass);
window.update();
qtapp->process_events();

renderer->check_error();
}
window.close();
}

} // namespace openage::renderer::tests
22 changes: 22 additions & 0 deletions libopenage/renderer/demo/demo_7.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2025-2025 the openage authors. See copying.md for legal info.

#pragma once

#include "util/path.h"

namespace openage::renderer::tests {

/**
* Demonstrate the shader template system for shader generation.
* - Window creation
* - Create a shader template
* - Load shader snippets (command) from files
* - Generate shader sources from the template
* - Creating a render pass
* - Creating a renderable from a mesh
*
* @param path Path to the project rootdir.
*/
void renderer_demo_7(const util::Path &path);

} // namespace openage::renderer::tests
7 changes: 6 additions & 1 deletion libopenage/renderer/demo/tests.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015-2024 the openage authors. See copying.md for legal info.
// Copyright 2015-2025 the openage authors. See copying.md for legal info.

#include "tests.h"

Expand All @@ -12,6 +12,7 @@
#include "renderer/demo/demo_4.h"
#include "renderer/demo/demo_5.h"
#include "renderer/demo/demo_6.h"
#include "renderer/demo/demo_7.h"
#include "renderer/demo/stresstest_0.h"
#include "renderer/demo/stresstest_1.h"

Expand Down Expand Up @@ -47,6 +48,10 @@ void renderer_demo(int demo_id, const util::Path &path) {
renderer_demo_6(path);
break;

case 7:
renderer_demo_7(path);
break;

default:
log::log(MSG(err) << "Unknown renderer demo requested: " << demo_id << ".");
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
shader_template.cpp
)
65 changes: 65 additions & 0 deletions libopenage/renderer/stages/world/shader_template.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2024-2025 the openage authors. See copying.md for legal info.

#include "shader_template.h"

#include <cstring>

#include "error/error.h"
#include "log/log.h"

namespace openage::renderer::world {

ShaderTemplate::ShaderTemplate(const util::Path &template_path) {
auto file = template_path.open();
this->template_code = file.read();
file.close();
}

void ShaderTemplate::load_snippets(const util::Path &snippet_path) {
// load config here
util::Path snippet_path_copy = snippet_path;
for (const auto &entry : snippet_path_copy.iterdir()) {
if (entry.get_name().ends_with(".snippet")) {
add_snippet(entry);
}
}
}

void ShaderTemplate::add_snippet(const util::Path &snippet_path) {
std::string file_name = snippet_path.get_name();
std::string name = file_name.substr(0, file_name.find_last_of('.'));
auto file = snippet_path.open();
std::string content = file.read();
file.close();

snippets[name] = content;
}

std::string ShaderTemplate::generate_source() const {
std::string result = template_code;

// Process each placeholder
for (const auto &[name, snippet_code] : snippets) {
std::string placeholder = "// PLACEHOLDER: " + name;
size_t pos = result.find(placeholder);

if (pos != std::string::npos) {
result.replace(pos, placeholder.length(), snippet_code);
}
else {
log::log(WARN << "Placeholder not found in template: " << name);
}
}

// Check if all placeholders were replaced
size_t placeholder_pos = result.find("// PLACEHOLDER:");
if (placeholder_pos != std::string::npos) {
size_t line_end = result.find('\n', placeholder_pos);
std::string missing = result.substr(placeholder_pos,
line_end - placeholder_pos);
throw Error(MSG(err) << "Missing snippet for placeholder: " << missing);
}

return result;
}
} // namespace openage::renderer::world
62 changes: 62 additions & 0 deletions libopenage/renderer/stages/world/shader_template.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2024-2025 the openage authors. See copying.md for legal info.

#pragma once

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

#include "util/path.h"

namespace openage {
namespace renderer {
namespace world {

/**
* Manages shader templates and their code snippets.
* Allows loading configurable shader commands and generating
* complete shader source code.
*/
class ShaderTemplate {
public:
/**
* Create a shader template from source code of shader.
*
* @param template_path Path to the template file.
*/
explicit ShaderTemplate(const util::Path &template_path);

/**
* Load all snippets from a JSON config file.
*
* @param config_path Path to JSON config file.
* @param base_path Base path for resolving relative snippet paths.
*/
void load_snippets(const util::Path &snippet_path);

/**
* Add a single code snippet to snippets map.
*
* @param name Snippet identifier.
* @param snippet_path Path to the snippet file.
*/
void add_snippet(const util::Path &snippet_path);

/**
* Generate final shader source code with all snippets inserted.
*
* @return Complete shader code.
* @throws Error if any required placeholders are missing snippets.
*/
std::string generate_source() const;

private:
// Original template code with placeholders
std::string template_code;
// Mapping of placeholder IDs to their code snippets
std::map<std::string, std::string> snippets;
};
} // namespace world
} // namespace renderer
} // namespace openage
Loading