-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Configurable World Shader Commands System #1738
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
base: master
Are you sure you want to change the base?
Changes from all commits
f94d34e
235fcf3
3dc759f
64620ae
8cda18b
84abb5d
dfc8cd1
28fc642
dd48b95
39dee0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
} |
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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
float alpha = (sin(time) + 1.0) * 0.5; |
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); |
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 |
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ add_sources(libopenage | |
object.cpp | ||
render_entity.cpp | ||
render_stage.cpp | ||
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('.')); | ||
Comment on lines
+29
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
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); | ||
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idea for a faster approach: Instead of searching the whole source string for each snippet, we could search for the placeholder positions during initialization of the class and save the positions. Then when we want to insert the snippets later, we just have to look up the positions again. Probably not that big of a deal right now, but it might be more performant on large template files. |
||
|
||
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 |
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; | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could generate |
||||||||||||||||||
|
||||||||||||||||||
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; | ||||||||||||||||||
Comment on lines
+55
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need three slashes, so that doxygen detects it as a docstring.
Suggested change
|
||||||||||||||||||
}; | ||||||||||||||||||
} // namespace world | ||||||||||||||||||
} // namespace renderer | ||||||||||||||||||
} // namespace openage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.