diff --git a/assets/shaders/terrain.vert.glsl b/assets/shaders/terrain.vert.glsl index aef9b45867..614f6f382b 100644 --- a/assets/shaders/terrain.vert.glsl +++ b/assets/shaders/terrain.vert.glsl @@ -7,9 +7,17 @@ out vec2 tex_pos; uniform mat4 model; +// camera parameters for transforming the object position +// and scaling the subtex to the correct size layout (std140) uniform camera { - mat4 view; - mat4 proj; + // view matrix (world to view space) + mat4 view; + // projection matrix (view to clip space) + mat4 proj; + // inverse zoom factor (1.0 / zoom) + float inv_zoom; + // inverse viewport size (1.0 / viewport size) + vec2 inv_viewport_size; }; void main() { diff --git a/assets/shaders/world2d.vert.glsl b/assets/shaders/world2d.vert.glsl index 76cd6db34d..7987d40ec3 100644 --- a/assets/shaders/world2d.vert.glsl +++ b/assets/shaders/world2d.vert.glsl @@ -5,10 +5,19 @@ layout(location=1) in vec2 uv; out vec2 vert_uv; -// transformation for object (not vertex!) position to clip space +// camera parameters for transforming the object position +// and scaling the subtex to the correct size layout (std140) uniform camera { - mat4 view; - mat4 proj; + // 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_ @@ -27,33 +36,55 @@ uniform bool flip_y; // parameters for scaling and moving the subtex // to the correct position in clip space -// offset from the subtex anchor -// moves the subtex relative to the subtex center -uniform vec2 anchor_offset; - +// animation scalefactor // scales the vertex positions so that they // match the subtex dimensions -uniform vec2 scale; +// +// 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_offset.x + float(!flip_x) * anchor_offset.x; - float anchor_y = float(flip_y) * -1.0 * anchor_offset.y + float(!flip_y) * anchor_offset.y; + 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 scale and the transformed object position in clip space - mat4 move = mat4(scale.x, 0.0, 0.0, 0.0, - 0.0, scale.y, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, + // 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 diff --git a/libopenage/renderer/camera/camera.cpp b/libopenage/renderer/camera/camera.cpp index 604ddf440f..884aceca1b 100644 --- a/libopenage/renderer/camera/camera.cpp +++ b/libopenage/renderer/camera/camera.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "camera.h" @@ -28,10 +28,7 @@ Camera::Camera(const std::shared_ptr &renderer, proj{Eigen::Matrix4f::Identity()} { this->look_at_scene(Eigen::Vector3f(0.0f, 0.0f, 0.0f)); - resources::UBOInput view_input{"view", resources::ubo_input_t::M4F32}; - resources::UBOInput proj_input{"proj", resources::ubo_input_t::M4F32}; - auto ubo_info = resources::UniformBufferInfo{resources::ubo_layout_t::STD140, {view_input, proj_input}}; - this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); + this->init_uniform_buffer(renderer); log::log(INFO << "Created new camera at position " << "(" << this->scene_pos[0] @@ -55,10 +52,7 @@ Camera::Camera(const std::shared_ptr &renderer, viewport_changed{true}, view{Eigen::Matrix4f::Identity()}, proj{Eigen::Matrix4f::Identity()} { - resources::UBOInput view_input{"view", resources::ubo_input_t::M4F32}; - resources::UBOInput proj_input{"proj", resources::ubo_input_t::M4F32}; - auto ubo_info = resources::UniformBufferInfo{resources::ubo_layout_t::STD140, {view_input, proj_input}}; - this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); + this->init_uniform_buffer(renderer); log::log(INFO << "Created new camera at position " << "(" << this->scene_pos[0] @@ -269,4 +263,15 @@ const std::shared_ptr &Camera::get_uniform_buffer() con return this->uniform_buffer; } +void Camera::init_uniform_buffer(const std::shared_ptr &renderer) { + resources::UBOInput view_input{"view", resources::ubo_input_t::M4F32}; + resources::UBOInput proj_input{"proj", resources::ubo_input_t::M4F32}; + resources::UBOInput inv_zoom_input{"inv_zoom", resources::ubo_input_t::F32}; + resources::UBOInput inv_viewport_size{"inv_viewport_size", resources::ubo_input_t::V2F32}; + auto ubo_info = resources::UniformBufferInfo{ + resources::ubo_layout_t::STD140, + {view_input, proj_input, inv_zoom_input, inv_viewport_size}}; + this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); +} + } // namespace openage::renderer::camera diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index 9cdd559e6f..c8c5c23076 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -43,6 +43,7 @@ class Camera { * The camera uses default values. Its centered on the origin of the scene (0.0f, 0.0f, 0.0f) * and has a zoom level of 1.0f. * + * @param renderer openage renderer instance. * @param viewport_size Initial viewport size of the camera (width x height). */ Camera(const std::shared_ptr &renderer, @@ -51,6 +52,7 @@ class Camera { /** * Create a new camera for the renderer. * + * @param renderer openage renderer instance. * @param viewport_size Viewport size of the camera (width x height). * @param scene_pos Position of the camera in the scene. * @param zoom Zoom level of the camera (defaults to 1.0f). @@ -187,6 +189,13 @@ class Camera { const std::shared_ptr &get_uniform_buffer() const; private: + /** + * Create the uniform buffer for the camera. + * + * @param renderer openage renderer instance. + */ + void init_uniform_buffer(const std::shared_ptr &renderer); + /** * Position in the 3D scene. */ diff --git a/libopenage/renderer/demo/stresstest_0.cpp b/libopenage/renderer/demo/stresstest_0.cpp index ce7da14948..01169b0849 100644 --- a/libopenage/renderer/demo/stresstest_0.cpp +++ b/libopenage/renderer/demo/stresstest_0.cpp @@ -49,7 +49,14 @@ void renderer_stresstest_0(const util::Path &path) { "view", camera->get_view_matrix(), "proj", - camera->get_projection_matrix()); + camera->get_projection_matrix(), + "inv_zoom", + 1.0f / camera->get_zoom()); + auto viewport_size = camera->get_viewport_size(); + Eigen::Vector2f viewport_size_vec{ + 1.0f / static_cast(viewport_size[0]), + 1.0f / static_cast(viewport_size[1])}; + cam_unifs->update("inv_viewport_size", viewport_size_vec); camera->get_uniform_buffer()->update_uniforms(cam_unifs); // Render stages diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index a06cc16edf..d26ba7b96c 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2023 the openage authors. See copying.md for legal info. +// Copyright 2017-2024 the openage authors. See copying.md for legal info. #include "renderer.h" @@ -33,10 +33,10 @@ GlRenderer::GlRenderer(const std::shared_ptr &ctx, // global GL alpha blending settings // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparate( - GL_SRC_ALPHA, // source (overlaying) RGB factor + GL_SRC_ALPHA, // source (overlaying) RGB factor GL_ONE_MINUS_SRC_ALPHA, // destination (underlying) RGB factor - GL_ONE, // source (overlaying) alpha factor - GL_ONE_MINUS_SRC_ALPHA // destination (underlying) alpha factor + GL_ONE, // source (overlaying) alpha factor + GL_ONE_MINUS_SRC_ALPHA // destination (underlying) alpha factor ); // global GL depth testing settings @@ -90,6 +90,11 @@ std::shared_ptr GlRenderer::add_uniform_buffer(resources::Uniform size_t offset = 0; for (auto const &input : inputs) { auto type = GL_UBO_INPUT_TYPE.get(input.type); + auto size = resources::UniformBufferInfo::get_size(input, info.get_layout()); + + // align offset to the size of the type + offset += offset % size; + uniforms.emplace( std::make_pair(input.name, GlInBlockUniform{type, @@ -97,7 +102,8 @@ std::shared_ptr GlRenderer::add_uniform_buffer(resources::Uniform resources::UniformBufferInfo::get_size(input, info.get_layout()), resources::UniformBufferInfo::get_stride_size(input.type, info.get_layout()), input.count})); - offset += resources::UniformBufferInfo::get_size(input, info.get_layout()); + + offset += size; } return std::make_shared(this->gl_context, diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 3ccd49ce80..31b0d17778 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -1,4 +1,4 @@ -// Copyright 2013-2023 the openage authors. See copying.md for legal info. +// Copyright 2013-2024 the openage authors. See copying.md for legal info. #include "shader_program.h" @@ -445,7 +445,12 @@ void GlShaderProgram::bind_uniform_buffer(const char *block_name, std::shared_pt auto gl_buffer = std::dynamic_pointer_cast(buffer); auto &block = this->uniform_blocks[block_name]; - // TODO: Check if the uniform buffer matches the block definition + // Check if the uniform buffer matches the block definition + for (auto const &pair : block.uniforms) { + auto const &unif = pair.second; + ENSURE(gl_buffer->has_uniform(pair.first.c_str()), + "Uniform buffer does not contain uniform '" << pair.first << "' required by block " << block_name); + } block.binding_point = gl_buffer->get_binding_point(); glUniformBlockBinding(*this->handle, block.index, block.binding_point); diff --git a/libopenage/renderer/resources/buffer_info.cpp b/libopenage/renderer/resources/buffer_info.cpp index c3215d63ba..de9a1751c8 100644 --- a/libopenage/renderer/resources/buffer_info.cpp +++ b/libopenage/renderer/resources/buffer_info.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "buffer_info.h" @@ -27,7 +27,13 @@ const std::vector &UniformBufferInfo::get_inputs() const { size_t UniformBufferInfo::get_size() const { size_t size = 0; for (const auto &input : this->inputs) { - size += this->get_size(input, this->layout); + // size of the input type + size_t input_size = this->get_size(input, this->layout); + + // inputs must additionally be aligned to a multiple of their size + size_t align_size = size % input_size; + + size += input_size + align_size; } return size; } diff --git a/libopenage/renderer/stages/camera/manager.cpp b/libopenage/renderer/stages/camera/manager.cpp index 580f7db597..39eddd2b52 100644 --- a/libopenage/renderer/stages/camera/manager.cpp +++ b/libopenage/renderer/stages/camera/manager.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "manager.h" @@ -97,11 +97,25 @@ void CameraManager::update_motion() { } void CameraManager::update_uniforms() { + // transformation matrices this->uniforms->update( "view", - camera->get_view_matrix(), + this->camera->get_view_matrix(), "proj", - camera->get_projection_matrix()); + this->camera->get_projection_matrix()); + + // zoom scaling + this->uniforms->update( + "inv_zoom", + 1.0f / this->camera->get_zoom()); + + auto viewport_size = this->camera->get_viewport_size(); + Eigen::Vector2f viewport_size_vec{ + 1.0f / static_cast(viewport_size[0]), + 1.0f / static_cast(viewport_size[1])}; + this->uniforms->update("inv_viewport_size", viewport_size_vec); + + // update the uniform buffer this->camera->get_uniform_buffer()->update_uniforms(this->uniforms); } diff --git a/libopenage/renderer/stages/world/object.cpp b/libopenage/renderer/stages/world/object.cpp index 23915d420f..c58e9faa98 100644 --- a/libopenage/renderer/stages/world/object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "object.h" @@ -138,23 +138,24 @@ void WorldObject::update_uniforms(const time::time_t &time) { auto coords = tex_info->get_subtex_info(subtex_idx).get_tile_params(); this->uniforms->update(this->tile_params, coords); - // scale and keep width x height ratio of texture - // when the viewport size changes - auto scale = animation_info->get_scalefactor() / this->camera->get_zoom(); - auto screen_size = this->camera->get_viewport_size(); - auto subtex_size = tex_info->get_subtex_info(subtex_idx).get_size(); + // Animation scale factor + // Scales the subtex up or down in the shader + auto scale = animation_info->get_scalefactor(); + this->uniforms->update(this->scale, scale); - // Scaling with viewport size and zoom - auto scale_vec = Eigen::Vector2f{ - scale * (static_cast(subtex_size[0]) / screen_size[0]), - scale * (static_cast(subtex_size[1]) / screen_size[1])}; - this->uniforms->update(this->scale, scale_vec); + // Subtexture size in pixels + auto subtex_size = tex_info->get_subtex_info(subtex_idx).get_size(); + Eigen::Vector2f subtex_size_vec{ + static_cast(subtex_size[0]), + static_cast(subtex_size[1])}; + this->uniforms->update(this->subtex_size, subtex_size_vec); - // Move subtexture in scene so that its anchor point is at the object's position + // Anchor point offset (in pixels) + // moves the subtex in the shader so that the anchor point is at the object's position auto anchor = tex_info->get_subtex_info(subtex_idx).get_anchor_params(); - auto anchor_offset = Eigen::Vector2f{ - scale * (static_cast(anchor[0]) / screen_size[0]), - scale * (static_cast(anchor[1]) / screen_size[1])}; + Eigen::Vector2f anchor_offset{ + static_cast(anchor[0]), + static_cast(anchor[1])}; this->uniforms->update(this->anchor_offset, anchor_offset); } diff --git a/libopenage/renderer/stages/world/object.h b/libopenage/renderer/stages/world/object.h index e2fc865dd6..a34a0c690c 100644 --- a/libopenage/renderer/stages/world/object.h +++ b/libopenage/renderer/stages/world/object.h @@ -132,6 +132,7 @@ class WorldObject { inline static uniform_id_t tex; inline static uniform_id_t tile_params; inline static uniform_id_t scale; + inline static uniform_id_t subtex_size; inline static uniform_id_t anchor_offset; private: diff --git a/libopenage/renderer/stages/world/render_stage.cpp b/libopenage/renderer/stages/world/render_stage.cpp index 85e5da7048..d24bb5f896 100644 --- a/libopenage/renderer/stages/world/render_stage.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "render_stage.h" @@ -136,6 +136,7 @@ void WorldRenderStage::init_uniform_ids() { WorldObject::tex = this->display_shader->get_uniform_id("tex"); WorldObject::tile_params = this->display_shader->get_uniform_id("tile_params"); WorldObject::scale = this->display_shader->get_uniform_id("scale"); + WorldObject::subtex_size = this->display_shader->get_uniform_id("subtex_size"); WorldObject::anchor_offset = this->display_shader->get_uniform_id("anchor_offset"); }