How to draw on a single buffer for generative art purposes - c++

I'm actually facing a issue I can't solve by myself, using SFML I have a single shape, and I'm moving it at each frame, and then I draw it. The problem is, SFML using two buffers, the trail won't be the same on each buffer (frame) and it will give a weird blinking effect.
I need your knowledge, should I find another way of doing it, like an array of shapes that I'll extend. Or is there a way to skip the second buffer? Making this buffer a piece of paper, and my shape the brush.
sf::CircleShape circle;
//Init the circle (radius etc)
sf::Vector2f pos(0, 500);
while(!done)
{
pos.x += 1;
circle.setPosition(pos);
window.draw(circle);
window.display();
}
May you spend a nice day.

you can achieve that by using sf::RenderTexture which acts as the back buffer (single buffer) to make your drawing and then draw it on screen via sf::RenderWindow`
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML art");
window.setVerticalSyncEnabled(true);
sf::RenderTexture target;
if (!target.create(window.getSize().x, window.getSize().y))
return -1;
target.clear(sf::Color::White);
target.display();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::CircleShape circle(5.f, 32u);
circle.setPosition(sf::Vector2f(sf::Mouse::getPosition(window)));
circle.setFillColor(sf::Color::Red);
target.draw(circle, sf::BlendNone);
target.display();
}
window.clear();
window.draw(sf::Sprite(target.getTexture()));
window.display();
}
}

The purpose of double buffering is to avoid showing parts of not fully rendered scene during rendering of a frame. So your problem has nothing to do with double-buffering itself. Basically, you need to generate all the primitives necessary to render a particular frame every time. In your specific case you could achieve this like this:
sf::CircleShape circle;
//Init the circle (radius etc)
sf::Vector2f pos(0, 500);
int frameCount = 0;
while(!done)
{
++frameCount;
for (int i = 0; i < frameCount; ++i) {
circle.setPosition(pos + sf::Vector2f(i, 0));
window.draw(circle);
}
window.display();
}

Related

Getting a Pixel to print to the screen using SFML & C++

My main goal is to convert a bunch of pairs of numbers from a text file (numbers.txt) and assign each of these numbers as pixel coordinates. Then I need to print a pixel at each of those coordinates that will eventually form a picture of the United States composed of only single pixels.
Right now, what I'm doing is just trying to get any pixel of any color to print to the screen using SFML. Maybe if I can get that far I can somehow take the numbers stored in numbers.txt and convert them to coordinates with a pixel at each one.
Here's my code so far...
#include <iostream>
#include <fstream> // the fstream class
#include <SFML/Graphics.hpp>
#include <string> // will help to use strings
using namespace std;
int main()
{
string line;
int width, height;
fstream myFile;
myFile.open("resources/numbers.txt");
if (myFile)
{
cout << "This file is opened\n";
}
else
return EXIT_FAILURE;
cout << "Enter 2 integer values";
cin >> width >> height;
// Print a black window to the screen
sf::RenderWindow window(sf::VideoMode(width, height), "MAP");
sf::Texture texture;
sf::Sprite sprite;
sprite.setTexture(texture);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
window.close();
break;
}
}
window.clear();
//window.draw(sprite);
//*************DRAW THE SPRITE HERE****************
// create a new vertex
sf::Vertex vertex;
// set its position
vertex.position = sf::Vector2f(10.f, 50.f);
// set its color
vertex.color = sf::Color::Red;
// set its texture coordinates
vertex.texCoords = sf::Vector2f(100.f, 100.f);
sf::VertexArray pixel(sf::Points);
window.draw(pixel);
//display window
window.display();
}
return 0;
}
This does compile fine. But only displays a black screen to the user. I need to somehow get a pixel on that black screen. I am very new to SFML and C++ and looked up on the SFML mainpage about points and pixels but only got this far. Can any of you guys help? It's super appreciated!
All the best.
there are several approaches in SFML to draw a single pixel on the screen. The first one is what you are trying to do with sf::VertexArray.
In my opinion the simpler and more intuitive is approach with sf::RectangleShape where you can create rectangle with 1x1 size and this one I am going to present you.
Firstly, we can add helpful function which will create "pixel" at given position and color.
sf::RectangleShape addPixel(sf::Vector2f position, sf::Uint8 red, sf::Uint8 green, sf::Uint8 blue)
{
sf::RectangleShape pixel;
pixel.setSize({ 1.f, 1.f });
pixel.setFillColor({ red, green, blue });
pixel.setPosition(position);
return pixel;
}
That function will return sf::RectangleShape with the color created from RGBs values passed as arguments to that function.
But you have to store these pixels somewhere.
So let's create window, and some container to hold our pixels, e.g. std::vector<sf::RectangleShape>
and add to it some pixels:
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
std::vector<sf::RectangleShape> pixels;
pixels.push_back(addPixel({ 100, 100 }, 255, 0, 0));
pixels.push_back(addPixel({ 101, 100 }, 255, 255, 0));
pixels.push_back(addPixel({ 102, 100 }, 0, 0, 0));
pixels.push_back(addPixel({ 103, 100 }, 255, 0, 255));
Finally you create main loop where you draw all pixels in each iteration.
while (window.isOpen())
{
window.clear();
for (const auto& pixel : pixels)
{
window.draw(pixel);
}
window.display();
}
Make sure you add more pixels before trying to display it. On high resolution screen single pixels could be invisible.
EDIT: You code also works but you forgot to add sf::Vertex to sf::VertexArray.
All you should do is:
sf::Vertex vertex1;
vertex1.position = sf::Vector2f(10.f, 50.f);
vertex1.color = sf::Color::Red;
sf::VertexArray pixel;
pixel.append(vertex1); // That's what you forgot.
window.draw(pixel);
As above, with this approach also add more sf::Vertex next to each other, otherwise you may not notice single pixel on the screen.
Hope it helps!

SFML Views vs window confusion

I have this single c++ / SFML code...
#include <SFML/Graphics.hpp>
#define windowWidth 600
#define windowHeight 300
int main()
{
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "SFML Views");
sf::View view(sf::FloatRect(0,0, windowWidth, windowHeight));
view.zoom(2);
window.setView(view);
sf::RectangleShape back (sf::Vector2f(windowWidth, windowHeight));
back.setFillColor(sf::Color::White);
sf::RectangleShape rect (sf::Vector2f(200, 100));
rect.setFillColor(sf::Color::Red);
rect.setPosition(windowWidth - rect.getSize().x, windowHeight - rect.getSize().y); // position in the lower right corner
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(back);
window.draw(rect);
window.display();
}
return 0;
}
...which might position the red rectangle in the lower right corner of the window.
However, when I zoom the view in (as in the code), it obviously moves along with the entire window like this image.
I have some doubts:
To correct the positioning of the red rectangle and place it in the lower right corner of the global window, I currently have to do some calculations considering the zoom factor, original rectangle size, etc. Is there any easier way to position this rectangle in the lower-right corner of the global window?
How do I prevent some objects from being resized by the zoom of the view?
What do I have to do to have multiple active views in the same window?
Is there any easier way to position this rectangle in the lower-right corner of the global window?
From SFML 2D camera tutorial: "To draw something using a view, you must draw it after calling the setView function of the target to which you are drawing"
So the drawing part should be
window.clear();
window.setView(view);
window.draw(back);
window.setView(window.getDefaultView()); //don't zoom red rect
window.draw(rect);
window.display();
What do I have to do to have multiple active views in the same window?
Just call setView() for each active view and then drawing all the things in that view (again, if needed)
window.setView(leftHalfView);
window.draw(a);
window.draw(b);
window.draw(c);
window.setView(rightHalfView);
window.draw(a);
window.draw(b);
window.draw(c);
window.setView(minimap);
window.draw(a);
window.draw(b);
window.setView(window.getDefaultView());
window.draw(x);
window.draw(y);

SFML Pixel Perfect Collision limited to a view?

In this code I have one view (white, zoom 2) and the original window, also one sprite for a car and one sprite for a wall...
#include <SFML/Graphics.hpp>
#include "collision.hpp"
#define windowWidth 1000
#define windowHeight 600
int main()
{
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "SFML Views");
sf::View view(sf::FloatRect(0,0, windowWidth, windowHeight));
view.zoom(2);
// car
sf::Texture imgCar;
Collision::CreateTextureAndBitmask(imgCar, "carro.png" );
sf::Sprite car;
car.setTexture(imgCar);
car.setPosition(0, (windowHeight - imgCar.getSize().y) / 2);
// wall
sf::Texture imgWall;
Collision::CreateTextureAndBitmask( imgWall, "barreira.png" );
sf::Sprite wall;
wall.setTexture(imgWall);
wall.setPosition(windowWidth - imgWall.getSize().x, 0);
sf::RectangleShape background (sf::Vector2f(windowWidth, windowHeight));
background.setFillColor(sf::Color::White);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
if (!Collision::PixelPerfectTest(car, wall))
car.move(0.1, 0);
window.clear();
window.setView(view);
window.draw(background);
window.setView(window.getDefaultView());
window.draw(car);
window.draw(wall);
window.display();
}
return 0;
}
... if both car and wall are related with the main window, I get a perfect car collision with the wall. (see image).
Now if I put the car inside the view ...
window.clear();
window.setView(view);
window.draw(background);
window.draw(car);
window.setView(window.getDefaultView());
window.draw(wall);
window.display();
... the collision is detected as if the wall were within the boundaries of the view (which is zoomed in) (see image).
How can I make the collision detection independent of the view?

SFML 2.4.2 Co-ordinate system offset

I am writing a small program using SFML which draws a 5 x 5 rectangle at any point specified by clicking the mouse.
I am using mapPixelToCoords() to map the mouse coordinates to the pixels as recommended in the documentation, however I am still encountering a weird offset between the coordinates and the actual shape which is drawn.
The offset varies on different parts of the window (it is not a constant x,y value).
I would like the rectangle to be drawn precisely under the mouse pointer, how do I go about removing this offset?
This is the code I'm using to draw the rectangle on click:
Screenshot of offset in the window
int main(){
sf::RenderWindow window(sf::VideoMode(800, 720), "SFML window");
sf::Event event;
//Rectangle to be drawn on the screen
sf::RectangleShape someRect;
someRect.setFillColor(sf::Color(0, 0, 0));
someRect.setSize({ 5, 5 });
someRect.setPosition(100, 120);
while (window.isOpen())
{
if (!windowStarted) {
event.type = sf::Event::Closed;
}
while (window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::MouseButtonPressed) {
someRect.setPosition(window.mapPixelToCoords(sf::Mouse::getPosition(window)));
}
}
window.clear(sf::Color(255, 255, 255));
window.draw(someRect);
window.display();
}
return 0;
}

(2.3.1) Set scale of background texture to renderwindow size

Just started learning SFML (so please bear with me).
I have made a RenderWindow object, and I have a image I want to fit perfectly to that window.
By searching through the documentation, I found the sf::Sprite::SetScale function. Is this the right function to do it? But how do I set the scale of the sprite to the scale of the RenderWindow, when the RenderWindow object size is set in pixels? Do I have to get the scale of the RenderWindow, and then assign the background sprite to that scale or?
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(600, 300), "Simple Game");
sf::Texture BackgroundTexture;
sf::Sprite background;
//background.setScale(0.2, 0.2); <--- how?
if(!BackgroundTexture.loadFromFile("media/background.png"))
{
return -1;
}
else
{
background.setTexture(BackgroundTexture);
}
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
switch(event.type)
{
case sf::Event::Closed:
window.close();
}
}
window.clear();
window.draw(background);
window.display();
}
}
The scale factor you need is simply the ratio between the window size and the texture size. sf::Texture has a getSize function, as has sf::RenderWindow. Simply get the sizes of both, calculate the ratio and use it to set the scale of the sprite, like this:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(600, 300), "Simple Game");
sf::Texture BackgroundTexture;
sf::Sprite background;
sf::Vector2u TextureSize; //Added to store texture size.
sf::Vector2u WindowSize; //Added to store window size.
if(!BackgroundTexture.loadFromFile("background.png"))
{
return -1;
}
else
{
TextureSize = BackgroundTexture.getSize(); //Get size of texture.
WindowSize = window.getSize(); //Get size of window.
float ScaleX = (float) WindowSize.x / TextureSize.x;
float ScaleY = (float) WindowSize.y / TextureSize.y; //Calculate scale.
background.setTexture(BackgroundTexture);
background.setScale(ScaleX, ScaleY); //Set scale.
}
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
switch(event.type)
{
case sf::Event::Closed:
window.close();
}
}
window.clear();
window.draw(background);
window.display();
}
}