Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Zooming in and out makes line flicker  (Read 2267 times)

0 Members and 1 Guest are viewing this topic.

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Zooming in and out makes line flicker
« on: November 12, 2023, 11:23:47 pm »
Sorry for double-posting, I'm having a problem understanding the solution to this.
When zooming in and out of a grid, I see that lines would disappear, as seen in the video below:
https://streamable.com/e0bd53

I googled the problem and found in an old thread here that the problem is setting the view to non-integer values. Makes sense. But how can I set the view to integer values in this case? All I do is call the 'zoom' function, I'm not moving the view or anything, that I can round my values.

Here is the "zoom event" in the event loop:
case sf::Event::MouseWheelScrolled:
                    if (evnt.mouseWheelScroll.delta <= -1) // Scroll down - zoom-out
                        zoom = std::min(2.0, zoom + 0.1); // By using &#39;min&#39; with &#39;2&#39;, we set it as a lower limit.
                    else if (evnt.mouseWheelScroll.delta >= 1) // Scroll up - zoom-in
                        zoom = std::max(0.5, zoom - 0.1); // By using &#39;max&#39; with &#39;0.5&#39;, we set it as an upper limit.

                    view.setSize(window.getDefaultView().getSize()); // Reset the size
                    view.zoom(zoom);
                    window.setView(view);
                    break;

And just for reference, here is the draw function:
void drawGrid(sf::RenderWindow& window, std::unordered_set<sf::Vector2i, pair_hash, pair_equal>& grid){
    sf::RectangleShape cell(sf::Vector2f(CELL_SIZE, CELL_SIZE));
    cell.setOutlineColor(sf::Color(200, 200, 200)); // Beige
    cell.setOutlineThickness(1.25);
    for (int i = 0; i < GRID_HEIGHT / CELL_SIZE; i++){
        for (int j = 0; j < GRID_WIDTH / CELL_SIZE; j++){
            if (grid.count({j,i})) cell.setFillColor(LIVE_CELL_COLOR);
            else cell.setFillColor(DEAD_CELL_COLOR);

            // Set cell position based on its grid coordinates.
            cell.setPosition(j * CELL_SIZE, i * CELL_SIZE);
            window.draw(cell);
        }
    }
}

Thanks in advance.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Zooming in and out makes line flicker
« Reply #1 on: November 13, 2023, 12:39:27 am »
Are the lines created by the rectangle shape's outline?
This would mean that the rectangles and their outlines are competing for pixels as they shrink.

"Keeping to integers" can be tough when scaling. To be exact, you'd need to always scale the view by half/double and then round to similar (when a view is scaled up to double so that everything is half-sized, you need to round to double values.
e.g. if you are drawing at (1, 1) and scale the view so that that point would be (0.5, 0.5), you need to draw at (2, 2) instead so that the point would be (1, 1).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: Zooming in and out makes line flicker
« Reply #2 on: November 13, 2023, 09:00:04 am »
Are the lines created by the rectangle shape's outline?
This would mean that the rectangles and their outlines are competing for pixels as they shrink.

"Keeping to integers" can be tough when scaling. To be exact, you'd need to always scale the view by half/double and then round to similar (when a view is scaled up to double so that everything is half-sized, you need to round to double values.
e.g. if you are drawing at (1, 1) and scale the view so that that point would be (0.5, 0.5), you need to draw at (2, 2) instead so that the point would be (1, 1).

Yes, the lines are created by the rectangle shape's outline
Would you suggest to move on to tileMap or Vertex array?

Scaling only by half/double seems like too much :<

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Zooming in and out makes line flicker
« Reply #3 on: November 13, 2023, 03:24:51 pm »
Moving to a vertex array is almost always the answer ;D
It's the ability to display multiple quads/triangles at once so much fewer draw calls for multiple rectangle shapes.
Of course, a tile map is just a vertex array at heart but designed to draw tiles.

My point was that even by only scaling double and half, keeping to integers still brings complications.
Scaling by anything else can be even more complications.

The consideration really is if you can put up with slight gaps. If not, you'll need a technique to reduce it:

Not scaling is one.

"Over-drawing" is another: drawing the tiles bigger than they should be so that they overlap.

