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

Author Topic: Smooth scrolling large textures  (Read 9873 times)

0 Members and 5 Guests are viewing this topic.

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Smooth scrolling large textures
« on: September 06, 2012, 12:33:09 am »
Hello all,

I'm experimenting with tile-based maps. I created a 800 x 800 RenderTexture, then rendered the tiles to this instead to the screen. Then created a sprite from this RenderTexture, and I'm trying to move it to scroll the map. But no matter what kind of method I try, the movement will be choppy.

Here's the code:
int main()
{
        sf::RenderWindow window(sf::VideoMode(640, 480, 32), "SFML window", sf::Style::Close);
        window.setFramerateLimit(60);

        sf::Clock clock;
        float deltaTime = 0;
        const int scrollSpeed = 150;

        sf::RenderTexture layer1;
        if(!layer1.create(800, 800)) { return EXIT_FAILURE; }

        sf::Sprite layer1sprite;
        layer1sprite.setTexture(layer1.getTexture());

        sf::Texture beach01;
        beach01.loadFromFile("C:/sfml_app/Data/Tiles/Beach01.png");

        Tileset beach1(beach01, 32, 32);

        while(window.isOpen())
        {
                sf::Event event;

                while(window.pollEvent(event))
                {
                        if(event.type == sf::Event::Closed) { window.close(); }
                }

                layer1.clear();
                beach1.setTile(20);
                beach1.setPosition(64, 64);
                layer1.draw(beach1);
                layer1.display();

                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { layer1sprite.move(0.f, -scrollSpeed * deltaTime); }
                else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { layer1sprite.move(0.f, scrollSpeed * deltaTime); }
                else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { layer1sprite.move(-scrollSpeed * deltaTime, 0.f); }
                else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { layer1sprite.move(scrollSpeed * deltaTime, 0.f); }

                deltaTime = clock.restart().asSeconds();

                window.clear();
                window.draw(layer1sprite);
                window.display();
        }

        return 0;
}

If I disable the framerate limiter, the movement will be a bit smoother, but still choppy (plus 60 FPS is quite reasonable, nearly all commercial games are locked at this rate, so this shouldn't be the problem).

What should I do to make it smooth? (Interestingly enough, the same method works fine for small sprites, I don't know what's wrong with larger ones.)

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Smooth scrolling large textures
« Reply #1 on: September 06, 2012, 12:43:28 am »
Since I don't have your classes I can't even run the code.  :)
Did you try:
layer1.setSmooth(true);
Also, why use rendertexture if you're redrawing everything every frame anyway?
For tilemaps you should proably go with vertex arrays.
Back to C++ gamedev with SFML in May 2023

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Smooth scrolling large textures
« Reply #2 on: September 06, 2012, 01:00:52 am »
When I said "make it smooth" I meant smooth scrolling. setSmooth just turns on texture filtering.

I'm not using vertex arrays yet for a number of reasons:
1, This is just an experiment,
2, Even with vertex arrays, I believe I need to use render-to-texture,
3, The problem that I can't do smooth scrolling with large textures is not related to the way the tiles are rendered, since the tile map is just one large texture,
4, I haven't figured out how to use vertex arrays yet,
5, As far as I know, older graphics cards don't support them,
6, The Nintendo Entertainment System certainly had no vertex array support, yet it was capable of smooth scrolling tile-based maps  :)

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Smooth scrolling large textures
« Reply #3 on: September 06, 2012, 01:07:27 am »
I'm not sure but the position of sprite might be trunc'd if it's texture is not smooth. So with these numbers all you're doing is move by 2 pixels then 3, then 2, then 3, so on, so forth.
Quote
1, This is just an experiment,
2, Even with vertex arrays, I believe I need to use render-to-texture,
3, The problem that I can't do smooth scrolling with large textures is not related to the way the tiles are rendered, since the tile map is just one large texture,
4, I haven't figured out how to use vertex arrays yet,
5, As far as I know, older graphics cards don't support them,
6, The Nintendo Entertainment System certainly had no vertex array support, yet it was capable of smooth scrolling tile-based maps 
1. that's quite alright,
2. that's also alright but the late trend of work-around-to-render-more-than-you-really-can is quite peculiar,
4. So instead of asking you try and work around by asking about something else ;D
5. in the end, every sprite, text, whatever, will attempt to use draw(vertices,count,primitive,states) or vertex array(which then uses the long draw method too), so if it doesn't support vertices, it doesn't support sfml
6. Just.. :)
Back to C++ gamedev with SFML in May 2023

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10987
    • View Profile
    • development blog
    • Email
