ParticleEmitter improvement

Hello everybody.

I know that there is a lot of topic about that, but i would like to open a new one and share my solution/approach.

First of all, all the thanks goes to Kirill Vainer, the original author of the ParticleEmitter.
Ok, that said, what is the problem ? The problem is : we don’t have control on particles after they are emitted. Also, a change on this part of the code must not break anything.

So, here is my approach:

  1. change everything “private” to “protected”. I know it’s not the same thing, and i know what is the difference and why people use private. Actually, you learn to use “private” and it becomes a reflex. It’s a good reflex, no problem here. But, for the ParticleEmitter that so many people complain about it for everything it lacks, it would be a good solution. There is still no “i didn’t notice i shouldn’t touch that and everything is broken” and you have the capability of modify it if you really want it.

  2. create a ParticlesUpdater. I know, there is already a ParticleInfluencer but it’s an interface and if i add something on it, i’ll break compatibillity. What is the job of a ParticlesUpdater ? Here is the interface:

    public interface ParticlesUpdater
    {
    /**
    * Is called when a particle is newly used. As particles can be reused, don’t rely on “p” being actually a new object.
    * @param p The used particle.
    /
    public void newParticle(Particle p);
    /
    *
    * Is called when a particle need an update.
    * @param p The particle to update.
    * @param tpf The frame duration.
    * @param min min size of the particle.
    * @param max max size of the particle.
    * @param emitter The emitter of the particle.
    */
    public void update(Particle p, float tpf, Vector3f min, Vector3f max, ParticleEmitter emitter);
    }

