Skip to content

Instantly share code, notes, and snippets.

@WideWord
Last active March 12, 2016 19:15
Show Gist options
  • Save WideWord/5c0d7e9f1b35116bd1f8 to your computer and use it in GitHub Desktop.
Save WideWord/5c0d7e9f1b35116bd1f8 to your computer and use it in GitHub Desktop.
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <list>
#include <math.h>
const int screenSize = 800;
float length(sf::Vector2f vector) {
return sqrtf(vector.x * vector.x + vector.y * vector.y);
}
sf::Vector2f norm(sf::Vector2f vector) {
return vector / length(vector);
}
float dot(sf::Vector2f a, sf::Vector2f b) {
return a.x * b.x + a.y * b.y;
}
float cross(sf::Vector2f a, sf::Vector2f b) {
return a.x * b.y - b.x * a.y;
}
bool intersect(sf::Vector2f a1, sf::Vector2f a2, sf::Vector2f b1, sf::Vector2f b2, sf::Vector2f& intersectionPoint) {
auto p = a1;
auto r = a2 - a1;
auto q = b1;
auto s = b2 - b1;
if (cross(r, s) < 0.001f) return false;
auto t = cross(q - p, s) / cross(r, s);
auto u = cross(-(p - q), r) / cross(r, s);
if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
intersectionPoint = p + t * r;
return true;
} else {
return false;
}
}
struct Particle {
sf::Vector2f position;
sf::Vector2f speed;
bool isActive;
sf::Clock lifetime;
};
struct Emitter {
sf::Vector2f position;
float frequency;
sf::Vector2f direction;
float randomSpeedFactor;
};
struct AttractionPoint {
sf::Vector2f position;
float power;
};
struct Wall {
sf::RectangleShape shape;
};
std::list<Particle> particles;
std::list<Emitter> emitters;
std::list<AttractionPoint> attractionPoints;
std::list<Wall> walls;
Particle& newParticle() {
for (auto& particle : particles) {
if (!particle.isActive) {
return particle;
}
}
particles.push_back(Particle());
return particles.front();
}
sf::Vector2f randVector() {
return sf::Vector2f((float)(rand() % 1000 - 500) / 500, (float)(rand() % 1000 - 500) / 500);
}
int main() {
sf::RenderWindow window(sf::VideoMode(screenSize, screenSize), "Particles");
sf::Clock clock;
{
Emitter emitter;
emitter.position = sf::Vector2f(150, 400);
emitter.frequency = 500;
emitter.direction = sf::Vector2f(400, 0);
emitter.randomSpeedFactor = 50;
emitters.push_back(emitter);
AttractionPoint point;
point.position = sf::Vector2f(400, 600);
point.power = 0;
attractionPoints.push_back(point);
Wall wall;
wall.shape.setPosition(600, 400 - 150);
wall.shape.setSize(sf::Vector2f(20, 300));
walls.push_back(wall);
}
while (window.isOpen()) {
float deltaTime = clock.getElapsedTime().asSeconds();
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}
if (deltaTime < 1.0f / 60.0f) {
continue;
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
attractionPoints.front().position = sf::Vector2f(sf::Mouse::getPosition(window));
attractionPoints.front().power = 100000;
} else {
attractionPoints.front().power = 0;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
walls.front().shape.rotate(1);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
walls.front().shape.rotate(-1);
}
window.clear(sf::Color::Black);
for (auto& emitter : emitters) {
auto newParticleCount = ceilf(emitter.frequency * deltaTime);
for (int i = 0; i < newParticleCount; ++i) {
auto &particle = newParticle();
particle.isActive = true;
particle.lifetime.restart();
particle.position = emitter.position;
particle.speed = emitter.direction + norm(randVector()) * (float)(rand() % 1000) / 1000.0f * emitter.randomSpeedFactor;
}
}
for (auto& particle : particles) {
if (!particle.isActive) continue;
if (particle.lifetime.getElapsedTime().asSeconds() > 10.0f) particle.isActive = false;
for (auto attractionPoint : attractionPoints) {
auto particleToAttraction = attractionPoint.position - particle.position;
auto len = length(particleToAttraction);
particle.speed = particle.speed + particleToAttraction / (len * len * len) * attractionPoint.power;
}
bool isIntersectWall = false;
sf::Vector2f intersectionPoint;
sf::Vector2f intersectionNormal;
float intersectionDistance = 1000000;
for (auto wall : walls) {
auto bounds = wall.shape.getGlobalBounds();
auto boundsInset = length(particle.speed) * deltaTime + 5;
bounds.height += boundsInset * 2;
bounds.width += boundsInset * 2;
bounds.top -= boundsInset;
bounds.left -= boundsInset;
if (!bounds.contains(particle.position)) continue;
auto pointCount = wall.shape.getPointCount();
for (int i = 0; i <= pointCount; ++i) {
auto start = wall.shape.getPoint(i);
auto end = wall.shape.getPoint(i == pointCount - 1 ? 0 : i + 1);
start = wall.shape.getTransform().transformPoint(start);
end = wall.shape.getTransform().transformPoint(end);
sf::Vector2f curIntersectionPoint;
if (intersect(start, end, particle.position, particle.position + particle.speed * deltaTime, curIntersectionPoint)) {
float curIntersectionDistance = length(particle.position - curIntersectionPoint);
if (!isIntersectWall || curIntersectionDistance < intersectionDistance) {
isIntersectWall = true;
intersectionDistance = curIntersectionDistance;
intersectionPoint = curIntersectionPoint;
auto ray = start - end;
auto normal = norm(sf::Vector2f(-ray.y, ray.x));
if (dot(normal, particle.speed) > 0) normal = -normal;
intersectionNormal = normal;
}
}
}
}
if (isIntersectWall) {
particle.speed = -2 * dot(particle.speed, intersectionNormal) * intersectionNormal + particle.speed;
auto partBeforeIntersect = intersectionDistance / length(particle.speed * deltaTime);
particle.position = intersectionPoint + particle.speed * deltaTime * (1.0f - partBeforeIntersect);
} else {
particle.position += particle.speed * deltaTime;
}
sf::CircleShape cirlce;
cirlce.setPosition(particle.position);
cirlce.setRadius(1);
cirlce.setFillColor(sf::Color::Yellow);
window.draw(cirlce);
}
/*
for (auto attractionPoint : attractionPoints) {
sf::CircleShape cirlce;
cirlce.setPosition(attractionPoint.position);
cirlce.setRadius(1);
cirlce.setFillColor(sf::Color::White);
window.draw(cirlce);
}
*/
for (auto wall : walls) {
auto shape = wall.shape;
shape.setFillColor(sf::Color(255,255,255,127));
window.draw(shape);
}
clock.restart();
window.display();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment