1package com.jme3.effect.shapes;
2
3import com.jme3.export.InputCapsule;
4import com.jme3.export.JmeExporter;
5import com.jme3.export.JmeImporter;
6import com.jme3.export.OutputCapsule;
7import com.jme3.math.FastMath;
8import com.jme3.math.Vector3f;
9import com.jme3.scene.Mesh;
10import com.jme3.scene.VertexBuffer.Type;
11import com.jme3.util.BufferUtils;
12import java.io.IOException;
13import java.util.ArrayList;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17import java.util.Map.Entry;
18
19/**
20 * This emiter shape emits the particles from the given shape's vertices
21 * @author Marcin Roguski (Kaelthas)
22 */
23public class EmitterMeshVertexShape implements EmitterShape {
24
25    protected List<List<Vector3f>> vertices;
26    protected List<List<Vector3f>> normals;
27
28    /**
29     * Empty constructor. Sets nothing.
30     */
31    public EmitterMeshVertexShape() {
32    }
33
34    /**
35     * Constructor. It stores a copy of vertex list of all meshes.
36     * @param meshes
37     *        a list of meshes that will form the emitter's shape
38     */
39    public EmitterMeshVertexShape(List<Mesh> meshes) {
40        this.setMeshes(meshes);
41    }
42
43    /**
44     * This method sets the meshes that will form the emiter's shape.
45     * @param meshes
46     *        a list of meshes that will form the emitter's shape
47     */
48    public void setMeshes(List<Mesh> meshes) {
49        Map<Vector3f, Vector3f> vertToNormalMap = new HashMap<Vector3f, Vector3f>();
50
51        this.vertices = new ArrayList<List<Vector3f>>(meshes.size());
52        this.normals = new ArrayList<List<Vector3f>>(meshes.size());
53        for (Mesh mesh : meshes) {
54            // fetching the data
55            float[] vertexTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Position));
56            float[] normalTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Normal));
57
58            // unifying normals
59            for (int i = 0; i < vertexTable.length; i += 3) {// the tables should have the same size and be dividable by 3
60                Vector3f vert = new Vector3f(vertexTable[i], vertexTable[i + 1], vertexTable[i + 2]);
61                Vector3f norm = vertToNormalMap.get(vert);
62                if (norm == null) {
63                    norm = new Vector3f(normalTable[i], normalTable[i + 1], normalTable[i + 2]);
64                    vertToNormalMap.put(vert, norm);
65                } else {
66                    norm.addLocal(normalTable[i], normalTable[i + 1], normalTable[i + 2]);
67                }
68            }
69
70            // adding data to vertices and normals
71            List<Vector3f> vertices = new ArrayList<Vector3f>(vertToNormalMap.size());
72            List<Vector3f> normals = new ArrayList<Vector3f>(vertToNormalMap.size());
73            for (Entry<Vector3f, Vector3f> entry : vertToNormalMap.entrySet()) {
74                vertices.add(entry.getKey());
75                normals.add(entry.getValue().normalizeLocal());
76            }
77            this.vertices.add(vertices);
78            this.normals.add(normals);
79        }
80    }
81
82    /**
83     * This method fills the point with coordinates of randomly selected mesh vertex.
84     * @param store
85     *        the variable to store with coordinates of randomly selected mesh vertex
86     */
87    @Override
88    public void getRandomPoint(Vector3f store) {
89        int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
90        int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() - 1);
91        store.set(vertices.get(meshIndex).get(vertIndex));
92    }
93
94    /**
95     * This method fills the point with coordinates of randomly selected mesh vertex.
96     * The normal param is filled with selected vertex's normal.
97     * @param store
98     *        the variable to store with coordinates of randomly selected mesh vertex
99     * @param normal
100     *        filled with selected vertex's normal
101     */
102    @Override
103    public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
104        int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
105        int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() - 1);
106        store.set(vertices.get(meshIndex).get(vertIndex));
107        normal.set(normals.get(meshIndex).get(vertIndex));
108    }
109
110    @Override
111    public EmitterShape deepClone() {
112        try {
113            EmitterMeshVertexShape clone = (EmitterMeshVertexShape) super.clone();
114            if (this.vertices != null) {
115                clone.vertices = new ArrayList<List<Vector3f>>(vertices.size());
116                for (List<Vector3f> list : vertices) {
117                    List<Vector3f> vectorList = new ArrayList<Vector3f>(list.size());
118                    for (Vector3f vector : list) {
119                        vectorList.add(vector.clone());
120                    }
121                    clone.vertices.add(vectorList);
122                }
123            }
124            if (this.normals != null) {
125                clone.normals = new ArrayList<List<Vector3f>>(normals.size());
126                for (List<Vector3f> list : normals) {
127                    List<Vector3f> vectorList = new ArrayList<Vector3f>(list.size());
128                    for (Vector3f vector : list) {
129                        vectorList.add(vector.clone());
130                    }
131                    clone.normals.add(vectorList);
132                }
133            }
134            return clone;
135        } catch (CloneNotSupportedException e) {
136            throw new AssertionError();
137        }
138    }
139
140    @Override
141    public void write(JmeExporter ex) throws IOException {
142        OutputCapsule oc = ex.getCapsule(this);
143        oc.writeSavableArrayList((ArrayList<List<Vector3f>>) vertices, "vertices", null);
144        oc.writeSavableArrayList((ArrayList<List<Vector3f>>) normals, "normals", null);
145    }
146
147    @Override
148    @SuppressWarnings("unchecked")
149    public void read(JmeImporter im) throws IOException {
150        InputCapsule ic = im.getCapsule(this);
151        this.vertices = ic.readSavableArrayList("vertices", null);
152
153        List<List<Vector3f>> tmpNormals = ic.readSavableArrayList("normals", null);
154        if (tmpNormals != null){
155            this.normals = tmpNormals;
156        }
157    }
158}
159