-
Hello, I've wanted to make a small particle system in SFML. I created the first version using the sf::RectangleShape class to make the little particles and it worked fine but was quite slow (started impacting FPS at around 1500 particles) so I re-wrote a bit of it using vertex arrays which appears to be much faster but the actual window has weird black lines in it:
(http://i43.tinypic.com/2lj44g0.png)
Here's my source file, please note that this is only a rough experiment and so is a bit messy:
#include <iostream>
#include <string>
#include <stdio.h>
#include <vector>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
using namespace std;
sf::Vector2f Randomize(int Boundary1, int Boundary2)
{
return sf::Vector2f((0 +((rand() % Boundary1))), (0 +((rand() % Boundary2))));
}
int RandomizeInt(int Boundary1, int Boundary2)
{
return (Boundary1 +((rand() % Boundary2)));
}
struct PI
{
sf::Color Colour = sf::Color::Yellow;
int X = 0;
int Y = 0;
int LifeTime = 20000;
};
int main()
{
int FPS = 0; //Setting up variables for measuring the FPS
sf::Clock FPSClock;
sf::Time FPSTime;
sf::Music music;
if (!music.openFromFile("sound.ogg"))
return -1; // error
music.play();
bool MousePressed = false;
sf::Clock EffectClock;
sf::Time EffectTime;
int ActiveParticles = 0; //How many particles are active
int ParticleCap = 0; //The limit of how many particles can be placed
cout << "\nParticle Cap: ";
cin >> ParticleCap;
PI ParticleInfo[ParticleCap]; //Our particle information such as colour and lifetime.
sf::VertexArray Pixel(sf::Points, ParticleCap);
sf::RenderWindow window(sf::VideoMode(800, 600), "Partical System");
window.setVerticalSyncEnabled(true);
while (window.isOpen())
{
sf::Vector2i MousePosition = sf::Mouse::getPosition(window); //Storing mouse coords for later use
if (sf::Mouse::isButtonPressed(sf::Mouse::Left) && MousePressed == false) //Checking if mouse is pressed
{
for(int a = 0; a < ParticleCap; a++) //Updating particle information
{
Pixel[a].position = sf::Vector2f(MousePosition.x,MousePosition.y);
ParticleInfo[a].X = MousePosition.x;
ParticleInfo[a].Y = MousePosition.y;
}
MousePressed = true;
EffectClock.restart();
}
else if(!sf::Mouse::isButtonPressed(sf::Mouse::Left) && MousePressed == true)
{
MousePressed = false;
}
sf::Event event; //Polling window events to check for user input
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
EffectTime = EffectClock.getElapsedTime();
for(int a = 0; a < ParticleCap; a++) //Updating particle information
{
ParticleInfo[a].LifeTime = RandomizeInt(0,40);
ParticleInfo[a].X = ParticleInfo[a].X + RandomizeInt(0,6) - 3;
ParticleInfo[a].Y = ParticleInfo[a].Y + RandomizeInt(0,6) - 3;
Pixel[a].color = ParticleInfo[a].Colour;
if(EffectTime.asMilliseconds() > ParticleInfo[a].LifeTime)
{
//Pixel[a].position = sf::Vector2f(-1,-1);
ParticleInfo[a].X = MousePosition.x;
ParticleInfo[a].Y = MousePosition.y;
Pixel[a].position = sf::Vector2f(ParticleInfo[a].X, ParticleInfo[a].Y);
EffectClock.restart();
}
else
{
Pixel[a].position = sf::Vector2f(ParticleInfo[a].X, ParticleInfo[a].Y - 6);
ParticleInfo[a].Y = ParticleInfo[a].Y - 6;
}
}
window.clear(sf::Color::Black);
window.draw(Pixel);
window.display();
FPS++; //Calculating FPS and displaying it
FPSTime = FPSClock.getElapsedTime();
if(FPSTime.asSeconds() >= 1)
{
cout << "\nFPS: " << FPS << endl;
FPSClock.restart();
FPS = 0;
}
}
return 0;
}
Why is this?
-
I don't have those weird lines, so it could be a driver issue.
(http://i.imgur.com/Zj6UezV.jpg)
If you don't want the particles to form a yellow block, you can increase the spawning area and the lifetime and decrease the number of particles.
ParticleInfo[a].LifeTime = RandomizeInt(15,40);
ParticleInfo[a].X = ParticleInfo[a].X + RandomizeInt(0,20) - 10;
ParticleInfo[a].Y = ParticleInfo[a].Y + RandomizeInt(0,20) - 10;
(http://i.imgur.com/E0RHiUN.jpg)
-
Hm, what compiler are you using?
Altering the way that the particles are spread does not alter the lines in the window and the lines don't move with the particles, they stay stationary. All of my drivers are up to data.
-
What if you minimize the code example to a sf::Points vertex array that contains static points? For example if you build a rectangle out of points, do the black lines still appear? Avoid everything that's not directly necessary, and keep the code as small as possible.
If they do not, the problem may also be related to the random generator. Linear congruential generators produce extremely bad random numbers, recognizable patterns are typical for them. You could try with an engine from <random> instead.
-
Oh, I've made shapes out of vertexes before and they do not appear. I've tried seeding the generator and that hasn't changed it.
-
It's not about seeding, the regular patterns are an inherent property of linear congruential generators.
I'm not sure if it's that in your case -- that's why I stated you should try the random engines from the <random> header (http://en.cppreference.com/w/cpp/numeric/random) and see if they change something. If you happen use Thor, you could also call thor::random() (http://www.bromeon.ch/libraries/thor/v2.0/doc/_random_8hpp.html).
-
I'm using MinGW 4.8 32bit on Windows 7 with a nVidia GeForce GTX 680 and the latest drivers.
I commented the music part out, but that shouldn't make any difference.
This code should give you a completely white window:
#include <SFML/Graphics.hpp>
int main() {
int windowWidth = 800;
int windowHeight = 600;
sf::VertexArray vertices(sf::PrimitiveType::Points);
for (int y = 0; y <= windowHeight; ++y) {
for (int x = 0; x <= windowWidth; ++x) {
vertices.append(sf::Vertex(sf::Vector2f(x, y), sf::Color::White));
}
}
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "Test", sf::Style::Close);
window.setVerticalSyncEnabled(true);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
window.clear();
window.draw(vertices);
window.display();
}
return 0;
}
-
Interesting...
(http://i41.tinypic.com/28vxbfm.png)
Why's this?
-
Probably a driver issue. Can you try the same test, but with sf::Vector2f(x + 0.5, y + 0.5) for coordinates?
-
Strange. What if you choose the correct vertex positions (i.e. center of pixels, not left-upper corner) and don't draw vertices outside the window? That is:
for (int y = 0; y < windowHeight; ++y) {
for (int x = 0; x < windowWidth; ++x) {
vertices.append(sf::Vertex(sf::Vector2f(x+0.5f, y+0.5f), sf::Color::White));
}
}
-
It is a completely white screen if I do that.
-
Ok, then do it like that :)
(It's a result of the pixel rasterization. OpenGL leaves some freedom to the decision how pixels "on the border" are handled. Usually however, drivers act consistently -- I'm not sure to what degree they have to).
-
Thankyou for your help everyone :D
Do I have to add the +0.5f after every movement or is there a quicker way of doing it?
Here's what I have now which works great:
#include <iostream>
#include <string>
#include <stdio.h>
#include <vector>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
using namespace std;
sf::Vector2f Randomize(int Boundary1, int Boundary2)
{
srand(time(NULL));
return sf::Vector2f((0 +((rand() % Boundary1))), (0 +((rand() % Boundary2))));
}
int RandomizeInt(int Boundary1, int Boundary2)
{
return (Boundary1 +((rand() % Boundary2)));
}
struct PI
{
sf::Color Colour = sf::Color::Yellow;
int X = 0;
int Y = 0;
int LifeTime = 20000;
};
int main()
{
int FPS = 0; //Setting up variables for measuring the FPS
sf::Clock FPSClock;
sf::Time FPSTime;
sf::Music music;
if (!music.openFromFile("sound.ogg"))
return -1; // error
music.play();
bool MousePressed = false;
sf::Clock EffectClock;
sf::Time EffectTime;
int ActiveParticles = 0; //How many particles are active
int ParticleCap = 0; //The limit of how many particles can be placed
cout << "\nParticle Cap: ";
cin >> ParticleCap;
PI ParticleInfo[ParticleCap]; //Our particle information such as colour and lifetime.
sf::VertexArray Pixel(sf::Points, ParticleCap);
sf::RenderWindow window(sf::VideoMode(800, 600), "Partical System", sf::Style::Default);
window.setVerticalSyncEnabled(true);
while (window.isOpen())
{
sf::Vector2i MousePosition = sf::Mouse::getPosition(window); //Storing mouse coords for later use
if (sf::Mouse::isButtonPressed(sf::Mouse::Left) && MousePressed == false) //Checking if mouse is pressed
{
for(int a = 0; a < ParticleCap; a++) //Updating particle information
{
Pixel[a].position = sf::Vector2f(MousePosition.x+0.5f,MousePosition.y+0.5f);
ParticleInfo[a].X = MousePosition.x+0.5f;
ParticleInfo[a].Y = MousePosition.y+0.5f;
}
MousePressed = true;
EffectClock.restart();
}
else if(!sf::Mouse::isButtonPressed(sf::Mouse::Left) && MousePressed == true)
{
MousePressed = false;
}
sf::Event event; //Polling window events to check for user input
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
EffectTime = EffectClock.getElapsedTime();
for(int a = 0; a < ParticleCap; a++) //Updating particle information
{
ParticleInfo[a].LifeTime = RandomizeInt(0,40);
ParticleInfo[a].X = ParticleInfo[a].X + RandomizeInt(0,6) - 3;
ParticleInfo[a].Y = ParticleInfo[a].Y + RandomizeInt(0,6) - 3;
Pixel[a].color = ParticleInfo[a].Colour;
if(EffectTime.asMilliseconds() > ParticleInfo[a].LifeTime)
{
//Pixel[a].position = sf::Vector2f(-1,-1);
ParticleInfo[a].X = MousePosition.x;
ParticleInfo[a].Y = MousePosition.y;
Pixel[a].position = sf::Vector2f(ParticleInfo[a].X+0.5f, ParticleInfo[a].Y+0.5f);
EffectClock.restart();
}
else
{
Pixel[a].position = sf::Vector2f(ParticleInfo[a].X+0.5f, (ParticleInfo[a].Y - 6)+0.5f);
ParticleInfo[a].Y = (ParticleInfo[a].Y - 6)+0.5f;
}
}
window.clear(sf::Color::Black);
window.draw(Pixel);
window.display();
FPS++; //Calculating FPS and displaying it
FPSTime = FPSClock.getElapsedTime();
if(FPSTime.asSeconds() >= 1)
{
cout << "\nFPS: " << FPS << endl;
FPSClock.restart();
FPS = 0;
}
}
return 0;
}
Thanks, and sorry for the late replies... I've been filling in college application forms...