Drawing at an integer-perfect state on a render texture and then drawing the render texture with modifications that would normally cause the gaps (there can be no gaps because it's already been rendered without them on the render texture).
This is a bit of work and can still be an issue when scaling if not handled carefully.

Using larger tiles can reduce issues too; Less likely to have gaps if there aren't tiles. Building a map of full pieces can a reliable way but removes the convenience of repeatable tiles.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: Zooming in and out makes line flicker
« Reply #4 on: November 13, 2023, 08:52:30 pm »
Moving to a vertex array is almost always the answer ;D
It's the ability to display multiple quads/triangles at once so much fewer draw calls for multiple rectangle shapes.
Of course, a tile map is just a vertex array at heart but designed to draw tiles.

My point was that even by only scaling double and half, keeping to integers still brings complications.
Scaling by anything else can be even more complications.

The consideration really is if you can put up with slight gaps. If not, you'll need a technique to reduce it:

Not scaling is one.

"Over-drawing" is another: drawing the tiles bigger than they should be so that they overlap.

Drawing at an integer-perfect state on a render texture and then drawing the render texture with modifications that would normally cause the gaps (there can be no gaps because it's already been rendered without them on the render texture).
This is a bit of work and can still be an issue when scaling if not handled carefully.

Using larger tiles can reduce issues too; Less likely to have gaps if there aren't tiles. Building a map of full pieces can a reliable way but removes the convenience of repeatable tiles.

I see, maybe I'll pass on the idea of implementing 'zoom' for now, as I'm only beginning with SFML.
Thaks a lot :)

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: Zooming in and out makes line flicker
« Reply #5 on: January 01, 2024, 04:36:47 pm »
Moving to a vertex array is almost always the answer ;D
It's the ability to display multiple quads/triangles at once so much fewer draw calls for multiple rectangle shapes.
Of course, a tile map is just a vertex array at heart but designed to draw tiles.

My point was that even by only scaling double and half, keeping to integers still brings complications.
Scaling by anything else can be even more complications.

The consideration really is if you can put up with slight gaps. If not, you'll need a technique to reduce it:

Not scaling is one.

"Over-drawing" is another: drawing the tiles bigger than they should be so that they overlap.

Drawing at an integer-perfect state on a render texture and then drawing the render texture with modifications that would normally cause the gaps (there can be no gaps because it's already been rendered without them on the render texture).
This is a bit of work and can still be an issue when scaling if not handled carefully.

Using larger tiles can reduce issues too; Less likely to have gaps if there aren't tiles. Building a map of full pieces can a reliable way but removes the convenience of repeatable tiles.

Hey, sorry for the bump, I'm trying to get back to this after I've experienced a bit more with SFML.
I've read about vertexArray in the doc, and I just want to know one thing:

Performence is not my main concern - despite drawing a lot of sf::RectangleShape to make the grid, I'm only drawing the ones in view, so draw() cost almost nothing (from testing).

I want to move to VertexArray mainly to solve this zoom issue, when lines flicker in and out of existence when zooming. Do you think it would solve it?
« Last Edit: January 01, 2024, 04:38:52 pm by abcd1234567 »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Zooming in and out makes line flicker
« Reply #6 on: January 02, 2024, 07:21:44 pm »
A vertex array allows you to draw many triangles at once rather than a couple (or so) at a time (like for a rectangle shape) so improves performance. It doesn't, however, change the way those triangles are calculated so vertex arrays don't automatically solve floating point ambiguities.

You would still need to apply a method (one or more I mentioned above or maybe something else you can find or think of!) to overcome those issues.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: Zooming in and out makes line flicker
« Reply #7 on: January 02, 2024, 09:23:32 pm »
A vertex array allows you to draw many triangles at once rather than a couple (or so) at a time (like for a rectangle shape) so improves performance. It doesn't, however, change the way those triangles are calculated so vertex arrays don't automatically solve floating point ambiguities.

You would still need to apply a method (one or more I mentioned above or maybe something else you can find or think of!) to overcome those issues.

Thank you for the help.

It seems that moving to VertexArray did solve this problem for me, as seen here, the lines are not flickering:
https://streamable.com/0p825x
This is basically a VertexArray of Quads and a separate VertexArray of lines (I want the cells to be clickable, so lines are not enough)

But - is there a way to show only the vertices in view? Of course, the more the array grows - such is the draw time, so a grid of 1024x1024 cells (each 32x32) is already slow (drawing the quads+lines takes about 50ms).
I Googled it and found in old threads that it shouldn't be the bottleneck, and with VertexArray - it's drawing everything or nothing. How can I solve this?


Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Zooming in and out makes line flicker
« Reply #8 on: January 02, 2024, 09:38:56 pm »
Good to hear that your problem has been fixed. :)

Not sure if it's "fixed" as such though. If you're drawing lines, they will always be there and never flicker. The quads are being "hidden" by those lines and 'may' be exposed on other occasions where the lines are on the edge of a pixel.

I'm not sure I under what you mean by drawing the quads because you want them to be clickable. You can, of course, just test the rectangle area for mouse positions. And, if you mean that you need it to display something when it's clicked (or hovered over), you can simply draw those cells that need to have something. This means you can not draw the others. You can do this by resizing the vertex array after calculating how many cells will be displayed and then just adding those ones.

Since your grid is so strict and even, it should be simple enough to calculate if one of those is outside of the window. Indeed, you can use rectangle collision to check this (SFML's FloatRects can do this; it's called intersects). Check the cell's rectangle against the window's rectangle (actually the view's rectangle).
Then, combining with the things I mentioned in the previous paragraph, you can only draw to the cells in range (by resizing and only adding the ones that are in range).


As an aside, if you would like to use a tile map that can do this automatically, you could try:
https://github.com/Hapaxia/CheeseMap/wiki
(https://en.sfml-dev.org/forums/index.php?topic=29172.0)
You could use Cheese Map to display your map and then draw the lines around it.
It automatically "culls" cells outside of the view. You can also have de-activated cells that aren't displayed if you'd like.
« Last Edit: January 02, 2024, 09:40:40 pm by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: Zooming in and out makes line flicker
« Reply #9 on: January 03, 2024, 06:39:43 pm »
Good to hear that your problem has been fixed. :)

Not sure if it's "fixed" as such though. If you're drawing lines, they will always be there and never flicker. The quads are being "hidden" by those lines and 'may' be exposed on other occasions where the lines are on the edge of a pixel.

I'm not sure I under what you mean by drawing the quads because you want them to be clickable. You can, of course, just test the rectangle area for mouse positions. And, if you mean that you need it to display something when it's clicked (or hovered over), you can simply draw those cells that need to have something. This means you can not draw the others. You can do this by resizing the vertex array after calculating how many cells will be displayed and then just adding those ones.

Since your grid is so strict and even, it should be simple enough to calculate if one of those is outside of the window. Indeed, you can use rectangle collision to check this (SFML's FloatRects can do this; it's called intersects). Check the cell's rectangle against the window's rectangle (actually the view's rectangle).
Then, combining with the things I mentioned in the previous paragraph, you can only draw to the cells in range (by resizing and only adding the ones that are in range).


As an aside, if you would like to use a tile map that can do this automatically, you could try:
https://github.com/Hapaxia/CheeseMap/wiki
(https://en.sfml-dev.org/forums/index.php?topic=29172.0)
You could use Cheese Map to display your map and then draw the lines around it.
It automatically "culls" cells outside of the view. You can also have de-activated cells that aren't displayed if you'd like.

I'm afraid I don't fully understand what you're saying in the 3rd and 4th paragraphs.
I have a huge grid (so that it "looks" infinite), which is clickable (you click on a cell and it changes color), and I have a view. I only draw what is in view.

Currently I simply hold a set of "clicked" cells (sparse matrix, if you will), and each frame I simply iterate over all cells in view, and check for each one if it is "clicked" - if yes, I draw it in color. If not - I leave it blank.

I am able to this since I'm holding an unordered_set (hash map), but vertexArray is just...an array. So for every cell (in view) I need to check the entire array from start to finish. Not very efficient.

Also, you said "you can simply draw those cells", did you mean drawing with quads?
I can't see a way to implement a clickable grid with just lines

Thanks in advance

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Zooming in and out makes line flicker
« Reply #10 on: January 06, 2024, 11:51:25 am »
Yes, I was talking about quads. One way to draw the grid is to draw the cells as quads and change their colours (transparency is an option too!). Another is to just draw the ones that exist (the ones that are "clicked").

It's worth noting that a vertex array isn't actually an array; it's a vector, meaning you can resize it at will (it uses a std::vector internally). This allows you to resize it for the cells that you actually want to draw. You can do this by resizing it once (to the number you need) and then accessing them or by clearing it and pushing each vertex separately (for each visible quad). This is what I was mentioning in my 3rd paragraph.

My 4rd paragraph was about testing to see if the cell was in the view and then not adding it to the list of quads to draw. Since you already test to see if a cell is in the view, these calculation can be the same.

Basically, if you have a set of cells you want to draw already, you can parse them and build the vertex array based on its information. You should be able to do this every frame.

For example, here is how Selba Ward's Starfield 3D updates its vertex array:
https://github.com/Hapaxia/SelbaWard/blob/master/src/SelbaWard/Starfield3d.cpp#L260-L275
It resizes m_vertices based on how many stars there and how many vertices each star requires. Then, it fills in that information for those stars.
Note that instead of an sf::VertexArray, it uses a std::vector<sf::Vertex> directly but they are equivalent.

Other examples (in Selba Ward):
Sprite Batcher:
https://github.com/Hapaxia/SelbaWard/blob/master/src/SelbaWard/SpriteBatch.cpp#L401
needs to resize based on how many sprites there are to show.
Polygon:
https://github.com/Hapaxia/SelbaWard/blob/master/src/SelbaWard/Polygon.cpp#L232-L244
This is quite a simple example that shows being resized and updated from already-processed triangles.

Remember that resizing an sf::VertexArray (or std::vector<sf::Vertex>) is acceptable to do every frame.
For example, here's a video of an unfinished game that constantly resizes the vertex array (significantly) every frame:
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*