Drawing 2D stuff

Introduction

As you learnt in the previous tutorials, the window module of SFML provides an easy way to open an OpenGL window and handle its events but it doesn't help when it comes to draw something. The only option which is left to you is to use the powerful-yet-complex-and-low-level OpenGL API.

Fortunately, SFML provides a graphics module which will help you draw 2D entities in a simpler way than with OpenGL.

The drawing window

To draw the entities provided by the graphics module, you must use a specialized window class: sf::RenderWindow. This class derives from sf::Window, and inherits all its functions. Everything that you've learnt about sf::Window (creation, event handling, controlling the framerate, mixing with OpenGL, etc.) is applicable to sf::RenderWindow.

On top of that, sf::RenderWindow adds high-level functions to help you draw things easily. In this tutorial we'll focus on two of these functions: clear and draw. They are as simple as their name: clear clears the whole window with the chosen color, and draw draws whatever object you give to it.

Here is what a typical main loop looks like with a render window:

#include <SFML/Graphics.hpp>

int main()
{
    // create the window
    sf::RenderWindow window(sf::VideoMode(800, 600), "My window");

    // run the program as long as the window is open
    while (window.isOpen())
    {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                window.close();
        }

        // clear the window with black color
        window.clear(sf::Color::Black);

        // draw everything here...
        // window.draw(...);

        // end the current frame
        window.display();
    }

    return 0;
}

Calling clear before drawing anything is mandatory, otherwise you may keep undefined pixels from previous frames. The only exception is when you cover the entire window with what you draw, so that no pixel is left undrawn; in this case you can avoid calling clear (although it won't make a big difference on performances).

Calling display is also mandatory, it takes what was drawn since the last call to display and displays it on the window. Indeed, things are not drawn directly to the window, but to a hidden buffer. This buffer is then copied to the window when you call display -- this is called double-buffering.

This clear/draw/display cycle is the only good way to draw things. Don't try other strategies, such as keeping pixels from the previous frame, "erasing" pixels, or drawing once and calling display multiple times. You'll get strange results due to double-buffering.
Modern graphics chipsets and APIs are really made for repeated clear/draw/display cycles where everything is completely refreshed at each iteration of the main loop. Don't be scared to draw 1000 sprites 60 times per second, you're far below the millions of triangles that your computer can handle.

What can I draw now?

Now that you have a main loop which is ready to draw, let's see what, and how, you can actually draw there.

SFML provides four kinds of drawable entities: three of them are ready to be used (sprites, texts and shapes), the last one is the building block that will help you to create your own drawable entities (vertex arrays).

Although they share some common properties, these entities have their own specificities and deserve their own tutorials:

Off-screen drawing

SFML also provides a way to draw to a texture instead of directly to a window. To do so, use a sf::RenderTexture instead of a sf::RenderWindow. It has the same functions for drawing, inherited from their common base sf::RenderTarget.

// create a 500x500 render-texture
sf::RenderTexture renderTexture;
if (!renderTexture.create(500, 500))
{
    // error...
}

// drawing uses the same functions
renderTexture.clear();
renderTexture.draw(sprite); // or any other drawable
renderTexture.display();

// get the target texture (where the stuff has been drawn)
const sf::Texture& texture = renderTexture.getTexture();

// draw it to the window
sf::Sprite sprite(texture);
window.draw(sprite);

The getTexture function returns a read-only texture, which means that you can only use it, not modify it. If you need to modify it before using it, you can copy it to your own sf::Texture instance.

sf::RenderTexture also has the same functions as sf::RenderWindow for handling views and OpenGL (see the corresponding tutorials for more details). If you use OpenGL to draw to the render-texture, you can have a depth buffer by using the third optional argument of the create function.

renderTexture.create(500, 500, true); // enable depth buffer

Drawing from threads

SFML supports multi-threaded drawing, and you don't even have to do anything to make it work. The only thing to remember is to deactivate a window before using it in another thread; that's because a window (more precisely its OpenGL context) cannot be active in multiple threads at the same time.

void renderingThread(sf::RenderWindow* window)
{
    // the rendering loop
    while (window->isOpen())
    {
        // draw...

        // end the current frame
        window->display();
    }
}

int main()
{
    // create the window (remember: it's safer to create it in the main thread due to OS limitations)
    sf::RenderWindow window(sf::VideoMode(800, 600), "OpenGL");

    // deactivate its OpenGL context
    window.setActive(false);

    // launch the rendering thread
    sf::Thread thread(&renderingThread, &window);
    thread.launch();

    // the event/logic/whatever loop
    while (window.isOpen())
    {
        ...
    }

    return 0;
}

As you can see, you don't even need to bother with the activation of the window in the rendering thread, SFML does it automatically for you whenever it needs to be done.

Remember to always create the window and handle its events in the main thread, for maximum portability, as explained in the window tutorial.