diff --git a/assets/test/shaders/demo_7_world.frag.glsl b/assets/test/shaders/demo_7_world.frag.glsl new file mode 100644 index 0000000000..d6ffd22029 --- /dev/null +++ b/assets/test/shaders/demo_7_world.frag.glsl @@ -0,0 +1,34 @@ +#version 330 + +in vec2 vert_uv; + +layout(location = 0) out vec4 col; +layout(location = 1) out uint id; + +uniform sampler2D tex; +uniform uint u_id; + +// position (top left corner) and size: (x, y, width, height) +uniform vec4 tile_params; + +vec2 uv = vec2( + vert_uv.x * tile_params.z + tile_params.x, + vert_uv.y *tile_params.w + tile_params.y); + +void main() { + vec4 tex_val = texture(tex, uv); + int alpha = int(round(tex_val.a * 255)); + switch (alpha) { + case 0: + col = tex_val; + discard; + + // do not save the ID + return; + //@COMMAND_SWITCH@ + default: + col = tex_val; + break; + } + id = u_id; +} \ No newline at end of file diff --git a/assets/test/shaders/demo_7_world.vert.glsl b/assets/test/shaders/demo_7_world.vert.glsl new file mode 100644 index 0000000000..7987d40ec3 --- /dev/null +++ b/assets/test/shaders/demo_7_world.vert.glsl @@ -0,0 +1,101 @@ +#version 330 + +layout(location=0) in vec2 v_position; +layout(location=1) in vec2 uv; + +out vec2 vert_uv; + +// camera parameters for transforming the object position +// and scaling the subtex to the correct size +layout (std140) uniform camera { + // view matrix (world to view space) + mat4 view; + // projection matrix (view to clip space) + mat4 proj; + // inverse zoom factor (1.0 / zoom) + // high zoom = upscale subtex + // low zoom = downscale subtex + float inv_zoom; + // inverse viewport size (1.0 / viewport size) + vec2 inv_viewport_size; +}; + +// can be used to move the object position in world space _before_ +// it's transformed to clip space +// this is usually unnecessary because we want to draw the +// subtex where the object is, so this can be set to the identity matrix +uniform mat4 model; + +// position of the object in world space +uniform vec3 obj_world_position; + +// flip the subtexture horizontally/vertically +uniform bool flip_x; +uniform bool flip_y; + +// parameters for scaling and moving the subtex +// to the correct position in clip space + +// animation scalefactor +// scales the vertex positions so that they +// match the subtex dimensions +// +// high animation scale = downscale subtex +// low animation scale = upscale subtex +uniform float scale; + +// size of the subtex (in pixels) +uniform vec2 subtex_size; + +// offset of the subtex anchor point +// from the subtex center (in pixels) +// used to move the subtex so that the anchor point +// is at the object position +uniform vec2 anchor_offset; + +void main() { + // translate the position of the object from world space to clip space + // this is the position where we want to draw the subtex in 2D + vec4 obj_clip_pos = proj * view * model * vec4(obj_world_position, 1.0); + + // subtex has to be scaled to account for the zoom factor + // and the animation scale factor. essentially this is (animation scale / zoom). + float zoom_scale = scale * inv_zoom; + + // Scale the subtex vertices + // we have to account for the viewport size to get the correct dimensions + // and then scale the subtex to the zoom factor to get the correct size + vec2 vert_scale = zoom_scale * subtex_size * inv_viewport_size; + + // Scale the anchor offset with the same method as above + // to get the correct anchor position in the viewport + vec2 anchor_scale = zoom_scale * anchor_offset * inv_viewport_size; + + // if the subtex is flipped, we also need to flip the anchor offset + // essentially, we invert the coordinates for the flipped axis + float anchor_x = float(flip_x) * -1.0 * anchor_scale.x + float(!flip_x) * anchor_scale.x; + float anchor_y = float(flip_y) * -1.0 * anchor_scale.y + float(!flip_y) * anchor_scale.y; + + // offset the clip position by the offset of the subtex anchor + // imagine this as pinning the subtex to the object position at the subtex anchor point + obj_clip_pos += vec4(anchor_x, anchor_y, 0.0, 0.0); + + // create a move matrix for positioning the vertices + // uses the vert scale and the transformed object position in clip space + mat4 move = mat4(vert_scale.x, 0.0, 0.0, 0.0, + 0.0, vert_scale.y, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + obj_clip_pos.x, obj_clip_pos.y, obj_clip_pos.z, 1.0); + + // calculate the final vertex position + gl_Position = move * vec4(v_position, 0.0, 1.0); + + // if the subtex is flipped, we also need to flip the uv tex coordinates + // essentially, we invert the coordinates for the flipped axis + + // !flip_x is default because OpenGL uses bottom-left as its origin + float uv_x = float(!flip_x) * uv.x + float(flip_x) * (1.0 - uv.x); + float uv_y = float(flip_y) * uv.y + float(!flip_y) * (1.0 - uv.y); + + vert_uv = vec2(uv_x, uv_y); +} diff --git a/assets/test/shaders/world_commands.config b/assets/test/shaders/world_commands.config new file mode 100644 index 0000000000..16b6a9d399 --- /dev/null +++ b/assets/test/shaders/world_commands.config @@ -0,0 +1,23 @@ +[COMMAND] +placeholder=COMMAND_SWITCH +alpha=254 +description=Red tint +code={ +col = vec4(1.0, 0.0, 0.0, 1.0) * tex_val; +} + +[COMMAND] +placeholder=COMMAND_SWITCH +alpha=252 +description=Green tint +code={ +col = vec4(0.0, 1.0, 0.0, 1.0) * tex_val; +} + +[COMMAND] +placeholder=COMMAND_SWITCH +alpha=250 +description=Blue tint +code={ +col = vec4(0.0, 0.0, 1.0, 1.0) * tex_val; +} \ No newline at end of file diff --git a/libopenage/renderer/demo/CMakeLists.txt b/libopenage/renderer/demo/CMakeLists.txt index fb93e5279b..7567731afd 100644 --- a/libopenage/renderer/demo/CMakeLists.txt +++ b/libopenage/renderer/demo/CMakeLists.txt @@ -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 diff --git a/libopenage/renderer/demo/demo_7.cpp b/libopenage/renderer/demo/demo_7.cpp new file mode 100644 index 0000000000..a3a21bf94d --- /dev/null +++ b/libopenage/renderer/demo/demo_7.cpp @@ -0,0 +1,106 @@ +// demo_shader_commands.h +#pragma once + +#include "util/path.h" + +#include +#include + +#include "coord/tile.h" +#include "renderer/camera/camera.h" +#include "renderer/gui/integration/public/gui_application_with_logger.h" +#include "renderer/opengl/window.h" +#include "renderer/render_factory.h" +#include "renderer/render_pass.h" +#include "renderer/render_target.h" +#include "renderer/resources/assets/asset_manager.h" +#include "renderer/resources/shader_source.h" +#include "renderer/stages/camera/manager.h" +#include "renderer/stages/screen/render_stage.h" +#include "renderer/stages/skybox/render_stage.h" +#include "renderer/stages/terrain/render_entity.h" +#include "renderer/stages/terrain/render_stage.h" +#include "renderer/stages/world/render_entity.h" +#include "renderer/stages/world/render_stage.h" +#include "renderer/uniform_buffer.h" +#include "time/clock.h" + +namespace openage::renderer::tests { + +void renderer_demo_7(const util::Path &path) { + // Basic setup + auto qtapp = std::make_shared(); + window_settings settings; + settings.width = 800; + settings.height = 600; + settings.debug = true; + + auto window = std::make_shared("Shader Commands Demo", settings); + auto renderer = window->make_renderer(); + auto camera = std::make_shared(renderer, window->get_size()); + auto clock = std::make_shared(); + auto asset_manager = std::make_shared( + renderer, + path["assets"]["test"]); + auto cam_manager = std::make_shared(camera); + + auto shaderdir = path / "assets" / "test" / "shaders"; + + std::vector> + render_passes{}; + + // Initialize world renderer with shader commands + auto world_renderer = std::make_shared( + window, + renderer, + camera, + shaderdir, + shaderdir, // Temporarily, Shader commands config has the same path with shaders for this demo + asset_manager, + clock); + + render_passes.push_back(world_renderer->get_render_pass()); + + auto screen_renderer = std::make_shared( + window, + renderer, + path["assets"]["shaders"]); + std::vector> targets{}; + for (auto &pass : render_passes) { + targets.push_back(pass->get_target()); + } + screen_renderer->set_render_targets(targets); + + render_passes.push_back(screen_renderer->get_render_pass()); + + auto render_factory = std::make_shared(nullptr, world_renderer); + + auto entity1 = render_factory->add_world_render_entity(); + entity1->update(0, coord::phys3(0.0f, 0.0f, 0.0f), "./textures/test_gaben.sprite"); + + auto entity2 = render_factory->add_world_render_entity(); + entity2->update(1, coord::phys3(3.0f, 0.0f, 0.0f), "./textures/test_gaben.sprite"); + + auto entity3 = render_factory->add_world_render_entity(); + entity3->update(2, coord::phys3(-3.0f, 0.0f, 0.0f), "./textures/test_gaben.sprite"); + + // Main loop + while (not window->should_close()) { + qtapp->process_events(); + + // Update camera matrices + cam_manager->update(); + + world_renderer->update(); + + for (auto &pass : render_passes) { + renderer->render(pass); + } + + renderer->check_error(); + + window->update(); + } +} + +} // namespace openage::renderer::tests \ No newline at end of file diff --git a/libopenage/renderer/demo/demo_7.h b/libopenage/renderer/demo/demo_7.h new file mode 100644 index 0000000000..45c192523d --- /dev/null +++ b/libopenage/renderer/demo/demo_7.h @@ -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 { + +/** + * Show off the render stages in the level 2 renderer and the camera + * system. + * - Window creation + * - Creating a camera + * - Initializing the level 2 render stages: skybox, terrain, world, screen + * - Adding renderables to the render stages via the render factory + * - Moving camera with mouse/keyboard callbacks + * + * @param path Path to the project rootdir. + */ +void renderer_demo_7(const util::Path &path); + +} // namespace openage::renderer::tests diff --git a/libopenage/renderer/demo/tests.cpp b/libopenage/renderer/demo/tests.cpp index d3fb0e3c21..bab82af2e3 100644 --- a/libopenage/renderer/demo/tests.cpp +++ b/libopenage/renderer/demo/tests.cpp @@ -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" @@ -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" @@ -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;