Re: Smooth scrolling large textures
« Reply #4 on: September 06, 2012, 01:10:14 am »
I'm not using vertex arrays yet for a number of reasons:
1, This is just an experiment,
How's this a reason? :P

2, Even with vertex arrays, I believe I need to use render-to-texture,
I don't see why, but then again I've no idea what you're trying to do.

3, The problem that I can't do smooth scrolling with large textures is not related to the way the tiles are rendered, since the tile map is just one large texture,
Without a complete and minimal example the reproduces the problem, we can't really help that much, but guess and construct some theories. ;)

5, As far as I know, older graphics cards don't support them,
You're wrong, a vertex array is nothing else as an array of vertices and vertices are the fundamental part of 3D/2D rendering... ;)
RenderTexture on the other hand don't work on some Intel GPUs. ;)

6, The Nintendo Entertainment System certainly had no vertex array support, yet it was capable of smooth scrolling tile-based maps  :)
Hu? What should this have to do with anything? ???
Nintendo is a platform on it's own with it's own rendering stuff etc...

Also as FRex asked, why do you use a RenderTexture if you draw to it every iteration anyways? ;)

Have you thought about moving the view around instead of the map? Would make things a bit easier I think. There's a tutorial on how to use a view in the wiki section. ;)

When you talk about small and big texture, what numbers are you refering to?
« Last Edit: September 06, 2012, 01:13:38 am by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Smooth scrolling large textures
« Reply #5 on: September 06, 2012, 01:37:57 am »
I'm using RenderTexture because I thought it will be faster to combine all the tiles into one big texture, and move it that way, rather than move each tile individually.

My RenderTexture (later referred as the "large" texture) is 800 x 800 pixels. The "small" sprite I was referring to was 32 x 32.

Moving the view was the logical thing to do, until I did a search on this forum, and on Google. People had nothing but issues with that method (tile rendering artifacts, extremely choppy movement, etc.), and, ironically, the sollution that was recommended in 90% of the cases was to render the tiles to a RenderTexture and use a derived sprite to scroll the map...

Indeed, I didn't include all the source files with that code sample, but I was right when I thought the problem isn't related to RenderTextures or the tile system. When I load a 800 x 800 image from a file, the problem is still present.

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Smooth scrolling large textures
« Reply #6 on: September 06, 2012, 01:44:04 am »
Quote
People had nothing but issues with that method (tile rendering artifacts, extremely choppy movement, etc.)
You can go take a look into Eduard Engine in sfml projects section to see if that quality of tilemap is adequate to your needs, sometimes white lines will appear(extremley rare), but that is only effect of my laziness and focus on concrete functionality, this is already resolved on my end, there is not a single whiteline artifact with my vertex arrays of tiles as of now.
« Last Edit: September 06, 2012, 02:00:19 am by FRex »
Back to C++ gamedev with SFML in May 2023

kaB00M

  • Full Member
  • ***
  • Posts: 101
    • View Profile
    • Caffeware
    • Email
Re: Smooth scrolling large textures
« Reply #7 on: September 07, 2012, 05:36:53 pm »
Not sure if this is what you need.

sprite.setTextureRect(sf::IntRect(x, y, width, height));
 

Crop the large image to a rectangle a little big bigger than the screen resolution.

This will draw less.



