Cannot see Particle Emitter

Hello ,

I’m using Aaron Perkins code for multiple viewports for a large terrain which works very well. However I’m trying to add a Particle emitter to simulate volcanoes and whatever I do I cant seem see the emitter.

Here is the code I use to create the emitter and multiple viewports (note that the emitter is attached to the rootNode.

I’d be grateful if anyone can spot an obvious flaw.

//******************************* // Add some smoke emitters //*******************************
    smokeEmitter = new ParticleEmitter("Emitter", Type.Triangle, 100);
    Material smokeMat = new Material(assetManager,"Common/MatDefs/Misc/Particle.j3md");
   
    
    smokeMat.setTexture("Texture",assetManager.loadTexture("Materials/flame.png"));
    
    smokeEmitter.setMaterial(smokeMat);
    smokeEmitter.setImagesX(2);
    smokeEmitter.setImagesY(2);

    smokeEmitter.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
    smokeEmitter.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
    smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
    smokeEmitter.setStartSize(1.5f);
    smokeEmitter.setEndSize(0.1f);
    smokeEmitter.setGravity(0, 0, 0);
    smokeEmitter.setLowLife(1f);
    smokeEmitter.setHighLife(3f);
    smokeEmitter.getParticleInfluencer().setVelocityVariation(0.3f);

   //     Vector3f smokeLocation = new Vector3f( craters[0].xpos,0, craters[0].ypos);
   //     smokeLocation.setY(terrain.getHeight(new Vector2f(craters[0].xpos,craters[0].ypos))+3000f);
 
    Vector3f smokeLocation = new Vector3f( 0,0,0);
    smokeLocation.setY(terrain.getHeight(new Vector2f(0,0)));
 
    System.out.println("cx=" + smokeLocation.x + " cy=" + smokeLocation.y + " cz=" + smokeLocation.z + " hscale=" + hScale + " yScale=" + yScale);
    smokeEmitter.setLocalTranslation(smokeLocation);
  //  smokeEmitter.setLocalTranslation(0,terrainMax+200,0);
   
 
    rootNode.attachChild(smokeEmitter);
   
    smokeEmitter.scale(100);
    smokeEmitter.setEnabled(true);

.
.
.

  multiViewport= true;
    
    if(multiViewport)
    {
      farViewPort = this.getViewPort();
      farCam = this.getCamera();
    
      nearCam = this.farCam.clone();
      nearCam.setFrustumPerspective(45f, (float)nearCam.getWidth()/nearCam.getHeight(), 0.1f, 310f);
      farCam.setFrustumPerspective(45f, (float)farCam.getWidth()/farCam.getHeight(), 300f, 1e7f);
    
      nearCam.setViewPort(0f, 1f, 0.0f, 1.0f);
      nearViewPort = this.getRenderManager().createMainView("NearView", nearCam);
   nearViewPort.setClearFlags(false,false,true);
      nearViewPort.attachScene(rootNode);
       
    }

I think you forgot to .emitAllParticles() or whatever the method is called.

I did that after I posted the code and I still can’t see it.

You could create particle effects by using the SDK. Then you can modify parameters easier in real time. Instead of creating the particle effect in code you load the prepared .j3o file.

OK, I’ll try that, thanks.

I implemented visual particles builder in my own Editor :slight_smile:

3 Likes

I can see a flash at the start of the game, but it doesn’t happen again, even though emitAllParticles is in the update loop.

As I understand it, once the particle emitter is started, it keeps on emitting until being told to stop by ‘KillParticles()’. Is that correct ?

If I copy my code to a new project, the emitter runs once and then disappears. How do I make it run continuously ?

To start an emitter, use setEnabled(true); (this is true by default). To stop it, setEnabled(false); When you stop it you will notice that the particles all just pause exactly where they are, and are no longer updated. KillAllParticles(); does not stop the emitter, it just destroys the currently released particles - if it’s still enabled it’ll release new ones.

So if you disable it, then kill all particles, it just vanishes. To stop it from emitting new particles but let the existing ones continue to fade out (like how you’d usually want to use it) you want to use: setParticlesPerSec(value); You can slowly wind it down or just set it straight to 0.

The default value for particlesPerSec is 20.

I can see a flash at the start of the game, but it doesn’t happen again, even though emitAllParticles is in the update loop.

emitAllParticles will just emit all of them in one go, and no matter the release speed it won’t be able to release more until some die. You don’t want to be using this in simpleUpdate every frame (usually).

When I saw viewports I instantly thought that the emitter isn’t getting updated, but you attach it to the rootNode so I am assuming that isn’t the problem in the original case.

smokeEmitter.scale(100);

Probably worth mentioning, this will not scale the particles.

Instead of the particles originating from a specific point, you can make them come from random positions within a shape.

emitter.setShape(new EmitterBoxShape(new Vector3f(-1.8f, -1.8f, -1.8f),
                                          new Vector3f(1.8f, 1.8f, 1.8f)));

(from the TestPointSprite.java example)

Bunch of other shapes you can use: http://javadoc.jmonkeyengine.org/com/jme3/effect/shapes/EmitterShape.html

This shape will be effected by that scale value.

1 Like

OK, I’m getting confused now.

Here is my simplest code.

The emitter is a global variable.

It appears the case that once the emitter has done its job it no longer exists, because when t > 10 in my Update loop I get a “Null pointer” exception on the setEnabled(true) line in the update loop.

What do I need to do to avoid recreating the emitter every time I want to use it ? I want to simulate a volcano erupting, so it will be virtually continuous emmission.

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

/**

  • test
  • @author normenhansen
    */

public class Main extends SimpleApplication {

public  ParticleEmitter smokeEmitter;
public float            time = 0;

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

@Override
public void simpleInitApp() {
   
   ParticleEmitter smokeEmitter;
   smokeEmitter = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 100);
    Material smokeMat = new Material(assetManager,"Common/MatDefs/Misc/Particle.j3md");
   
    
    smokeMat.setTexture("Texture",assetManager.loadTexture("Materials/flame.png"));

    smokeEmitter.setMaterial(smokeMat);
    smokeEmitter.setImagesX(2);
    smokeEmitter.setImagesY(2);

    smokeEmitter.setEndColor(  new ColorRGBA(0.6f, 0f, 0f, 1f));   // red
    smokeEmitter.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
    smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
    smokeEmitter.setParticlesPerSec(100);
    smokeEmitter.setStartSize(1f);
    smokeEmitter.setEndSize(2f);
    smokeEmitter.setGravity(0, 5, 0);
    smokeEmitter.setLowLife(1500f);
    smokeEmitter.setHighLife(2800f);
    smokeEmitter.scale(0.1f);
    smokeEmitter.setEnabled(true);

    Vector3f smokeLocation = new Vector3f( 0,0,-200);

    smokeEmitter.setLocalTranslation(smokeLocation);
    
      rootNode.attachChild(smokeEmitter);
    
  //  smokeEmitter.emitAllParticles();
    
    ///************ End Smoke Emitter************* ///
}

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

   time += tpf;
   System.out.println("Time=" + time) ;
   
   if (time > 10) 
   {
       smokeEmitter.setEnabled(true);
       time = 0;
   }

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

}

So here you have your ParticleEmitter named smokeEmitter

And here you create a reference to another ParticleEmitter, with the same name. So your original one which simpleUpdate is accessing hasn’t been initialized yet :stuck_out_tongue:

So no re-creation required.

1 Like

Well spotted, it’s always a simple thing.

Thanks, I’ll carry on now.

I once spent hours trying to fix my code without noticing a ; after an if statement. Swear programming is ageing me before my time.

It’s always handy to have ‘peer review’ :slight_smile:

1 Like

Still can’t get it working with multiple viewports.

: (

I’m just off, 7 am here, but if you make a simple test case with 2 viewports I’ll gladly have a go at it when I wake up. Pretty sure I had this problem a few months back myself.

1 Like

I can get it to work with a very simple dual viewport with no other objects except the emitter, but when I add terrain I can’t see the emitter.

I have set the local translation of the emitter to the same as an object in the root node that I can see , but it still doesn’t appear, so I’m at a loss.

If you have a single class example which reproduce the issue I would past the code here (don’t forget the three back ticks befor and after to have it nicely formatted).

Is the document pointed to here

https://docs.jmonkeyengine.org/advanced/particle_emitters.html

Available elsewhere ?, as this link is broken.

https://jmonkeyengine.github.io/wiki/jme3/advanced/particle_emitters.html

My Full code is too long to post, but I think these are the relevant bits

// create particle emitter variable

public    ParticleEmitter      smokeEmitter;

// in InitApp, call routine to initialis the emitter

CreateSmokeEmitter();

// CreateSmokeEmitter procedure

////////////////////////////////////////////////////////
// Create Smokje Emitter
////////////////////////////////////////////////////////

public void CreateSmokeEmitter()
{
    smokeEmitter = new ParticleEmitter("Emitter", Type.Triangle, 100);
    smokeMat = new Material(assetManager,"Common/MatDefs/Misc/Particle.j3md");
   
    
    smokeMat.setTexture("Texture",assetManager.loadTexture("Materials/flame.png"));
    
    smokeEmitter.setShape(new EmitterBoxShape(new Vector3f(0,0,0),
                                              new Vector3f(15,19,15)));
    smokeEmitter.setMaterial(smokeMat);
    smokeEmitter.setImagesX(2);
    smokeEmitter.setImagesY(2);
    smokeEmitter.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
    smokeEmitter.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
    smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
    smokeEmitter.setParticlesPerSec(100);
    smokeEmitter.setStartSize(1f);
    smokeEmitter.setEndSize(2f);
    smokeEmitter.setGravity(0, 5, 0);
    smokeEmitter.setLowLife(1f);
    smokeEmitter.setHighLife(3f);
     
    rootNode.attachChild(smokeEmitter);
}

// part of a routine to position the emitter

public void AttachBases(int stepSize, int numpoints, float xscale, float yscale, float zscale)
{
float bx, by;
float x, y, baseHeight;
boolean doBases = true;
float level ;
Vector2f basePos = new Vector2f();

    Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    mat.setColor("Diffuse", ColorRGBA.White);
    mat.setColor("Ambient", ColorRGBA.White);
    mat.setTexture("DiffuseMap", assetManager.loadTexture("Materials/base.png"));
    
    

    if(doBases) 
   {     
    theBases = new Bases(stepSize, (int)(numpoints), (int)xscale) ;
       
    for(int i=0;i< theBases.numBases;i++)
    {
      // System.out.println("Base " + i + " x= " + theBases.theBases[i].x + " y=" + theBases.theBases[i].y);
    }
    
   for(int i=0; i < theBases.numBases;i++ )     
    {     
        bx = theBases.xBases[i].x;
        by = theBases.xBases[i].y;
    
      
        Box  base = new Box(xscale/2f, xscale/2f, xscale/2f);
        Geometry baseGeom = new Geometry("Base" + i, base);        
                
        baseGeom.setMaterial(mat);
        Vector3f baseLoc = new Vector3f(bx,0,by);
        baseHeight = terrain.getHeight(new Vector2f( bx,by));
        baseLoc.setY(baseHeight-xscale/4f);
        
        
        baseGeom.setLocalTranslation(baseLoc);
        RigidBodyControl r = new RigidBodyControl(0f);
        baseGeom.addControl(r);
        bulletAppState.getPhysicsSpace().add(r); 
        baseGeom.setQueueBucket(Bucket.Transparent);
        baseGeom.setShadowMode(ShadowMode.Cast);
        rootNode.attachChild(baseGeom);
        
        // Attach a smoke emitter
        
        if(i==0)
        {
            Vector3f smokeLoc = baseLoc;
            smokeLoc.add(1000,5000,1000);
        //    smokeEmitter.scale(xscale,xscale,xscale);
            smokeEmitter.setLocalTranslation(smokeLoc);
          
          
            
        }
    }
    
    
   }
}

// Multiple viewport setup

 if(multiViewport)
    {
      farViewPort = this.getViewPort();
      farCam = this.getCamera();
    
      nearCam = this.farCam.clone();
      nearCam.setFrustumPerspective(45f, (float)nearCam.getWidth()/nearCam.getHeight(), 0.1f, 310f);
      farCam.setFrustumPerspective(45f, (float)farCam.getWidth()/farCam.getHeight(), 300f, 1e7f);
    
      nearCam.setViewPort(0f, 1f, 0.0f, 1.0f);
      nearViewPort = this.getRenderManager().createMainView("NearView", nearCam);
      nearViewPort.setBackgroundColor(ColorRGBA.BlackNoAlpha);
   nearViewPort.setClearFlags(false,false,false);
      nearViewPort.attachScene(rootNode);
 
    }

// Update Routine

public void simpleUpdate(float tpf) {
    //TODO: add update code
    
   float      frust;
   float      bRange;
   boolean    dothis = false;
   float      time   = 0;    

   time += tpf;
   if(time >20)
   {
     //  smokeEmitter.emitAllParticles();
     //  time = 0;
   }
   
   fp.UpdateFlightParameters(tpf*140); // slow the game down by increasing the tpf factor
 
   if(! smokeEmitter.isEnabled())smokeEmitter.setEnabled(true);

.
.
}

If anyone has the time to look at this , thank you.