ParticleFuseKernel

The ParticleFuseKernel is a particle kernel which fuses particles together if they get too close to each other. It duplicates the functionality of Nuke’s ParticleFuse node and demonstrates
random access to the particle images as well as how to retire particles.

Change the paFuseDistance parameter to set the direciton of the wind and the paDrag parameter to alter the air resistance.

// A kernel which fuses particles which get too close to each other. It demonstrates
// random access to particles and expiring particles.
kernel ParticleFuseKernel : ImageComputationKernel<ePixelWise>
{
  Image<eRead,eAccessRandom> p_position;
  Image<eReadWrite,eAccessRandom> p_life;
  Image<eReadWrite,eAccessRandom> p_size;
  Image<eReadWrite,eAccessRandom> p_mass;
  Image<eReadWrite,eAccessRandom> p_velocity;

  param:
    float _fuseDistance;
    float _dt;

  local:
    int _numParticles;

  void define() {
    defineParam(_fuseDistance, "paFuseDistance", 0.1f);
    defineParam(_dt, "_dt", 1.0f);
  }

  void init() {
    _numParticles = p_position.bounds.height();
  }

  void process(int2 pos) {
    const float kFuseDistanceSquared = _fuseDistance * _fuseDistance;

    int i = pos.y;
    if ( p_life(0, i).x > 0 ) {
      float3 lposition = p_position(0, i);

      // Loop through all the other particles
      for (int j = 0; j < i; ++j) {
        // Ignore particles that are already dead
        if (p_life(0, j).x == 0 )
          continue;

        // Are the two particles closer than the fuse distance?
        float3 gap = p_position(0, j) - lposition;
        float distanceSquared = dot(gap, gap);
        if (distanceSquared < kFuseDistanceSquared) {
          p_life(0, j) = 0.0f; // Kill the other particle
          // ...and merge its properties with this one
          float mi = p_mass(0, i);
          float mj = p_mass(0, j);
          float3 si = p_size(0, i);
          float3 sj = p_size(0, j);
          float3 vi = p_velocity(0, i);
          float3 vj = p_velocity(0, j);
          p_size(0, i) = si+sj; // Should really take the cube root here to keep volume constant
          p_mass(0, i) = mi+mj;
          p_velocity(0, i) = (mi*vi+mj*vj)/(mi+mj);
        }
      }
    }
  }
};