N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Smooth scrolling large textures
« Reply #8 on: September 09, 2012, 04:22:36 pm »
Okay, sorry for bumping this, but I felt like opening a new topic for this would be pointless. I've hacked together a simple vertex array based tile map system (from various code snippets from the forum and some tutorials). And, it still didn't solve anything. The map scrolling is jerky, and, of course, now I have those line artifacts when moving the view around the map.

The code (now with 100% less external classes  :P ):
#include <SFML/Graphics.hpp>

int main()
{
        sf::RenderWindow window(sf::VideoMode(640, 480, 32), "SFML Window", sf::Style::Close);
        window.setFramerateLimit(60);

        sf::Clock clock;
        sf::Event m_event;
        sf::View view2(sf::FloatRect(0, 0, 640, 480));

        float deltaTime;

        const int scrollSpeed = 80;
        const int width = 40;
        const int height = 40;
        const int size = 32;

        sf::Texture tileset;
        tileset.loadFromFile("Tileset.png");

        sf::VertexArray map(sf::Quads, width * height * 4);

        for(int x=0; x < width; ++x)
        {
                for(int y=0; y < height; ++y)
                {
                        int current = (x + y * width) * 4;
                        int tx = 0;
                        int ty = 0;

                        map[current + 0].position = sf::Vector2f((float)(x + 0) * (float)size, (float)(y + 0) * (float)size);
                        map[current + 1].position = sf::Vector2f((float)(x + 0) * (float)size, (float)(y + 1) * (float)size);
                        map[current + 2].position = sf::Vector2f((float)(x + 1) * (float)size, (float)(y + 1) * (float)size);
                        map[current + 3].position = sf::Vector2f((float)(x + 1) * (float)size, (float)(y + 0) * (float)size);

                        map[current + 0].texCoords = sf::Vector2f((float)(tx + 0) * (float)size, (float)(ty + 0) * (float)size);
                        map[current + 1].texCoords = sf::Vector2f((float)(tx + 0) * (float)size, (float)(ty + 1) * (float)size);
                        map[current + 2].texCoords = sf::Vector2f((float)(tx + 1) * (float)size, (float)(ty + 1) * (float)size);
                        map[current + 3].texCoords = sf::Vector2f((float)(tx + 1) * (float)size, (float)(ty + 0) * (float)size);
                }
        }

        while(window.isOpen())
        {
                while(window.pollEvent(m_event))
                {
                        if(m_event.type == sf::Event::Closed) { window.close(); }
                }

                deltaTime = clock.restart().asSeconds();

                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { view2.move(0.f, -scrollSpeed * deltaTime); }
                else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { view2.move(0.f, scrollSpeed * deltaTime); }
                else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { view2.move(-scrollSpeed * deltaTime, 0.f); }
                else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { view2.move(scrollSpeed * deltaTime, 0.f); }

                window.clear();
                window.setView(view2);
                window.draw(map, &tileset);
                window.display();
        }
        return 0;
}

So, does anybody have any idea how to scroll the view smoothly, and how to eliminate those lines?

Thanks in advance.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10987
    • View Profile
    • development blog
    • Email
Re: Smooth scrolling large textures
« Reply #9 on: September 09, 2012, 04:34:03 pm »
So, does anybody have any idea how to scroll the view smoothly, and how to eliminate those lines?
Have you tried to use a constante delta time? How big is the difference in the delta time? I mean if it jumps around you'll get some strange movement behaviours. ;)

Can you show an example of those lines?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Smooth scrolling large textures
« Reply #10 on: September 09, 2012, 04:42:25 pm »
Can you show an example of those lines?

Sure:



When I scroll the map up or down, these lines appear for a second. If I scroll left or right, they appear vertically (and in white for some reason).

Have you tried to use a constante delta time? How big is the difference in the delta time? I mean if it jumps around you'll get some strange movement behaviours. ;)

I did, but without success. But I had a feeling that I shold update the delta time in milliseconds instead of seconds, I'll try this later.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Smooth scrolling large textures
« Reply #11 on: September 09, 2012, 04:51:14 pm »
Round the view and object coordinates to integers.

