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