-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
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
Fix CanvasItem.draw_rect
function with filled = false
#41239
Conversation
0d785cc
to
cd1b716
Compare
REBASED AND READY FOR REVIEW |
34df7b7
to
f5bbffa
Compare
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.
Using the code from #35384 on this PR, if I have the base window size set to be small and let Godot stretch the Window to make things more visible, it looks correct:
However, if I scale up the Node2D instead of stretching the window, it doesn't look quite right:
I'm not super familiar with this function so I'm not sure if it's supposed to draw "one pixel thick" on the screen or relative to the Node2D, but this output is definitely not either.
Also, in addition to the blue rectangle not looking correct, the green one is drawn using filled set to false, but a size of one, but it looks like it's filled. If I change it to a size of not 1 it looks similarly broken to how the blue one is broken.
Thanks for the review. It looks like the bug in the Green and yellow lines are 1 pixel thick, blue and pink lines are 2 pixel thick. Node2D is scaled. If I set the border thickness to 2 pixels, then the rectangle is drawn correctly: I'll take a look at the |
f5bbffa
to
969ee68
Compare
969ee68
to
062dcce
Compare
I fixed the bug, but the solution is quite hacky. The main drawback is Also, there is a hack to check the width is 1 due to #51978: godot/servers/rendering/renderer_canvas_cull.cpp Lines 582 to 602 in 0056acf
In general, with integer scaling factors, everything is displayed correctly. For non-integer scales, lines can be drawn with different widths due to inaccurate mapping to the pixel grid. But I'm not sure if this problem can be solved at all. With the current behavior, errors occur even at integer scales and linewidths, especially if the color is translucent. |
Does this fix #58775? |
Or maybe not and it needs to be rebased on top of #69851? |
There are a couple of things in this PR that are not in #69851. I will rebase it soon. |
2074af3
to
ef23ee2
Compare
I think #58775 is not really solvable in a general case. It's not just a matter of obtaining proper final positions of the line endpoints in the coordinate space of the viewport/framebuffer/render target (or whatever you call it) which would be needed to be able to apply a proper offset in that space (small final offset; regardless of the zoom etc.). What we'd also need to know to determine what such offset should be is the exact algorithm used by the Vulkan/OpenGL/etc. for rasterizing the line primitives. And that's problematic, because these can differ between implementations since specifications aren't super strict:
So even assuming that rasterization is done according to the diamond exit rule (which is not guaranteed; but maybe it's actually the most common case?:thinking: no idea about that) we would still need to find a way to somehow obtain the final positions of the line endpoints within the fragments' coordinate space (I don't see how we could currently do it). And then we would still need to find a way of how to offset the vertices (probably branching into many different cases) to always produce satisfying fragments. E.g. take a look at the image from this post and imagine all possible configurations of the vertices (colored dots); and yeah, the rect can end up being transformed in there (so take into consideration rotations or... skewing; yeah, good luck with that 😄). TL;DR: I don't see us solving it. Would love to be surprised by someone but I don't think it will happen. For a non-general solution / hacky solution what maybe could be done is adding a method (are a bool param to the current Regarding #35384 I guess it's solved by combination of #69851 + this PR + godotengine/godot-docs#6455? |
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.
There's a question whether we should handle rects with negative size properly? Currently (AFAICT, haven't tested) only filled ones would be drawn correctly (I mean that rect
with negative size would be drawn the same as rect.abs()
). Shouldn't be hard to handle them too.
ef23ee2
to
c046bb8
Compare
Fixed. |
I guess it should be fine, that's a quite cheap call. Potentially could be done just for non-filled rects though. Also does changing how we draw the border for I wonder if there is any specific reason to prefer one over the other. |
I'm not sure since this PR started quite a while ago and these changes have carried over through all the rebases. I will remove them if they are unnecessary. |
To me they do seem like not changing anything (so leaving the changes should be fine too). |
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.
LGTM. Would welcome @clayjohn's opinion on #41239 (comment) before someone will merge this.
// Top line. | ||
RenderingServer::get_singleton()->canvas_item_add_line( | ||
canvas_item, | ||
p_rect.position + Size2(-offset, 0), | ||
p_rect.position + Size2(p_rect.size.width + offset, 0), | ||
rect.position + Size2(-offset, 0), | ||
rect.position + Size2(-offset + rect.size.width, 0), | ||
p_color, | ||
p_width); | ||
// Right line. | ||
RenderingServer::get_singleton()->canvas_item_add_line( | ||
canvas_item, | ||
p_rect.position + Size2(p_rect.size.width, offset), | ||
p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset), | ||
rect.position + Size2(rect.size.width, -offset), | ||
rect.position + Size2(rect.size.width, -offset + rect.size.height), | ||
p_color, | ||
p_width); | ||
// Bottom line. | ||
RenderingServer::get_singleton()->canvas_item_add_line( | ||
canvas_item, | ||
p_rect.position + Size2(p_rect.size.width + offset, p_rect.size.height), | ||
p_rect.position + Size2(-offset, p_rect.size.height), | ||
rect.position + Size2(offset + rect.size.width, rect.size.height), | ||
rect.position + Size2(offset, rect.size.height), | ||
p_color, | ||
p_width); | ||
// Left line. | ||
RenderingServer::get_singleton()->canvas_item_add_line( | ||
canvas_item, | ||
p_rect.position + Size2(0, p_rect.size.height - offset), | ||
p_rect.position + Size2(0, offset), | ||
rect.position + Size2(0, offset + rect.size.height), | ||
rect.position + Size2(0, offset), | ||
p_color, | ||
p_width); |
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.
Should draw_line
s be replaced with canvas_item_add_polyline
after #71679 is merged? Like here:
godot/scene/resources/rectangle_shape_2d.cpp
Lines 74 to 88 in 14fdd28
Vector<Vector2> stroke_points; | |
stroke_points.resize(5); | |
stroke_points.write[0] = -size * 0.5; | |
stroke_points.write[1] = Vector2(size.x, -size.y) * 0.5; | |
stroke_points.write[2] = size * 0.5; | |
stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5; | |
stroke_points.write[4] = -size * 0.5; | |
Vector<Color> stroke_colors; | |
stroke_colors.resize(5); | |
for (int i = 0; i < 5; i++) { | |
stroke_colors.write[i] = (p_color); | |
} | |
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors); |
(draw_rect
doesn't have antialiased
parameter anyway.)
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.
I guess so. A polyline should be slightly faster than having multiple regular lines. But that should be done in a later PR and needs be profiled before changing
I don't think there is a good reason to prefer one behaviour over the other. |
Thanks! |
Closes #35384.
Special thanks to @clayjohn and @kleonc for the explanations.