Quantcast
Channel: Bartek's coding blog
Viewing all articles
Browse latest Browse all 325

Flexible particle system - The Container 2

$
0
0
code and implementation
Last time I've written about problems that we can face when designing a particle container. This post will basically show my current (basic - without any optimizations) implementation. I will also write about possible improvements.

The Series

Introduction

Basic design:
  • ParticleData class which represents the container
    • Allocates and manages memory for a given max number of particles
    • Can kill and activate a particle
    • Active particles are in the front of the buffer, stored continuously
    • Each parameter is stored in a separate array. Most of them are 4d vectors
    • No use of std::vectors. The reason: they are very slow in debug mode. Another thing is that I know the max size of elements so managing memory is quite simple. And also I have more control over it.
  • So far GLM library is used, but it might change in the future
  • ParticleSystem holds one ParticleData
  • Generators and Updaters (stored also in ParticleSystem) operate on ParticleData

The declaration

The gist is located here: gist.github.com/fenbf/BasicParticles
ParticleData class
class ParticleData
{
public:
std::unique_ptr<glm::vec4[]> m_pos;
std::unique_ptr<glm::vec4[]> m_col;
std::unique_ptr<glm::vec4[]> m_startCol;
std::unique_ptr<glm::vec4[]> m_endCol;
std::unique_ptr<glm::vec4[]> m_vel;
std::unique_ptr<glm::vec4[]> m_acc;
std::unique_ptr<glm::vec4[]> m_time;
std::unique_ptr<bool[]> m_alive;

size_t m_count{ 0 };
size_t m_countAlive{ 0 };
public:
explicit ParticleData(size_t maxCount) { generate(maxCount); }
~ParticleData() { }

ParticleData(const ParticleData &) = delete;
ParticleData &operator=(const ParticleData &) = delete;

void generate(size_t maxSize);
void kill(size_t id);
void wake(size_t id);
void swapData(size_t a, size_t b);
};
Notes:
  • So far std::unique_ptr are used to hold raw arrays. But this will change, because we will need in the future to allocate aligned memory.

Implementation

Generation:
void ParticleData::generate(size_t maxSize)
{
m_count = maxSize;
m_countAlive = 0;

m_pos.reset(new glm::vec4[maxSize]);
m_col.reset(new glm::vec4[maxSize]);
m_startCol.reset(new glm::vec4[maxSize]);
m_endCol.reset(new glm::vec4[maxSize]);
m_vel.reset(new glm::vec4[maxSize]);
m_acc.reset(new glm::vec4[maxSize]);
m_time.reset(new glm::vec4[maxSize]);
m_alive.reset(new bool[maxSize]);
}
Kill:
void ParticleData::kill(size_t id)
{
if (m_countAlive > 0)
{
m_alive[id] = false;
swapData(id, m_countAlive - 1);
m_countAlive--;
}
}
Wake:
void ParticleData::wake(size_t id)
{
if (m_countAlive < m_count)
{
m_alive[id] = true;
swapData(id, m_countAlive);
m_countAlive++;
}
}
Swap:
void ParticleData::swapData(size_t a, size_t b)
{
std::swap(m_pos[a], m_pos[b]);
std::swap(m_col[a], m_col[b]);
std::swap(m_startCol[a], m_startCol[b]);
std::swap(m_endCol[a], m_endCol[b]);
std::swap(m_vel[a], m_vel[b]);
std::swap(m_acc[a], m_acc[b]);
std::swap(m_time[a], m_time[b]);
std::swap(m_alive[a], m_alive[b]);
}
Hints for optimizations:
  • maybe full swap is not needed?
  • maybe those if's in wake and kill could be removed?

Improvements

Configurable attributes

SoA style object gives use a nice possibility to create various ParticleData configurations. I do not have implemented it in current class, but I've used it before in some other system.
The simplest idea is to hold a mask of configured params:
ParticleData::mask = Params::Pos | Params::Vel | Params::Acc | Params::Color...
In the constructor memory for only selected param will be allocated.
generate() {
// ..
if (mask & Params::Vel)
allocate ParticleData::vel array
// ...
The change is also needed in updaters and generators: briefly we will be able to update only active parameters. A lot of if statements would be needed there. But it is doable.
update() {
// ..
if (mask & Params::Vel)
update ParticleData::vel array
// ...
Please not that the problem arise when one param depends on the other.
Limitations: there is a defined set of parameters, we only can choose a subset.
The second idea (not tested) would be to allow full dynamic configuration. Instead of having named set of available parameters we could store a map of <name, array>. Both name and type of param (vector, scalar, int) would be configurable. This would mean a lot of work, but for some kind of an particle editor this could be a real benefit.

What's Next

In the next article I will touch particle generation and update modules.
Again: the gist is located here: gist.github.com/fenbf/BasicParticles

Viewing all articles
Browse latest Browse all 325

Trending Articles