From 1bd66af54ce86d5882830ce5170cdfeb914bb873 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sun, 30 Jun 2024 23:51:13 +0800 Subject: [PATCH] Apply snap 2D transforms to pixel to viewport We shall not leave the viewport transform to be rounded by the code for rounding canvas items. Since the viewport transform is inverse to the camera transform, we get incorrect rounding at the halfway point that misaligns the viewport and the canvas item which the camera is following. Instead, reintroduce viewport rounding, but do it in a way that matches the rounding of canvas items. Also take into account the half-pixel offset of the centre point when viewport dimension is not divisible by two. For `CanvasLayer`s that follows viewport, take into account the scale when rounding. Overall this should work better compared to the rounding in Godot 4.2 (and earlier). --- servers/rendering/renderer_viewport.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 781d29ffaa4..4d6435f48a5 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -41,14 +41,31 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, RendererCanvasCull::Canvas *p_canvas, RendererViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) { Transform2D xf = p_viewport->global_transform; + Vector2 pixel_snap_offset; + if (p_viewport->snap_2d_transforms_to_pixel) { + // We use `floor(p + 0.5)` to snap canvas items, but `ceil(p - 0.5)` + // to snap viewport transform because the viewport transform is inverse + // to the camera transform. Also, if the viewport size is not divisible + // by 2, the center point is offset by 0.5 px and we need to add 0.5 + // before rounding to cancel it out. + pixel_snap_offset.x = (p_viewport->size.width % 2) ? 0.0 : -0.5; + pixel_snap_offset.y = (p_viewport->size.height % 2) ? 0.0 : -0.5; + } + float scale = 1.0; if (p_viewport->canvas_map.has(p_canvas->parent)) { Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform; + if (p_viewport->snap_2d_transforms_to_pixel) { + c_xform.columns[2] = (c_xform.columns[2] * p_canvas->parent_scale + pixel_snap_offset).ceil() / p_canvas->parent_scale; + } xf = xf * c_xform; scale = p_canvas->parent_scale; } Transform2D c_xform = p_canvas_data->transform; + if (p_viewport->snap_2d_transforms_to_pixel) { + c_xform.columns[2] = (c_xform.columns[2] + pixel_snap_offset).ceil(); + } xf = xf * c_xform; if (scale != 1.0 && !RSG::canvas->disable_scale) {