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).
This commit is contained in:
Alvin Wong 2024-06-30 23:51:13 +08:00
parent d5aadc38b4
commit 1bd66af54c

View file

@ -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) { 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; 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; float scale = 1.0;
if (p_viewport->canvas_map.has(p_canvas->parent)) { if (p_viewport->canvas_map.has(p_canvas->parent)) {
Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform; 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; xf = xf * c_xform;
scale = p_canvas->parent_scale; scale = p_canvas->parent_scale;
} }
Transform2D c_xform = p_canvas_data->transform; 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; xf = xf * c_xform;
if (scale != 1.0 && !RSG::canvas->disable_scale) { if (scale != 1.0 && !RSG::canvas->disable_scale) {