For the second method, i really don’t like the “min” and “max” thing, but it comes from the header of the particleUpdate method of the ParticleEmitter.

  1. Modify the ParticleEmitter. As everything in it was private, i had to copy-past it [and with it ParticleMesh ('case a method of ParticleMesh take a ParticleEmitter as parameter and my class, even if copy pasted, is not the good ParticleEmitter and doesn’t extend it), ParticlePointMesh and ParticleTriMesh (these both cause they are affected to a ParticleMesh objet … yeah, once again my class doesn’t extend the original class].

Here is the modifications:

// The ParticlesUpdater used, with a state-less default value.
protected ParticlesUpdater particlesUpdater = DefaultParticlesUpdater.instance;

// Getter and setter.
public ParticlesUpdater getParticlesUpdater()
{
  return particlesUpdater;
}

public void setParticlesUpdater(ParticlesUpdater particlesUpdater)
{
  this.particlesUpdater = particlesUpdater;
}

in the emitParticle method:

p.color.set(startColor);
p.size = startSize;
//shape.getRandomPoint(p.position);
particleInfluencer.influenceParticle(p, shape);
particlesUpdater.newParticle(p);// <- MODIFICATION
if (worldSpace)
{
  worldTransform.transformVector(p.position, p.position);
  worldTransform.getRotation().mult(p.velocity, p.velocity);

And this method was completly replaced:

protected void updateParticle(Particle p, float tpf, Vector3f min, Vector3f max)
{
  particlesUpdater.update(p, tpf, min, max, this);
}
  1. Profit ! Ok, just befure that, the DefaultParticlesUpdated class :smile:

    public class DefaultParticlesUpdater extends AbstractParticlesUpdater
    {
    public static final DefaultParticlesUpdater instance = new DefaultParticlesUpdater();
    private transient Vector3f temp = new Vector3f();

    @Override
    protected void applyGravity(Particle p, float tpf, ParticleEmitter emitter)
    {
    Vector3f gravity = emitter.getGravity();

     p.velocity.x -= gravity.x * tpf;
     p.velocity.y -= gravity.y * tpf;
     p.velocity.z -= gravity.z * tpf;
     temp.set(p.velocity).multLocal(tpf);
     p.position.addLocal(temp);
    

    }

    @Override
    protected void affectColor(Particle p, float lifePercent, ParticleEmitter emitter)
    {
    ColorRGBA startColor = emitter.getStartColor();
    ColorRGBA endColor = emitter.getEndColor();

     p.color.interpolate(startColor, endColor, lifePercent);
    

    }

    @Override
    protected void affectSize(Particle p, float lifePercent, ParticleEmitter emitter)
    {
    float startSize = emitter.getStartSize();
    float endSize = emitter.getEndSize();

     p.size = FastMath.interpolateLinear(lifePercent, startSize, endSize);
    

    }

    @Override
    protected void affectAngle(Particle p, float tpf, ParticleEmitter emitter)
    {
    p.angle += p.rotateSpeed * tpf;
    }

    @Override
    protected void computeBoundingVolume(Particle p, Vector3f max, Vector3f min)
    {
    temp.set(p.position).addLocal(p.size, p.size, p.size);
    max.maxLocal(temp);
    temp.set(p.position).subtractLocal(p.size, p.size, p.size);
    min.minLocal(temp);
    }

    @Override
    protected void selectImage(Particle p, float lifePercent, ParticleEmitter emitter)
    {
    boolean selectRandomImage = emitter.isSelectRandomImage();
    int imagesX = emitter.getImagesX();
    int imagesY = emitter.getImagesY();

     if (!selectRandomImage)
     {
       p.imageIndex = (int) (lifePercent * imagesX * imagesY);
     }
    

    }
    }

Most of its code comes from the old updateParticle method.
The AbstractParticlesUpdater:

public class AbstractParticlesUpdater implements ParticlesUpdater
{
  @Override
  public void update(Particle p, float tpf, Vector3f min, Vector3f max, ParticleEmitter emitter)
  {
    // Applying gravity
    applyGravity(p, tpf, emitter);

    // Affecting color, size and angle
    float lifePercent = (p.startlife - p.life) / p.startlife;
    affectColor(p, lifePercent, emitter);
    affectSize(p, lifePercent, emitter);
    affectAngle(p, tpf, emitter);

    // Computing bounding volume
    computeBoundingVolume(p, max, min);

    // Selecting image
    selectImage(p, lifePercent, emitter);
  }
  
  protected void applyGravity(Particle p, float tpf, ParticleEmitter emitter)
  {
  }
  
  protected void affectColor(Particle p, float lifePercent, ParticleEmitter emitter)
  {
  }
  
  protected void affectSize(Particle p, float lifePercent, ParticleEmitter emitter)
  {
  }
  
  protected void affectAngle(Particle p, float tpf, ParticleEmitter emitter)
  {
  }
  
  protected void computeBoundingVolume(Particle p, Vector3f max, Vector3f min)
  {
  }
  
  protected void selectImage(Particle p, float lifePercent, ParticleEmitter emitter)
  {
  }

  @Override
  public void newParticle(Particle p)
  {
  }
}

As you can see, everything is pretty trivial. But now let’s see a class a bit less trivial:

public class AnimSpriteParticlesUpdater extends AbstractParticlesUpdater
{
  protected List<Point2i[]> paths;
  protected Map<Particle, Point2i[]> particlesPaths;
  
  public AnimSpriteParticlesUpdater()
  {
    particlesPaths = new HashMap<>();
    paths = new ArrayList<>();
  }
  
  public void addPath(Point2i ... path)
  {
    paths.add(path);
  }
  
  @Override
  protected void selectImage(Particle p, float lifePercent, ParticleEmitter emitter)
  {
    int imagesX = emitter.getImagesX();
    int imagesY = emitter.getImagesY();
    
    Point2i[] particlePath = particlesPaths.get(p);
    Point2i tileIndex = particlePath[(int)(lifePercent * particlePath.length)];

    p.imageIndex = (imagesX * tileIndex.y) + tileIndex.x;
  }
  
  @Override
  public void newParticle(Particle p)
  {
    // Select a random path for the particle
    particlesPaths.put(p, selectAPath());
  }
  
  protected Point2i[] selectAPath()
  {
    assert(! paths.isEmpty());
    return paths.get((int) (paths.size() * FastMath.nextRandomFloat()));
  }
}

Ok, this class is not stateless and should be instanciated for every ParticleEmitter (otherwise you have a risk of memory leak with the map holding references to particles).

What this class does ? It allows the user to define a set of possible series of apparence for a particle. Exemple of use below:

public class Main extends SimpleApplication
{
  public static void main(String[] args)
  {
    SimpleApplication app = new Main();
    app.start();
  }

  @Override
  public void simpleInitApp()
  {
    
    ParticleEmitter character = new ParticleEmitter("Character", ParticleMesh.Type.Triangle, 2);
    character.setParticlesPerSec(1f);
    character.setLowLife(2f);
    character.setHighLife(2f);
    character.setStartSize(3);
    character.setEndSize(3);
    character.setImagesX(4);
    character.setImagesY(4);
    character.setShape(new EmitterSphereShape(Vector3f.ZERO, 5));

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setTexture("ColorMap", assetManager.loadTexture("Textures/ogre.png"));
    mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
    character.setMaterial(mat);

    AnimSpriteParticlesUpdater aspu = new AnimSpriteParticlesUpdater();
    for (int i = 0; i < 4; i++)
    {
      Point2i[] path = new Point2i[4];
      for (int j = 0; j < 4; j++)
      {
        path[j] = new Point2i(j, i);
      }
      aspu.addPath(path);
    }
    character.setParticlesUpdater(aspu);
    
    rootNode.attachChild(character);
  }

  @Override
  public void simpleUpdate(float tpf)
  {
    //TODO: add update code
  }

  @Override
  public void simpleRender(RenderManager rm)
  {
    //TODO: add render code
  }
}

Where the texture “ogre.png” is an ogre character like in rpg maker xp (4*4, walking in every directions). This code create particle showing the ogre walking.

I also created an updater that create like a gravity vortex with particles around, but i need to clean the code.

I can zip and post everything if you need.

I think that this approach would solve almost every limitation the current system has. I think, it’s my opinion, i am not sure. But, for exemple, it’s pretty easy to create a ParticlesUpdater that just delegate most of it’s work to a chain of other updaters.
It would also be nice to have a way to store datas about particles if not in particles directly then in the emitter responsible of thse particles. That way, there would be no risk of memory leak from here.

it was my 2 cent. Comments ? Ideas ? Reproachs ? Everything is good, and i hope that this time we will success in improving the particleemitter system.

1 Like

Actually making fields “private” is safer than “protected” beceause it’s prevents side effects, ie you should only modify fields by accessors (set, get …) of the class that know how it’s should be done. Plus if you change the data structure of the field you just have to modify the accessors.

Did you saw the work done by @t0neg0d : Google Code Archive - Long-term storage for Google Code Project Hosting.
and this contribution : http://wiki.jmonkeyengine.org/doku.php/jme3:contributions:particles

I agree that the actual particleEmitter can be improved (and also the sdk particle editor omg portal 3 confirmed)

Well, i said i do know the difference between private and protected, i do know why we use one and not the other etc. Yes, i know all of this and in most case private is fine. I was just saying that in this particular case, the problem is still in the trunk, even though a lot of suggestion and code have been shared. And a reason why was: we don’t want to change the code if it creates bug in existing code (typically because the interface of a class changed). So, make private things protected is a valid “half-way” solution. Beginners will not mess with the internal of the class (they will not even think of extending it) and professionnals will know what to do. Just put a fat comment saying “do that at your own risk” in capitals and we are good to go.

I already saw the second link, but both these links have the same problem: they redo everything. My idea is: fix what already exists, modify it without throwing away everything linked to what exists now. You can have tools that uses ParticleEmitter (manage pools of them etc.) and the “re-do the broken tool” just end with “re-do just everything”. Plus, it duplicates functionnalities.

I am perfectly ok to think of a “beat-everything-with-one-hand” approach for jme4, but for now we are still in jme3, and ParticleEmitter was part of the release version if i am not wrong. So, modifications in the code can only be done behind the curtain, in a way that existing working code will still work after the modification. You can replace the code of the existing ParticleEmitter with mine, and you’ll not see a difference in existing codes (providing that nobody extended the ParticleEmitter and added a method with name “getParticlesUpdater” (the setter will always be fine). And even for that, i can modify my code to handle this exact situation where a bug could occur).

But thanks for the reply anyway (these remarks had to be addressed).

I think it is okay to have some compatibility breakage in jME 3.1, which is why I think its best to add those new capabilities to ParticleInfluencer instead of making a new ParticleUpdater whose purpose is the same but with different interface.
Most users don’t have their own ParticleInfluencers, so it shouldn’t break anybody’s code.

1 Like

Agree, plus i was planning to modify the SDK in the way to support custom ParticleInfluencers or kind of.

edit : by SDK i mean the visual editor for particles emitters in the scene composer

It’s already in

There is no dedicated editor for particles though you can edit everything you need through the normal properties panel.

If the base class is changed it’s ok for me. I just want to have the core improved, to have this tool i love improved.
And, just for the record: if you replace the HashMap above with a WeakHashMap you solve all the memory leak issues (and it’s the purpose of a WeakHashMap: don’t keep references on objects that are not referenced somewhere else).

I also think i’ll create a class that hack ParticleEmitter (hello reflection) to provide functionnalities above on the ParticleEmitter of the 3.0 version. If the code is not going to change, i can provide a hack that, then, will not break in the future because of sneaky code modifications.

Not sure why this is needed?
Why not just make the necessary changes in ParticleEmitter, and then have the changes merged in jME 3.1?

1 Like

Cause not everyone will move to 3.1 (i will move, not everyone will). So, if the code in 3.0 is not gonna change (if the choice is “we will do that in 3.1, we will not modify 3.0”) i can try to create something to help people that will decide to stick in 3.0.
We could also take the best of 2 approach: make some method in the 3.0 emitter protected (instead of private, so i can override them), and do a real rework in the 3.1 (and break backward compatibility, but it’s ok, it’s a pure choice with pros and cons).

if the updateParticle method was protected i could just override it and replace its code with mine (and the modification would only affect the extended class, letting the base class as is).
and if the emitParticle was protected, i could override the method like this:

Particle result = super.emitParticle(min, max);
if ( result != null )
  particleUpdater.newParticle(p);

return result;

And it makes sense to mark them protected, it smell like they are doing a precise task and the detail of the implementation of this task could be delegated.

1 Like

Really like improvements to particles, but hey, about jme3.0 compatibility: personally I think it’s time to move on.

Sure, a lot of projects are still built with 3.0; we don’t even have an official 3.1 alpha build; but…
3.0 is basically netbeans 7 which doesn’t support java 8 features. And Java 7 is officially obsoleted by Oracle. :frowning:

Well, there is a difference between jme and its editor. You can use the library in eclipse, and even in newer versions of netbeans.
Also, you can take a program compiled in java7 and recompile it in java8 without changing a single line of code (99% sure about that). You’ll get warnings at most. So, the argument saying “java version changed, the interface of our framework can change too” is invalid.
And the implicit contract of a release (the difference between a release and an alpha/beta) contains the perennity of codes based on it. It’s acceptable that a code written for a certain version (for exemple 3.0) doesn’t work on a newer version (for exemple 3.1). It’s not acceptable that a code written for a version (and a release version) suddenly doesn’t work for this same version.

Most of my remarks here are about that: “how improve the particleemitter without breaking existing code”. I showed that it’s possible, so i don’t see a valid reason to withdraw from the implicit contract.

1 Like

First, I doubt we are going to be pushing out any new 3.0 releases let alone adding new features to a branch that’s purely in bug fix mode.

Second, I hope you had a chance to check out the two different particle effect library rewrites that folks did. There were some intercommunity politics that prevented them from becoming plugins at the time, I guess but I think the source to the second one is still around. Yep:

Wiki page:
http://wiki.jmonkeyengine.org/doku.php/jme3:contributions:particles

And source:
https://code.google.com/p/jmonkeyplatform-contributions/source/browse/#svn%2Ftrunk%2FParticleController

This was going to be a candidate for replacing the existing particle system.

2 Likes

I totally agree with pspeed, I just want to add : 3.1 is a DEV version so you can make more significant changes and not just “hacks”.