[SOLVED] Particles from a model's shape

Hi, monkeys!

I want to create a particle system that emits particles from a model’s shape. I thought I found the solution with the EmitterMeshFaceShape class, but it won’t work if I change the scale of the model since it uses the original Mesh object as a base for the particles. Is there any other way to achieve what I want? Or is there a way to change the Mesh's size with the Geometrys?

Here is a sample code so you can see what I am trying to do:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.effect.shapes.EmitterMeshFaceShape;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author fba
 */
public class ParticlesOnMesh extends SimpleApplication {

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

    @Override
    public void simpleInitApp() {
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);

        AmbientLight ambient = new AmbientLight();
        ambient.setColor(ColorRGBA.White.mult(.3f));
        rootNode.addLight(ambient);

        Spatial model = assetManager.loadModel("Models/Boat/boat.j3o");
        model.setName("Model");
        rootNode.attachChild(model);
        rootNode.attachChild(createParticles());

        // Uncomment the following line to see what happens when the scale is changed:
        // model.scale(.5f);

        // Uncomment the following line to see the particles only:
        // rootNode.detachChild(model);
    }

    private ParticleEmitter createParticles() {
        ParticleEmitter emitter = new ParticleEmitter("Particles", ParticleMesh.Type.Triangle, 10000);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
        mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
        emitter.setMaterial(mat);

        emitter.setStartColor(new ColorRGBA(1, .9725f, 0, 1));
        emitter.setEndColor(new ColorRGBA(1, .1255f, 0, 1));
        emitter.setStartSize(.1f);
        emitter.setEndSize(.1f);
        emitter.setLowLife(2);
        emitter.setHighLife(5);
        emitter.setImagesX(2);
        emitter.setImagesY(2);
        emitter.setParticlesPerSec(3000);
        emitter.setGravity(Vector3f.ZERO);

        List<Mesh> meshes = new ArrayList<>();
        addMeshesToList(rootNode.getChild("Model"), meshes);
        emitter.setShape(new EmitterMeshFaceShape(meshes)); // <-- Here the magic happens

        return emitter;
    }

    private void addMeshesToList(Spatial spatial, List<Mesh> meshes) {
        if (spatial instanceof Node) {
            for (Spatial s : ((Node) spatial).getChildren()) {
                if (s instanceof Node) {
                    addMeshesToList(s, meshes);
                } else {
                    meshes.add(((Geometry) s).getMesh());
                }
            }
        } else {
            meshes.add(((Geometry) spatial).getMesh());
        }
    }
}

Doesn’t startSize and endSize define the aforementioned scale?

You mean on the ParticleEmitter class? That’s for the initial and final sizes of each particle.

I’m quite sure it’s a scale, not a size, which is what you want, isn’t it? Or do I not understand?

If you want to alter an actual mesh, just multiply it’s positions by a scale… I’m not sure what you are trying to achieve if that doesn’t help :confused:

Thankyou, @jayfella!
Based on that information and after some peeking on jME’s code, I created this method that solves the problem.

    private void scaleMeshes(List<Mesh> meshes, float scale) {
        for (Mesh mesh : meshes) {
            SafeArrayList<VertexBuffer> list = mesh.getBufferList();
            for (VertexBuffer vb : list) {
                if (vb.getBufferType() == VertexBuffer.Type.Position) {
                    FloatBuffer b = (FloatBuffer) vb.getData();
                    for (int i = 0; i < b.capacity(); i++) {
                        b.put(i, b.get(i) * scale);
                    }
                    vb.updateData(b);
                }
            }
        }
    }