Click here to Skip to main content
Click here to Skip to main content

Flexible particle system - Emitter and Generators

, 16 May 2014
Rate this:
Please Sign up or sign in to vote.
Description of my emitter and generator module for particle system
particle emitter

In our particle system we have already a basic foundation: the container and the framework. Now we need some modules that can actually wake particles. In this post I will describe the emitter module and generators.

The Series

Introduction

Basic design:

  • SRP principle: particle system contains a particle container, list of emitters, list of updaters. It does only basic stuff like initialization, cleanup and manages the update procedure.
  • Generators can generate one or several different attributes for a particle.
  • An emitter holds one or more generators.
  • Updating and killing particles are left to updaters.

The gist is located here: fenbf / BasicParticleGenerators

Emitter loop

void ParticleEmitter::emit(double dt, ParticleData *p)
{
    const size_t maxNewParticles = static_cast<size_t>(dt*m_emitRate);
    const size_t startId = p->m_countAlive;
    const size_t endId = std::min(startId + maxNewParticles, p->m_count-1);

    for (auto &gen : m_generators)            // << gen loop
        gen->generate(dt, p, startId, endId);

    for (size_t i = startId; i < endId; ++i)  // << wake loop
        p->wake(i);
}

The idea: an emitter should emit a number of particles each frame. The pace of course depends on emit rate. The emitter should generate all needed attributes, but each attribute can be set by a different generator. So we have One to Many relation.

In the gen loop we call generators code. Each generator will set parameters for particles ranging from startId up to endId.

Then in the wake loop we wake selected particles.

Generator

A generator should now be actually a quite simple module: just take a range of particles and set new values for some parameter. All the 'complex' code was handled already by the particle system and the emitter (generator's parent).

Here is an example of BoxPosGen

class BoxPosGen : public ParticleGenerator
{
public:
    glm::vec4 m_pos{ 0.0 };
    glm::vec4 m_maxStartPosOffset{ 0.0 };
public:
    BoxPosGen() { }

    virtual void generate(double dt, ParticleData *p, 
                          size_t startId, size_t endId) override;
};

void BoxPosGen::generate(double dt, ParticleData *p, size_t startId, size_t endId)
{
    glm::vec4 posMin{ m_pos.x - m_maxStartPosOffset.x, 
                      m_pos.y - m_maxStartPosOffset.y, 
                      m_pos.z - m_maxStartPosOffset.z, 
                      1.0 };
    glm::vec4 posMax{ m_pos.x + m_maxStartPosOffset.x, 
                      m_pos.y + m_maxStartPosOffset.y, 
                      m_pos.z + m_maxStartPosOffset.z, 
                      1.0 };

    for (size_t i = startId; i < endId; ++i)
    {
        p->m_pos[i] = glm::linearRand(posMin, posMax);
    }
}

Thanks to this idea we can have a set of different generators and combine them into various emitters!

Other generators:

  • RoundPosGen - generates particle's position around the the circle (XY axis only)
  • BasicColorGen - generates start and end color for a particle.
  • BasicVelGen - velocity only, you can set min and max on each axis.
  • SphereVelGen - velocity vector is generated from a sphere around point
  • BasicTimeGen - time generation: between min and max

Example emitter

Emitter that uses RoundPosGen, BasicColorGen, BasicVelGen and BasicTimeGen:

auto particleEmitter = std::make_shared<ParticleEmitter>();
{
    particleEmitter->m_emitRate = (float)NUM_PARTICLES*0.45f;

    // pos:
    auto posGenerator = std::make_shared<generators::RoundPosGen>();
    posGenerator->m_center = glm::vec4{ 0.0, 0.0, 0.0, 0.0 };
    posGenerator->m_radX = 0.15f;
    posGenerator->m_radY = 0.15f;
    particleEmitter->addGenerator(posGenerator);

    auto colGenerator = std::make_shared<generators::BasicColorGen>();
    colGenerator->m_minStartCol = glm::vec4{ 0.7, 0.0, 0.7, 1.0 };
    colGenerator->m_maxStartCol = glm::vec4{ 1.0, 1.0, 1.0, 1.0 };
    colGenerator->m_minEndCol = glm::vec4{ 0.5, 0.0, 0.6, 0.0 };
    colGenerator->m_maxEndCol = glm::vec4{ 0.7, 0.5, 1.0, 0.0 };
    particleEmitter->addGenerator(colGenerator);

    auto velGenerator = std::make_shared<generators::BasicVelGen>();
    velGenerator->m_minStartVel = glm::vec4{ 0.0f, 0.0f, 0.15f, 0.0f };
    velGenerator->m_maxStartVel = glm::vec4{ 0.0f, 0.0f, 0.45f, 0.0f };
    particleEmitter->addGenerator(velGenerator);

    auto timeGenerator = std::make_shared<generators::BasicTimeGen>();
    timeGenerator->m_minTime = 1.0;
    timeGenerator->m_maxTime = 3.5;
    particleEmitter->addGenerator(timeGenerator);
}
m_system->addEmitter(particleEmitter);

circle particle emitter

Final Notes

I think that SRP principle helps a lot in this design. The code seems to be simple and straightforward to read. Each module does only one thing.

Another advantage of the system is that we can 'easily' translate this into a visual editor. You create a system, then add emitter, then fill it with different generators. The whole system can be set up from small blocks.

Are there any disadvantages? You need to understand the whole hierarchy of particle updaters/generators. For a simple system probably that is too much, but over the time such solution should help.

What's Next

Generators and emitters are useless when there is no Update mechanism! Next time I will describe such system in my particle 'engine'.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Bartlomiej Filipek
Software Developer
Poland Poland
Software developer interested in creating great code and passionate about teaching.
 
Technologies I use(d): C++, C#, JavaScript, OpenGL, GLSL, DirectX, OpenCL, CUDA, Windows Api, MFC, Visual Studio and even HTML and CSS.
 
See my programming blog: www.bfilipek.com
Follow on   Twitter   Google+

Comments and Discussions

 
QuestionNice PinmemberManikandan1018-May-14 6:06 
AnswerRe: Nice PinmemberBartlomiej Filipek18-May-14 9:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 16 May 2014
Article Copyright 2014 by Bartlomiej Filipek
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid