Particle System
unknown
c_cpp
4 years ago
5.2 kB
118
Indexable
#include "Emitter.h"
#include <iostream>
#include "ResourceManager.h"
using namespace glm;
/* From the Header */
// -----------------
// Represents a single particle and its state
struct Particle {
vec2 Position, Velocity;
vec4 Color;
float Life;
float Rotation;
short int type;
Particle() : Position(0.0f), Velocity(0.0f), Color(1.0f), Rotation(0.0f), type(0), Life(0.0f) { }
};
// -----------------
std::vector<Particle> Emitter::particles;
unsigned int Emitter::amount;
Shader Emitter::shader;
unsigned int Emitter::SpriteSheetID;
unsigned int Emitter::VAO;
void Emitter::Init( unsigned int a ) {
shader = ResourceManager::GetShader("particle");
SpriteSheetID = SpriteSheet::LoadSpriteSheet("particles", "particles.png", 16, 16);
amount = a;
init();
}
void Emitter::Update(float dt)
{
// update all particles
for (unsigned int i = 0; i < amount; ++i)
{
Particle& p = particles[i];
p.Life -= dt; // reduce life
if (p.Life > 0.0f)
{ // particle is alive, thus update
p.Position -= p.Velocity * dt;
p.Color.a -= dt * 2.5f;
}
}
}
void Emitter::AddParticles(short int type, vec2 position, vec2 velocity, float rotation, unsigned int newParticles, vec2 offset) {
// add new particles
for (unsigned int i = 0; i < newParticles; ++i)
{
int unusedParticle = firstUnusedParticle();
respawnParticle(particles[unusedParticle], type, position, velocity, rotation, offset);
}
}
// render all particles
void Emitter::Draw()
{
// use additive blending to give it a 'glow' effect
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
shader.Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, SpriteSheetID);
for (Particle particle : particles)
{
if (particle.Life > 0.0f)
{
// Prepare transformations
// Create a model matrix
mat4 model = mat4(1.0f);
// Move the sprite to the desired position
model = translate(model, vec3(abs(particle.Position.x) - 15, abs(particle.Position.y) - 15, 0.0f));
model = translate(model, vec3(0.5 * 30, 0.5f * 30, 0.0f)); // Move the sprite so the origin is in the center
model = rotate(model, radians(particle.Rotation), vec3(0.0f, 0.0f, 1.0f)); // Rotate around the center
model = translate(model, vec3(-0.5 * 30, -0.5f * 30, 0.0f)); // Reverse previous translation
model = scale(model, vec3(30, 30, 1.0f)); // Finally, scale if needed
shader.SetMatrix4("model", model); // Set the model matrix
shader.SetVector4f("color", particle.Color);
shader.SetFloat("type", particle.type); // Set the model matrix
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
}
// don't forget to reset to default blending mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void Emitter::init()
{
// set up mesh and attribute properties
unsigned int VBO;
float particle_quad[] = {
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
// fill mesh buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(particle_quad), particle_quad, GL_STATIC_DRAW);
// set mesh attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glBindVertexArray(0);
// create amount default particle instances
for (unsigned int i = 0; i < amount; ++i)
particles.push_back(Particle());
}
// stores the index of the last particle used (for quick access to next dead particle)
unsigned int lastUsedParticle = 0;
unsigned int Emitter::firstUnusedParticle()
{
// first search from last used particle, this will usually return almost instantly
for (unsigned int i = lastUsedParticle; i < amount; ++i) {
if (particles[i].Life <= 0.0f) {
lastUsedParticle = i;
return i;
}
}
// otherwise, do a linear search
for (unsigned int i = 0; i < lastUsedParticle; ++i) {
if (particles[i].Life <= 0.0f) {
lastUsedParticle = i;
return i;
}
}
// all particles are taken, override the first one (note that if it repeatedly hits this case, more particles should be reserved)
lastUsedParticle = 0;
return 0;
}
void Emitter::respawnParticle(Particle& particle, short int type, vec2 position, vec2 velocity, float rotation, vec2 offset)
{
particle.Position = position + offset;
particle.Color = vec4(1, 1, 1, 1.0f);
particle.Life = 1.0f;
particle.type = type;
particle.Rotation = rotation;
particle.Velocity = velocity * 0.1f;
}Editor is loading...