1 package com.jme3.effect.shapes;
2 
3 import com.jme3.export.InputCapsule;
4 import com.jme3.export.JmeExporter;
5 import com.jme3.export.JmeImporter;
6 import com.jme3.export.OutputCapsule;
7 import com.jme3.math.FastMath;
8 import com.jme3.math.Vector3f;
9 import com.jme3.scene.Mesh;
10 import com.jme3.scene.VertexBuffer.Type;
11 import com.jme3.util.BufferUtils;
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import 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  */
23 public 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      */
EmitterMeshVertexShape()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      */
EmitterMeshVertexShape(List<Mesh> meshes)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      */
setMeshes(List<Mesh> meshes)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
getRandomPoint(Vector3f store)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
getRandomPointAndNormal(Vector3f store, Vector3f normal)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
deepClone()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
write(JmeExporter ex)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")
read(JmeImporter im)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