Particle Emitter cannot emit particles if its emissions per second is updated every frame


#1

Within ParticleEmitter if the setParticlesPerSec is called every frame then no particle will ever be emitted. This is because setting ParticlesPerSec resets timeDifference. I can see this was done (in commit 07a6ca9) to solve the problem that after ParticlesPerSec has been zero for a long time timeDifference becomes huge.

The reason someone may want to call setParticlesPerSec every tick is to produce a smooth change in intensity, e.g. a slowly dying fire,

Test case (if reflection is used to just set the parameter it works fine, using the setter doesn’t)

public class Main extends SimpleApplication {

ParticleEmitter fire;
float particlesPerSecond = 10;
boolean useReflectionHack = false;

@Override
public void simpleUpdate(float tpf) {
    try {
        particlesPerSecond -= tpf;
        if (particlesPerSecond < 0) {
            particlesPerSecond = 5;
        }

        if(useReflectionHack){
            Field f = fire.getClass().getDeclaredField("particlesPerSec");
            f.setAccessible(true);
            f.set(fire, particlesPerSecond); //Works fine (in this use case)
        }else{
            fire.setParticlesPerSec(particlesPerSecond); //no particles ever emitted :(
        }
    } catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }
}

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

@Override
public void simpleInitApp() {

    fire =
            new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
    Material mat_red = new Material(assetManager,
            "Common/MatDefs/Misc/Particle.j3md");
    mat_red.setTexture("Texture", assetManager.loadTexture(
            "Effects/Explosion/flame.png"));
    fire.setMaterial(mat_red);
    fire.setImagesX(2);
    fire.setImagesY(2); // 2x2 texture animation
    fire.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
    fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
    fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
    fire.setStartSize(1.5f);
    fire.setEndSize(0.1f);
    fire.setGravity(0, 0, 0);
    fire.setLowLife(1f);
    fire.setHighLife(3f);
    fire.getParticleInfluencer().setVelocityVariation(0.3f);
    fire.setParticlesPerSec(particlesPerSecond);
    rootNode.attachChild(fire);
 }

}

Instead could timeDifference be capped when setParticlesPerSec is called such that at most it is just about to emit a particle. So the new setParticlesPerSec would be

public void setParticlesPerSec(float particlesPerSec) {
    this.particlesPerSec = particlesPerSec;
    timeDifference = Math.min(timeDifference,1f / particlesPerSec); //prevent large accumulated timeDifference from causing a huge number of particles to be emitted
}

Notes:
Original commit discussed here ParticleEmitter particlesPerSecond set to 0 accumulates timeDiff

Original commit https://github.com/jMonkeyEngine/jmonkeyengine/commit/07a6ca9


#2

Good catch. Could you please open an issue and/or file a pull request at GitHub?
https://github.com/jMonkeyEngine/jmonkeyengine


#3

Here is the issue https://github.com/jMonkeyEngine/jmonkeyengine/issues/1113

Here is the PR https://github.com/jMonkeyEngine/jmonkeyengine/pull/1114

(The PR itself seems to have created an issue, have I done this the right way?)


#4

You did good. PRs and issues use the same set of ID numbers, but they’re 2 different things.