Last active
March 12, 2016 19:15
-
-
Save WideWord/5c0d7e9f1b35116bd1f8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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