By the way, why do you cast all the time? If floats and integers are involved in an arithmetic expression, the result is automatically float. Otherwise, it can be implicitly converted. And if you really have to cast, prefer static_cast, because it only converts the things you want, and you directly see the conversion in code. The keyword is longer, but it isn't necessary very often.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10987
    • View Profile
    • development blog
    • Email
Re: Smooth scrolling large textures
« Reply #12 on: September 09, 2012, 05:00:58 pm »
By the way, why do you cast all the time? If floats and integers are involved in an arithmetic expression, the result is automatically float. Otherwise, it can be implicitly converted. And if you really have to cast, prefer static_cast, because it only converts the things you want, and you directly see the conversion in code. The keyword is longer, but it isn't necessary very often.
@N_K: See also Stroustrup's comment about this. ;)

But I had a feeling that I shold update the delta time in milliseconds instead of seconds, I'll try this later.
No floats are fine, exept if you want to use the unit milliseconds per meter or so. ;D
Otherwise you'd still have to devide the milliseconds by 1000 to get the seconds and at the point you'll probably use floats anyway. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Smooth scrolling large textures
« Reply #13 on: September 09, 2012, 05:24:35 pm »
Here's what eduard engine does to get rid of lines between vertex arrays(but it will cause half pixel diffrences between vertexs arrays and objects that get drawn without pixel perfection and it's a chety workaround):
In render proc:
1. Set center of member view variable to center of player,
2. pass view to setppview method
3. tell map to prepare all vertex array that fall into view for drawing
4. draw map
5. set window's view to the member view varialbe
6. draw anything that didn't require pixel perfection
7. draw debugging data (irrelevant here)
In setppview :
Round so that left top corner of view has integer coords and set that view
void EEGameScreen::Render(void)
{
        the_view.setCenter(Rptr->GetSMiddle());
        SetPPView(the_view);
        my_map.SetView(the_view);//wrong but ok 4 now,
        EE::Sys().Renderer.draw(my_map);
        EE::Sys().Renderer.setView(the_view);
        EE::Sys().Renderer.draw(*Rptr);
        if (draw_debugs){game_world->DrawDebugData();}
}      
void EEGameScreen::SetPPView(const sf::View& gview)
{
        sf::View copy=gview;
        sf::Vector2f halfsize=(copy.getSize()/2.f);
        sf::Vector2f vec=(copy.getCenter()-halfsize);
        vec.x=EESignedRound(vec.x);
        vec.y=EESignedRound(vec.y);
        copy.setCenter(vec+halfsize);
        EE::Sys().Renderer.setView(copy);
}
inline float EESignedRound(float a)//rounds float to nearest integer and returns it as float, respects the sign
{
        return (a>=0)?static_cast<float>(static_cast<int>(a+0.5f)):static_cast<float>(static_cast<int>(a-0.5f));
}
Of course replace EE::Sys().Renderer with your render window.
You can download exe from my thread in sfml projects if you want to see a demonstration.
« Last Edit: September 09, 2012, 05:29:03 pm by FRex »
Back to C++ gamedev with SFML in May 2023

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Smooth scrolling large textures
« Reply #14 on: September 09, 2012, 05:40:42 pm »
Thank you all. In the vertex array definition, I only used cast to stop the compiler fom complaining about "possible loss of data", but I've removed them.

Also, rounding the view movement coordinates fixed the problem with those artifacts. However, all I did now is to (again) cast the float values to int, and although it works, I know it's not the way it supposed to be done.

Here's what eduard engine does to get rid of lines between vertex arrays(but it will cause half pixel diffrences between vertexs arrays and objects that get drawn without pixel perfection and it's a chety workaround)

Thanks for this code, I'll try to re-use the rounding code from this. But I don't get the "half pixel differences". How is this possible to manipulate only "half pixels"? Aren't pixels the smallest screen units?
« Last Edit: September 09, 2012, 05:46:48 pm by N_K »