Changes to particles system:
1. Improvement in cooperation with mesh-shaped emitters. 2. Added particle influencers that calculate initial velocity. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7564 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
5a79adc4fe
commit
49116be02d
@ -48,21 +48,35 @@ public class EmitterBoxShape implements EmitterShape {
|
||||
}
|
||||
|
||||
public EmitterBoxShape(Vector3f min, Vector3f max) {
|
||||
if (min == null || max == null)
|
||||
throw new NullPointerException();
|
||||
if (min == null || max == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.len = new Vector3f();
|
||||
this.len.set(max).subtractLocal(min);
|
||||
}
|
||||
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
@Override
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
store.x = min.x + len.x * FastMath.nextRandomFloat();
|
||||
store.y = min.y + len.y * FastMath.nextRandomFloat();
|
||||
store.z = min.z + len.z * FastMath.nextRandomFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with data.
|
||||
* It does not fill the normal.
|
||||
* @param store the variable to store the point data
|
||||
* @param normal not used in this class
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
|
||||
this.getRandomPoint(store);
|
||||
}
|
||||
|
||||
public EmitterShape deepClone(){
|
||||
@Override
|
||||
public EmitterShape deepClone(){
|
||||
try {
|
||||
EmitterBoxShape clone = (EmitterBoxShape) super.clone();
|
||||
clone.min = min.clone();
|
||||
@ -89,12 +103,14 @@ public class EmitterBoxShape implements EmitterShape {
|
||||
this.len = len;
|
||||
}
|
||||
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(min, "min", null);
|
||||
oc.write(len, "length", null);
|
||||
}
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
min = (Vector3f) ic.readSavable("min", null);
|
||||
len = (Vector3f) ic.readSavable("length", null);
|
||||
|
@ -17,20 +17,46 @@ public class EmitterMeshConvexHullShape extends EmitterMeshFaceShape {
|
||||
* Empty constructor. Sets nothing.
|
||||
*/
|
||||
public EmitterMeshConvexHullShape() {}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor. It stores a copy of vertex list of all meshes.
|
||||
* @param meshes a list of meshes that will form the emitter's shape
|
||||
* @param meshes
|
||||
* a list of meshes that will form the emitter's shape
|
||||
*/
|
||||
public EmitterMeshConvexHullShape(List<Mesh> meshes) {
|
||||
super(meshes);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with coordinates of randomly selected point inside a convex hull
|
||||
* of randomly selected mesh.
|
||||
* @param store
|
||||
* the variable to store with coordinates of randomly selected selected point inside a convex hull
|
||||
* of randomly selected mesh
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
super.getRandomPoint(store);
|
||||
//now move the point from the meshe's face towards the center of the mesh
|
||||
//the center is in (0, 0, 0) in the local coordinates
|
||||
// now move the point from the meshe's face towards the center of the mesh
|
||||
// the center is in (0, 0, 0) in the local coordinates
|
||||
store.multLocal(FastMath.nextRandomFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with coordinates of randomly selected point inside a convex hull
|
||||
* of randomly selected mesh.
|
||||
* The normal param is not used.
|
||||
* @param store
|
||||
* the variable to store with coordinates of randomly selected selected point inside a convex hull
|
||||
* of randomly selected mesh
|
||||
* @param normal
|
||||
* not used in this class
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
|
||||
super.getRandomPointAndNormal(store, normal);
|
||||
// now move the point from the meshe's face towards the center of the mesh
|
||||
// the center is in (0, 0, 0) in the local coordinates
|
||||
store.multLocal(FastMath.nextRandomFloat());
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package com.jme3.effect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* This emiter shape emits the particles from the given shape's faces.
|
||||
@ -15,29 +18,79 @@ public class EmitterMeshFaceShape extends EmitterMeshVertexShape {
|
||||
* Empty constructor. Sets nothing.
|
||||
*/
|
||||
public EmitterMeshFaceShape() {}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor. It stores a copy of vertex list of all meshes.
|
||||
* @param meshes a list of meshes that will form the emitter's shape
|
||||
* @param meshes
|
||||
* a list of meshes that will form the emitter's shape
|
||||
*/
|
||||
public EmitterMeshFaceShape(List<Mesh> meshes) {
|
||||
super(meshes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setMeshes(List<Mesh> meshes) {
|
||||
this.vertices = new ArrayList<List<Vector3f>>(meshes.size());
|
||||
this.normals = new ArrayList<List<Vector3f>>(meshes.size());
|
||||
for (Mesh mesh : meshes) {
|
||||
Vector3f[] vertexTable = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position));
|
||||
int[] indices = new int[3];
|
||||
List<Vector3f> vertices = new ArrayList<Vector3f>(mesh.getTriangleCount() * 3);
|
||||
List<Vector3f> normals = new ArrayList<Vector3f>(mesh.getTriangleCount());
|
||||
for (int i = 0; i < mesh.getTriangleCount(); ++i) {
|
||||
mesh.getTriangle(i, indices);
|
||||
vertices.add(vertexTable[indices[0]]);
|
||||
vertices.add(vertexTable[indices[1]]);
|
||||
vertices.add(vertexTable[indices[2]]);
|
||||
normals.add(FastMath.computeNormal(vertexTable[indices[0]], vertexTable[indices[1]], vertexTable[indices[2]]));
|
||||
}
|
||||
this.vertices.add(vertices);
|
||||
this.normals.add(normals);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with coordinates of randomly selected point on a random face.
|
||||
* @param store
|
||||
* the variable to store with coordinates of randomly selected selected point on a random face
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
int meshIndex = FastMath.nextRandomInt(0, vertices.length-1);
|
||||
//the index of the first vertex of a face (must be dividable by 9)
|
||||
int vertIndex = FastMath.nextRandomInt(0, vertices[meshIndex].length / 9 - 1) * 9;
|
||||
//put the point somewhere between the first and the second vertex of a face
|
||||
int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
|
||||
// the index of the first vertex of a face (must be dividable by 3)
|
||||
int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1) * 3;
|
||||
// put the point somewhere between the first and the second vertex of a face
|
||||
float moveFactor = FastMath.nextRandomFloat();
|
||||
store.set(vertices[meshIndex][vertIndex] + (vertices[meshIndex][vertIndex + 3] - vertices[meshIndex][vertIndex]) * moveFactor,
|
||||
vertices[meshIndex][vertIndex + 1] + (vertices[meshIndex][vertIndex + 4] - vertices[meshIndex][vertIndex + 1]) * moveFactor,
|
||||
vertices[meshIndex][vertIndex + 2] + (vertices[meshIndex][vertIndex + 5] - vertices[meshIndex][vertIndex + 2]) * moveFactor);
|
||||
//move the result towards the last face vertex
|
||||
store.set(Vector3f.ZERO);
|
||||
store.addLocal(vertices.get(meshIndex).get(vertIndex));
|
||||
store.addLocal((vertices.get(meshIndex).get(vertIndex + 1).x - vertices.get(meshIndex).get(vertIndex).x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).y - vertices.get(meshIndex).get(vertIndex).y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).z - vertices.get(meshIndex).get(vertIndex).z) * moveFactor);
|
||||
// move the result towards the last face vertex
|
||||
moveFactor = FastMath.nextRandomFloat();
|
||||
store.addLocal((vertices[meshIndex][vertIndex + 6] - store.x) * moveFactor,
|
||||
(vertices[meshIndex][vertIndex + 7] - store.y) * moveFactor,
|
||||
(vertices[meshIndex][vertIndex + 8] - store.z) * moveFactor);
|
||||
store.addLocal((vertices.get(meshIndex).get(vertIndex + 2).x - store.x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).y - store.y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).z - store.z) * moveFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with coordinates of randomly selected point on a random face.
|
||||
* The normal param is filled with selected face's normal.
|
||||
* @param store
|
||||
* the variable to store with coordinates of randomly selected selected point on a random face
|
||||
* @param normal
|
||||
* filled with selected face's normal
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
|
||||
int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
|
||||
// the index of the first vertex of a face (must be dividable by 3)
|
||||
int faceIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1);
|
||||
int vertIndex = faceIndex * 3;
|
||||
// put the point somewhere between the first and the second vertex of a face
|
||||
float moveFactor = FastMath.nextRandomFloat();
|
||||
store.set(Vector3f.ZERO);
|
||||
store.addLocal(vertices.get(meshIndex).get(vertIndex));
|
||||
store.addLocal((vertices.get(meshIndex).get(vertIndex + 1).x - vertices.get(meshIndex).get(vertIndex).x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).y - vertices.get(meshIndex).get(vertIndex).y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).z - vertices.get(meshIndex).get(vertIndex).z) * moveFactor);
|
||||
// move the result towards the last face vertex
|
||||
moveFactor = FastMath.nextRandomFloat();
|
||||
store.addLocal((vertices.get(meshIndex).get(vertIndex + 2).x - store.x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).y - store.y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).z - store.z) * moveFactor);
|
||||
normal.set(normals.get(meshIndex).get(faceIndex));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package com.jme3.effect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
@ -18,60 +21,129 @@ import com.jme3.util.BufferUtils;
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class EmitterMeshVertexShape implements EmitterShape {
|
||||
protected float[][] vertices;
|
||||
|
||||
protected List<List<Vector3f>> vertices;
|
||||
protected List<List<Vector3f>> normals;
|
||||
|
||||
/**
|
||||
* Empty constructor. Sets nothing.
|
||||
*/
|
||||
public EmitterMeshVertexShape() {}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor. It stores a copy of vertex list of all meshes.
|
||||
* @param meshes a list of meshes that will form the emitter's shape
|
||||
* @param meshes
|
||||
* a list of meshes that will form the emitter's shape
|
||||
*/
|
||||
public EmitterMeshVertexShape(List<Mesh> meshes) {
|
||||
this.setMeshes(meshes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method sets the meshes that will form the emiter's shape.
|
||||
* @param meshes a list of meshes that will form the emitter's shape
|
||||
* @param meshes
|
||||
* a list of meshes that will form the emitter's shape
|
||||
*/
|
||||
public void setMeshes(List<Mesh> meshes) {
|
||||
this.vertices = new float[meshes.size()][];
|
||||
int i=0;
|
||||
for(Mesh mesh : meshes) {
|
||||
FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position);
|
||||
vertices[i++] = BufferUtils.getFloatArray(floatBuffer);
|
||||
Map<Vector3f, Vector3f> vertToNormalMap = new HashMap<Vector3f, Vector3f>();
|
||||
|
||||
this.vertices = new ArrayList<List<Vector3f>>(meshes.size());
|
||||
this.normals = new ArrayList<List<Vector3f>>(meshes.size());
|
||||
for (Mesh mesh : meshes) {
|
||||
// fetching the data
|
||||
float[] vertexTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Position));
|
||||
float[] normalTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Normal));
|
||||
|
||||
// unifying normals
|
||||
for (int i = 0; i < vertexTable.length; i += 3) {// the tables should have the same size and be dividable by 3
|
||||
Vector3f vert = new Vector3f(vertexTable[i], vertexTable[i + 1], vertexTable[i + 2]);
|
||||
Vector3f norm = vertToNormalMap.get(vert);
|
||||
if (norm == null) {
|
||||
norm = new Vector3f(normalTable[i], normalTable[i + 1], normalTable[i + 2]);
|
||||
vertToNormalMap.put(vert, norm);
|
||||
} else {
|
||||
norm.addLocal(normalTable[i], normalTable[i + 1], normalTable[i + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
// adding data to vertices and normals
|
||||
List<Vector3f> vertices = new ArrayList<Vector3f>(vertToNormalMap.size());
|
||||
List<Vector3f> normals = new ArrayList<Vector3f>(vertToNormalMap.size());
|
||||
for (Entry<Vector3f, Vector3f> entry : vertToNormalMap.entrySet()) {
|
||||
vertices.add(entry.getKey());
|
||||
normals.add(entry.getValue().normalizeLocal());
|
||||
}
|
||||
this.vertices.add(vertices);
|
||||
this.normals.add(normals);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method fills the point with coordinates of randomly selected mesh vertex.
|
||||
* @param store
|
||||
* the variable to store with coordinates of randomly selected mesh vertex
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
int meshIndex = FastMath.nextRandomInt(0, vertices.length-1);
|
||||
int vertIndex = FastMath.nextRandomInt(0, vertices[meshIndex].length / 3 - 1) * 3;
|
||||
store.set(vertices[meshIndex][vertIndex], vertices[meshIndex][vertIndex + 1], vertices[meshIndex][vertIndex + 2]);
|
||||
int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
|
||||
int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() - 1);
|
||||
store.set(vertices.get(meshIndex).get(vertIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with coordinates of randomly selected mesh vertex.
|
||||
* The normal param is filled with selected vertex's normal.
|
||||
* @param store
|
||||
* the variable to store with coordinates of randomly selected mesh vertex
|
||||
* @param normal
|
||||
* filled with selected vertex's normal
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
|
||||
int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
|
||||
int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() - 1);
|
||||
store.set(vertices.get(meshIndex).get(vertIndex));
|
||||
normal.set(normals.get(meshIndex).get(vertIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmitterShape deepClone() {
|
||||
try {
|
||||
EmitterMeshVertexShape clone = (EmitterMeshVertexShape) super.clone();
|
||||
clone.vertices = vertices==null ? null : vertices.clone();
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(vertices, "vertices", null);
|
||||
if (this.vertices != null) {
|
||||
clone.vertices = new ArrayList<List<Vector3f>>(vertices.size());
|
||||
for (List<Vector3f> list : vertices) {
|
||||
List<Vector3f> vectorList = new ArrayList<Vector3f>(list.size());
|
||||
for (Vector3f vector : list) {
|
||||
vectorList.add(vector.clone());
|
||||
}
|
||||
clone.vertices.add(vectorList);
|
||||
}
|
||||
}
|
||||
if (this.normals != null) {
|
||||
clone.normals = new ArrayList<List<Vector3f>>(normals.size());
|
||||
for (List<Vector3f> list : normals) {
|
||||
List<Vector3f> vectorList = new ArrayList<Vector3f>(list.size());
|
||||
for (Vector3f vector : list) {
|
||||
vectorList.add(vector.clone());
|
||||
}
|
||||
clone.normals.add(vectorList);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.writeSavableArrayList((ArrayList<List<Vector3f>>) vertices, "vertices", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
this.vertices = im.getCapsule(this).readFloatArray2D("vertices", null);
|
||||
this.vertices = im.getCapsule(this).readSavableArrayList("vertices", null);
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ public class EmitterPointShape implements EmitterShape {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public EmitterShape deepClone(){
|
||||
@Override
|
||||
public EmitterShape deepClone(){
|
||||
try {
|
||||
EmitterPointShape clone = (EmitterPointShape) super.clone();
|
||||
clone.point = point.clone();
|
||||
@ -59,9 +60,21 @@ public class EmitterPointShape implements EmitterShape {
|
||||
}
|
||||
}
|
||||
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
@Override
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
store.set(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the point with data.
|
||||
* It does not fill the normal.
|
||||
* @param store the variable to store the point data
|
||||
* @param normal not used in this class
|
||||
*/
|
||||
@Override
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
|
||||
store.set(point);
|
||||
}
|
||||
|
||||
public Vector3f getPoint() {
|
||||
return point;
|
||||
@ -71,12 +84,14 @@ public class EmitterPointShape implements EmitterShape {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(point, "point", null);
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
this.point = (Vector3f) im.getCapsule(this).readSavable("point", null);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,30 @@ package com.jme3.effect;
|
||||
import com.jme3.export.Savable;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
* This interface declares methods used by all shapes that represent particle emitters.
|
||||
* @author Kirill
|
||||
*/
|
||||
public interface EmitterShape extends Savable, Cloneable {
|
||||
public void getRandomPoint(Vector3f store);
|
||||
public EmitterShape deepClone();
|
||||
/**
|
||||
* This method fills in the initial position of the particle.
|
||||
* @param store
|
||||
* store variable for initial position
|
||||
*/
|
||||
public void getRandomPoint(Vector3f store);
|
||||
|
||||
/**
|
||||
* This method fills in the initial position of the particle and its normal vector.
|
||||
* @param store
|
||||
* store variable for initial position
|
||||
* @param normal
|
||||
* store variable for initial normal
|
||||
*/
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal);
|
||||
|
||||
/**
|
||||
* This method creates a deep clone of the current instance of the emitter shape.
|
||||
* @return deep clone of the current instance of the emitter shape
|
||||
*/
|
||||
public EmitterShape deepClone();
|
||||
}
|
||||
|
@ -49,11 +49,13 @@ public class EmitterSphereShape implements EmitterShape {
|
||||
}
|
||||
|
||||
public EmitterSphereShape(Vector3f center, float radius) {
|
||||
if (center == null)
|
||||
throw new NullPointerException();
|
||||
if (center == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
if (radius <= 0)
|
||||
throw new IllegalArgumentException("Radius must be greater than 0");
|
||||
if (radius <= 0) {
|
||||
throw new IllegalArgumentException("Radius must be greater than 0");
|
||||
}
|
||||
|
||||
this.center = center;
|
||||
this.radius = radius;
|
||||
@ -70,13 +72,19 @@ public class EmitterSphereShape implements EmitterShape {
|
||||
}
|
||||
}
|
||||
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
@Override
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
do {
|
||||
store.x = ((FastMath.nextRandomFloat() * 2f) - 1f) * radius;
|
||||
store.y = ((FastMath.nextRandomFloat() * 2f) - 1f) * radius;
|
||||
store.z = ((FastMath.nextRandomFloat() * 2f) - 1f) * radius;
|
||||
store.x = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
|
||||
store.y = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
|
||||
store.z = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
|
||||
} while (store.distance(center) > radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
|
||||
this.getRandomPoint(store);
|
||||
}
|
||||
|
||||
public Vector3f getCenter() {
|
||||
return center;
|
||||
@ -94,13 +102,15 @@ public class EmitterSphereShape implements EmitterShape {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(center, "center", null);
|
||||
oc.write(radius, "radius", 0);
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
center = (Vector3f) ic.readSavable("center", null);
|
||||
radius = ic.readFloat("radius", 0);
|
||||
|
@ -34,6 +34,8 @@ package com.jme3.effect;
|
||||
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.effect.ParticleMesh.Type;
|
||||
import com.jme3.effect.influencers.DefaultParticleInfluencer;
|
||||
import com.jme3.effect.influencers.ParticleInfluencer;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.InputCapsule;
|
||||
@ -54,11 +56,12 @@ import com.jme3.util.TempVars;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ParticleEmitter extends Geometry implements Control {
|
||||
|
||||
private static final EmitterShape DEFAULT_SHAPE = new EmitterPointShape(Vector3f.ZERO);
|
||||
|
||||
private static final ParticleInfluencer DEFAULT_INFLUENCER = new DefaultParticleInfluencer();
|
||||
|
||||
private EmitterShape shape = DEFAULT_SHAPE;
|
||||
private ParticleMesh particleMesh;
|
||||
private ParticleInfluencer particleInfluencer = DEFAULT_INFLUENCER;
|
||||
private ParticleMesh.Type meshType;
|
||||
private Particle[] particles;
|
||||
|
||||
@ -68,17 +71,15 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
// private int next = 0;
|
||||
// private ArrayList<Integer> unusedIndices = new ArrayList<Integer>();
|
||||
|
||||
private boolean randomAngle = false;
|
||||
private boolean selectRandomImage = false;
|
||||
private boolean facingVelocity = false;
|
||||
private boolean randomAngle;
|
||||
private boolean selectRandomImage;
|
||||
private boolean facingVelocity;
|
||||
private float particlesPerSec = 20;
|
||||
private float emitCarry = 0f;
|
||||
private float emitCarry;
|
||||
private float lowLife = 3f;
|
||||
private float highLife = 7f;
|
||||
private float gravity = 0.1f;
|
||||
private float variation = 0.2f;
|
||||
private float rotateSpeed = 0;
|
||||
private Vector3f startVel = new Vector3f();
|
||||
private Vector3f gravity = new Vector3f(0.0f, 0.1f, 0.0f);
|
||||
private float rotateSpeed;
|
||||
private Vector3f faceNormal = new Vector3f(Vector3f.NAN);
|
||||
|
||||
private int imagesX = 1;
|
||||
@ -91,17 +92,18 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
private float endSize = 2f;
|
||||
private boolean worldSpace = true;
|
||||
|
||||
private Vector3f temp = new Vector3f();
|
||||
|
||||
//variable that helps with computations
|
||||
private transient Vector3f temp = new Vector3f();
|
||||
|
||||
@Override
|
||||
public ParticleEmitter clone(){
|
||||
ParticleEmitter clone = (ParticleEmitter) super.clone();
|
||||
clone.shape = shape.deepClone();
|
||||
clone.setNumParticles(particles.length);
|
||||
clone.startVel = startVel.clone();
|
||||
clone.faceNormal = faceNormal.clone();
|
||||
clone.startColor = startColor.clone();
|
||||
clone.endColor = endColor.clone();
|
||||
clone.particleInfluencer = particleInfluencer.clone();
|
||||
clone.controls.add(clone);
|
||||
return clone;
|
||||
}
|
||||
@ -110,28 +112,28 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
super(name);
|
||||
|
||||
// ignore world transform, unless user sets inLocalSpace
|
||||
setIgnoreTransform(true);
|
||||
this.setIgnoreTransform(true);
|
||||
|
||||
// particles neither receive nor cast shadows
|
||||
setShadowMode(ShadowMode.Off);
|
||||
this.setShadowMode(ShadowMode.Off);
|
||||
|
||||
// particles are usually transparent
|
||||
setQueueBucket(Bucket.Transparent);
|
||||
this.setQueueBucket(Bucket.Transparent);
|
||||
|
||||
meshType = type;
|
||||
|
||||
setNumParticles(numParticles);
|
||||
this.setNumParticles(numParticles);
|
||||
|
||||
controls.add(this);
|
||||
|
||||
switch (meshType){
|
||||
case Point:
|
||||
particleMesh = new ParticlePointMesh();
|
||||
setMesh(particleMesh);
|
||||
this.setMesh(particleMesh);
|
||||
break;
|
||||
case Triangle:
|
||||
particleMesh = new ParticleTriMesh();
|
||||
setMesh(particleMesh);
|
||||
this.setMesh(particleMesh);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unrecognized particle type: "+meshType);
|
||||
@ -143,7 +145,8 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
super();
|
||||
}
|
||||
|
||||
public Control cloneForSpatial(Spatial spatial){
|
||||
@Override
|
||||
public Control cloneForSpatial(Spatial spatial){
|
||||
return (Control) spatial;
|
||||
}
|
||||
|
||||
@ -154,13 +157,25 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
public EmitterShape getShape(){
|
||||
return shape;
|
||||
}
|
||||
|
||||
public void setParticleInfluencer(ParticleInfluencer particleInfluencer) {
|
||||
this.particleInfluencer = particleInfluencer;
|
||||
}
|
||||
|
||||
public ParticleInfluencer getParticleInfluencer() {
|
||||
return particleInfluencer;
|
||||
}
|
||||
|
||||
public ParticleMesh.Type getMeshType() {
|
||||
return meshType;
|
||||
}
|
||||
|
||||
public boolean isInWorldSpace() {
|
||||
return worldSpace;
|
||||
}
|
||||
|
||||
public void setInWorldSpace(boolean worldSpace) {
|
||||
setIgnoreTransform(worldSpace);
|
||||
this.setIgnoreTransform(worldSpace);
|
||||
this.worldSpace = worldSpace;
|
||||
}
|
||||
|
||||
@ -195,10 +210,11 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
}
|
||||
|
||||
public Vector3f getFaceNormal() {
|
||||
if (Vector3f.isValidVector(faceNormal))
|
||||
return faceNormal;
|
||||
else
|
||||
return null;
|
||||
if (Vector3f.isValidVector(faceNormal)) {
|
||||
return faceNormal;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,10 +228,11 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
* if particles should face the camera.
|
||||
*/
|
||||
public void setFaceNormal(Vector3f faceNormal) {
|
||||
if (faceNormal == null || !Vector3f.isValidVector(faceNormal))
|
||||
this.faceNormal.set(Vector3f.NAN);
|
||||
else
|
||||
this.faceNormal = faceNormal;
|
||||
if (faceNormal == null || !Vector3f.isValidVector(faceNormal)) {
|
||||
this.faceNormal.set(Vector3f.NAN);
|
||||
} else {
|
||||
this.faceNormal = faceNormal;
|
||||
}
|
||||
}
|
||||
|
||||
public float getRotateSpeed() {
|
||||
@ -302,17 +319,54 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
this.endSize = endSize;
|
||||
}
|
||||
|
||||
public float getGravity() {
|
||||
return gravity;
|
||||
}
|
||||
/**
|
||||
* This method sets the gravity value of Y axis.
|
||||
* By default the Y axis is the only one to have gravity value non zero.
|
||||
* @param gravity
|
||||
* Set the gravity of Y axis, in units/sec/sec, of particles
|
||||
* spawned.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setGravity(float gravity) {
|
||||
this.gravity.y = gravity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gravity Set the gravity, in units/sec/sec, of particles
|
||||
* spawned.
|
||||
*/
|
||||
public void setGravity(float gravity) {
|
||||
this.gravity = gravity;
|
||||
}
|
||||
/**
|
||||
* This method returns the gravity vector.
|
||||
* @return the gravity vector
|
||||
*/
|
||||
public Vector3f getGravity() {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the gravity vector.
|
||||
* @param gravity
|
||||
* the gravity vector
|
||||
*/
|
||||
public void setGravity(Vector3f gravity) {
|
||||
this.gravity.set(gravity);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the gravity vector.
|
||||
* @param gravity
|
||||
* the gravity vector
|
||||
*/
|
||||
public void setGravity(float[] gravity) {
|
||||
this.setGravity(gravity[0], gravity[1], gravity[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the gravity vector.
|
||||
* @param gravity
|
||||
* the gravity vector
|
||||
*/
|
||||
public void setGravity(float x, float y, float z) {
|
||||
this.gravity.x = x;
|
||||
this.gravity.y = y;
|
||||
this.gravity.z = z;
|
||||
}
|
||||
|
||||
public float getHighLife() {
|
||||
return highLife;
|
||||
@ -406,8 +460,14 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
this.startSize = startSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is deprecated.
|
||||
* Use ParticleEmitter.getParticleInfluencer().getInitialVelocity() instead.
|
||||
* @return the initial velocity for particles
|
||||
*/
|
||||
@Deprecated
|
||||
public Vector3f getInitialVelocity(){
|
||||
return startVel;
|
||||
return particleInfluencer.getInitialVelocity();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -417,15 +477,27 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
* A particle will move toward its velocity unless it is effected by the
|
||||
* gravity.
|
||||
*
|
||||
* @deprecated
|
||||
* This method is deprecated.
|
||||
* Use ParticleEmitter.getParticleInfluencer().setInitialVelocity(initialVelocity); instead.
|
||||
*
|
||||
* @see ParticleEmitter#setVelocityVariation(float)
|
||||
* @see ParticleEmitter#setGravity(float)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setInitialVelocity(Vector3f initialVelocity){
|
||||
this.startVel.set(initialVelocity);
|
||||
this.particleInfluencer.setInitialVelocity(initialVelocity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This method is deprecated.
|
||||
* Use ParticleEmitter.getParticleInfluencer().getVelocityVariation(); instead.
|
||||
* @return the initial velocity variation factor
|
||||
*/
|
||||
@Deprecated
|
||||
public float getVelocityVariation() {
|
||||
return variation;
|
||||
return particleInfluencer.getVelocityVariation();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,9 +506,14 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
* from 0 to 1, where 0 means particles are to spawn with exactly
|
||||
* the velocity given in {@link ParticleEmitter#setStartVel(com.jme3.math.Vector3f) },
|
||||
* and 1 means particles are to spawn with a completely random velocity.
|
||||
*
|
||||
* @deprecated
|
||||
* This method is deprecated.
|
||||
* Use ParticleEmitter.getParticleInfluencer().setVelocityVariation(variation); instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setVelocityVariation(float variation) {
|
||||
this.variation = variation;
|
||||
this.particleInfluencer.setVelocityVariation(variation);
|
||||
}
|
||||
|
||||
// private int newIndex(){
|
||||
@ -472,38 +549,33 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
}
|
||||
|
||||
Particle p = particles[idx];
|
||||
if (selectRandomImage)
|
||||
p.imageIndex = (FastMath.nextRandomInt(0, imagesY-1) * imagesX) + FastMath.nextRandomInt(0, imagesX-1);
|
||||
if (selectRandomImage) {
|
||||
p.imageIndex = FastMath.nextRandomInt(0, imagesY-1) * imagesX + FastMath.nextRandomInt(0, imagesX-1);
|
||||
}
|
||||
|
||||
p.startlife = lowLife + FastMath.nextRandomFloat() * (highLife - lowLife);
|
||||
p.life = p.startlife;
|
||||
p.color.set(startColor);
|
||||
p.size = startSize;
|
||||
shape.getRandomPoint(p.position);
|
||||
//shape.getRandomPoint(p.position);
|
||||
particleInfluencer.influenceParticle(p, shape);
|
||||
if (worldSpace){
|
||||
p.position.addLocal(worldTransform.getTranslation());
|
||||
}
|
||||
p.velocity.set(startVel);
|
||||
if (randomAngle)
|
||||
p.angle = FastMath.nextRandomFloat() * FastMath.TWO_PI;
|
||||
if (rotateSpeed != 0)
|
||||
p.rotateSpeed = rotateSpeed * (0.2f + (FastMath.nextRandomFloat() * 2f - 1f) * .8f);
|
||||
|
||||
// NOTE: Using temp variable here
|
||||
temp.set(FastMath.nextRandomFloat(),FastMath.nextRandomFloat(),FastMath.nextRandomFloat());
|
||||
temp.multLocal(2f);
|
||||
temp.subtractLocal(1f,1f,1f);
|
||||
temp.multLocal(startVel.length());
|
||||
p.velocity.interpolate(temp, variation);
|
||||
|
||||
temp.set(p.position).addLocal(p.size, p.size, p.size);
|
||||
if (randomAngle) {
|
||||
p.angle = FastMath.nextRandomFloat() * FastMath.TWO_PI;
|
||||
}
|
||||
if (rotateSpeed != 0) {
|
||||
p.rotateSpeed = rotateSpeed * (0.2f + (FastMath.nextRandomFloat() * 2f - 1f) * .8f);
|
||||
}
|
||||
|
||||
temp.set(p.position).addLocal(p.size, p.size, p.size);
|
||||
max.maxLocal(temp);
|
||||
temp.set(p.position).subtractLocal(p.size, p.size, p.size);
|
||||
min.minLocal(temp);
|
||||
|
||||
lastUsed++;
|
||||
++lastUsed;
|
||||
firstUnUsed = idx + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -511,15 +583,14 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
* Instantly emits all the particles possible to be emitted. Any particles
|
||||
* which are currently inactive will be spawned immediately.
|
||||
*/
|
||||
@SuppressWarnings("empty-statement")
|
||||
public void emitAllParticles(){
|
||||
// Force world transform to update
|
||||
getWorldTransform();
|
||||
this.getWorldTransform();
|
||||
|
||||
TempVars vars = TempVars.get();
|
||||
assert vars.lock();
|
||||
|
||||
BoundingBox bbox = (BoundingBox) getMesh().getBound();
|
||||
BoundingBox bbox = (BoundingBox) this.getMesh().getBound();
|
||||
|
||||
Vector3f min = vars.vect1;
|
||||
Vector3f max = vars.vect2;
|
||||
@ -534,10 +605,12 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
max.set(Vector3f.NEGATIVE_INFINITY);
|
||||
}
|
||||
|
||||
while (emitParticle(min, max));
|
||||
while (this.emitParticle(min, max)) {
|
||||
;
|
||||
}
|
||||
|
||||
bbox.setMinMax(min, max);
|
||||
setBoundRefresh();
|
||||
this.setBoundRefresh();
|
||||
|
||||
assert vars.unlock();
|
||||
}
|
||||
@ -547,9 +620,10 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
* particles will be dead and no longer visible.
|
||||
*/
|
||||
public void killAllParticles(){
|
||||
for (int i = 0; i < particles.length; i++){
|
||||
if (particles[i].life > 0)
|
||||
freeParticle(i);
|
||||
for (int i = 0; i < particles.length; ++i){
|
||||
if (particles[i].life > 0) {
|
||||
this.freeParticle(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -582,7 +656,7 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
|
||||
private void updateParticleState(float tpf){
|
||||
// Force world transform to update
|
||||
getWorldTransform();
|
||||
this.getWorldTransform();
|
||||
|
||||
TempVars vars = TempVars.get();
|
||||
assert vars.lock();
|
||||
@ -590,7 +664,7 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY);
|
||||
Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY);
|
||||
|
||||
for (int i = 0; i < particles.length; i++){
|
||||
for (int i = 0; i < particles.length; ++i){
|
||||
Particle p = particles[i];
|
||||
if (p.life == 0){ // particle is dead
|
||||
// assert i <= firstUnUsed;
|
||||
@ -599,19 +673,21 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
|
||||
p.life -= tpf;
|
||||
if (p.life <= 0){
|
||||
freeParticle(i);
|
||||
this.freeParticle(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// position += velocity * tpf
|
||||
p.distToCam = -1;
|
||||
float g = gravity * tpf;
|
||||
p.velocity.y -= g;
|
||||
|
||||
// NOTE: Using temp variable
|
||||
|
||||
// applying gravity
|
||||
p.velocity.x -= gravity.x * tpf;
|
||||
p.velocity.y -= gravity.y * tpf;
|
||||
p.velocity.z -= gravity.z * tpf;
|
||||
temp.set(p.velocity).multLocal(tpf);
|
||||
p.position.addLocal(temp);
|
||||
|
||||
// affecting color, size and angle
|
||||
float b = (p.startlife - p.life) / p.startlife;
|
||||
p.color.interpolate(startColor, endColor, b);
|
||||
p.size = FastMath.interpolateLinear(b, startSize, endSize);
|
||||
@ -619,15 +695,16 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
|
||||
// Computing bounding volume
|
||||
temp.set(p.position).addLocal(p.size, p.size, p.size);
|
||||
max.maxLocal(temp);
|
||||
temp.set(p.position).subtractLocal(p.size, p.size, p.size);
|
||||
min.minLocal(temp);
|
||||
max.maxLocal(temp);
|
||||
temp.set(p.position).subtractLocal(p.size, p.size, p.size);
|
||||
min.minLocal(temp);
|
||||
|
||||
if (!selectRandomImage) // use animated effect
|
||||
p.imageIndex = (int) (b * imagesX * imagesY);
|
||||
if (!selectRandomImage) {
|
||||
p.imageIndex = (int) (b * imagesX * imagesY);
|
||||
}
|
||||
|
||||
if (firstUnUsed < i) {
|
||||
swap(firstUnUsed, i);
|
||||
if (firstUnUsed < i) {
|
||||
this.swap(firstUnUsed, i);
|
||||
if (i == lastUsed) {
|
||||
lastUsed = firstUnUsed;
|
||||
}
|
||||
@ -636,21 +713,21 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
}
|
||||
|
||||
float particlesToEmitF = particlesPerSec * tpf;
|
||||
int particlesToEmit = (int) (particlesToEmitF);
|
||||
int particlesToEmit = (int) particlesToEmitF;
|
||||
emitCarry += particlesToEmitF - particlesToEmit;
|
||||
|
||||
while (emitCarry > 1f){
|
||||
particlesToEmit ++;
|
||||
++particlesToEmit;
|
||||
emitCarry -= 1f;
|
||||
}
|
||||
|
||||
for (int i = 0; i < particlesToEmit; i++){
|
||||
emitParticle(min, max);
|
||||
for (int i = 0; i < particlesToEmit; ++i){
|
||||
this.emitParticle(min, max);
|
||||
}
|
||||
|
||||
BoundingBox bbox = (BoundingBox) getMesh().getBound();
|
||||
BoundingBox bbox = (BoundingBox) this.getMesh().getBound();
|
||||
bbox.setMinMax(min, max);
|
||||
setBoundRefresh();
|
||||
this.setBoundRefresh();
|
||||
|
||||
assert vars.unlock();
|
||||
}
|
||||
@ -658,29 +735,33 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
/**
|
||||
* Do not use.
|
||||
*/
|
||||
public void setSpatial(Spatial spatial) {
|
||||
@Override
|
||||
public void setSpatial(Spatial spatial) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enabled Set to enable or disable a particle. When a particle is
|
||||
* disabled, it will be "frozen in time" and not update.
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void update(float tpf) {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
updateParticleState(tpf);
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
if (enabled) {
|
||||
this.updateParticleState(tpf);
|
||||
}
|
||||
}
|
||||
|
||||
public void render(RenderManager rm, ViewPort vp) {
|
||||
@Override
|
||||
public void render(RenderManager rm, ViewPort vp) {
|
||||
Camera cam = vp.getCamera();
|
||||
|
||||
if (meshType == ParticleMesh.Type.Point){
|
||||
@ -688,14 +769,14 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
C *= cam.getWidth() * 0.5f;
|
||||
|
||||
// send attenuation params
|
||||
getMaterial().setFloat("Quadratic", C);
|
||||
this.getMaterial().setFloat("Quadratic", C);
|
||||
}
|
||||
|
||||
Matrix3f inverseRotation = Matrix3f.IDENTITY;
|
||||
if (!worldSpace){
|
||||
TempVars vars = TempVars.get();
|
||||
assert vars.lock();
|
||||
inverseRotation = getWorldRotation().toRotationMatrix(vars.tempMat3).invertLocal();
|
||||
inverseRotation = this.getWorldRotation().toRotationMatrix(vars.tempMat3).invertLocal();
|
||||
}
|
||||
particleMesh.updateParticleData(particles, cam, inverseRotation);
|
||||
if (!worldSpace){
|
||||
@ -704,7 +785,7 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
}
|
||||
|
||||
public void preload(RenderManager rm, ViewPort vp){
|
||||
updateParticleState(0);
|
||||
this.updateParticleState(0);
|
||||
particleMesh.updateParticleData(particles, vp.getCamera(), Matrix3f.IDENTITY);
|
||||
}
|
||||
|
||||
@ -719,12 +800,10 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
oc.write(particlesPerSec, "particlesPerSec", 0);
|
||||
oc.write(lowLife, "lowLife", 0);
|
||||
oc.write(highLife, "highLife", 0);
|
||||
oc.write(gravity, "gravity", 0);
|
||||
oc.write(variation, "variation", 0);
|
||||
oc.write(gravity, "gravity", null);
|
||||
oc.write(imagesX, "imagesX", 1);
|
||||
oc.write(imagesY, "imagesY", 1);
|
||||
|
||||
oc.write(startVel, "startVel", null);
|
||||
oc.write(startColor, "startColor", null);
|
||||
oc.write(endColor, "endColor", null);
|
||||
oc.write(startSize, "startSize", 0);
|
||||
@ -734,6 +813,8 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
oc.write(selectRandomImage, "selectRandomImage", false);
|
||||
oc.write(randomAngle, "randomAngle", false);
|
||||
oc.write(rotateSpeed, "rotateSpeed", 0);
|
||||
|
||||
oc.write(particleInfluencer, "influencer", DEFAULT_INFLUENCER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -743,18 +824,16 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
shape = (EmitterShape) ic.readSavable("shape", DEFAULT_SHAPE);
|
||||
meshType = ic.readEnum("meshType", ParticleMesh.Type.class, ParticleMesh.Type.Triangle);
|
||||
int numParticles = ic.readInt("numParticles", 0);
|
||||
setNumParticles(numParticles);
|
||||
this.setNumParticles(numParticles);
|
||||
|
||||
enabled = ic.readBoolean("enabled", true);
|
||||
particlesPerSec = ic.readFloat("particlesPerSec", 0);
|
||||
lowLife = ic.readFloat("lowLife", 0);
|
||||
highLife = ic.readFloat("highLife", 0);
|
||||
gravity = ic.readFloat("gravity", 0);
|
||||
variation = ic.readFloat("variation", 0);
|
||||
gravity = (Vector3f) ic.readSavable("gravity", null);
|
||||
imagesX = ic.readInt("imagesX", 1);
|
||||
imagesY = ic.readInt("imagesY", 1);
|
||||
|
||||
startVel = (Vector3f) ic.readSavable("startVel", null);
|
||||
startColor = (ColorRGBA) ic.readSavable("startColor", null);
|
||||
endColor = (ColorRGBA) ic.readSavable("endColor", null);
|
||||
startSize = ic.readFloat("startSize", 0);
|
||||
@ -768,16 +847,18 @@ public class ParticleEmitter extends Geometry implements Control {
|
||||
switch (meshType){
|
||||
case Point:
|
||||
particleMesh = new ParticlePointMesh();
|
||||
setMesh(particleMesh);
|
||||
this.setMesh(particleMesh);
|
||||
break;
|
||||
case Triangle:
|
||||
particleMesh = new ParticleTriMesh();
|
||||
setMesh(particleMesh);
|
||||
this.setMesh(particleMesh);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unrecognized particle type: "+meshType);
|
||||
}
|
||||
particleMesh.initParticleData(this, particles.length);
|
||||
|
||||
particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package com.jme3.effect.influencers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.jme3.effect.EmitterShape;
|
||||
import com.jme3.effect.Particle;
|
||||
import com.jme3.export.InputCapsule;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
* This emitter influences the particles so that they move all in the same direction.
|
||||
* The direction may vary a little if the velocity variation is non zero.
|
||||
* This influencer is default for the particle emitter.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class DefaultParticleInfluencer implements ParticleInfluencer {
|
||||
/** Temporary variable used to help with calculations. */
|
||||
protected transient Vector3f temp = new Vector3f();
|
||||
/** The initial velocity of the particles. */
|
||||
protected Vector3f startVelocity = new Vector3f();
|
||||
/** The velocity's variation of the particles. */
|
||||
protected float velocityVariation = 0.2f;
|
||||
|
||||
@Override
|
||||
public void influenceParticle(Particle particle, EmitterShape emitterShape) {
|
||||
emitterShape.getRandomPoint(particle.position);
|
||||
this.applyVelocityVariation(particle);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies the variation to the particle with already set velocity.
|
||||
* @param particle
|
||||
* the particle to be affected
|
||||
*/
|
||||
protected void applyVelocityVariation(Particle particle) {
|
||||
temp.set(FastMath.nextRandomFloat(), FastMath.nextRandomFloat(), FastMath.nextRandomFloat());
|
||||
temp.multLocal(2f);
|
||||
temp.subtractLocal(1f, 1f, 1f);
|
||||
temp.multLocal(startVelocity.length());
|
||||
particle.velocity.interpolate(temp, velocityVariation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(startVelocity, "startVelocity", Vector3f.ZERO);
|
||||
oc.write(velocityVariation, "variation", 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
startVelocity = (Vector3f) ic.readSavable("startVelocity", Vector3f.ZERO);
|
||||
velocityVariation = ic.readFloat("variation", 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleInfluencer clone() {
|
||||
try {
|
||||
return (ParticleInfluencer) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitialVelocity(Vector3f initialVelocity) {
|
||||
this.startVelocity.set(initialVelocity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f getInitialVelocity() {
|
||||
return startVelocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVelocityVariation(float variation) {
|
||||
this.velocityVariation = variation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVelocityVariation() {
|
||||
return velocityVariation;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.jme3.effect.influencers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.jme3.effect.EmitterShape;
|
||||
import com.jme3.effect.Particle;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
* This influencer does not influence particle at all.
|
||||
* It makes particles not to move.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class EmptyParticleInfluencer implements ParticleInfluencer {
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {}
|
||||
|
||||
@Override
|
||||
public void influenceParticle(Particle particle, EmitterShape emitterShape) {}
|
||||
|
||||
@Override
|
||||
public void setInitialVelocity(Vector3f initialVelocity) {}
|
||||
|
||||
@Override
|
||||
public Vector3f getInitialVelocity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVelocityVariation(float variation) {}
|
||||
|
||||
@Override
|
||||
public float getVelocityVariation() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleInfluencer clone() {
|
||||
try {
|
||||
return (ParticleInfluencer) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return new EmptyParticleInfluencer();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package com.jme3.effect.influencers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.jme3.effect.EmitterShape;
|
||||
import com.jme3.effect.Particle;
|
||||
import com.jme3.export.InputCapsule;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Matrix3f;
|
||||
|
||||
/**
|
||||
* This influencer calculates initial velocity with the use of the emitter's shape.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class NewtonianParticleInfluencer extends DefaultParticleInfluencer {
|
||||
/** Normal to emitter's shape factor. */
|
||||
protected float normalVelocity;
|
||||
/** Emitter's surface tangent factor. */
|
||||
protected float surfaceTangentFactor;
|
||||
/** Emitters tangent rotation factor. */
|
||||
protected float surfaceTangentRotation;
|
||||
|
||||
/**
|
||||
* Constructor. Sets velocity variation to 0.0f.
|
||||
*/
|
||||
public NewtonianParticleInfluencer() {
|
||||
this.velocityVariation = 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void influenceParticle(Particle particle, EmitterShape emitterShape) {
|
||||
emitterShape.getRandomPointAndNormal(particle.position, particle.velocity);
|
||||
// influencing the particle's velocity
|
||||
if (surfaceTangentFactor == 0.0f) {
|
||||
particle.velocity.multLocal(normalVelocity);
|
||||
} else {
|
||||
// calculating surface tangent (velocity contains the 'normal' value)
|
||||
temp.set(particle.velocity.z * surfaceTangentFactor, particle.velocity.y * surfaceTangentFactor, -particle.velocity.x * surfaceTangentFactor);
|
||||
if (surfaceTangentRotation != 0.0f) {// rotating the tangent
|
||||
Matrix3f m = new Matrix3f();
|
||||
m.fromAngleNormalAxis(FastMath.PI * surfaceTangentRotation, particle.velocity);
|
||||
temp = m.multLocal(temp);
|
||||
}
|
||||
// applying normal factor (this must be done first)
|
||||
particle.velocity.multLocal(normalVelocity);
|
||||
// adding tangent vector
|
||||
particle.velocity.addLocal(temp);
|
||||
}
|
||||
if (velocityVariation != 0.0f) {
|
||||
this.applyVelocityVariation(particle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the normal velocity factor.
|
||||
* @return the normal velocity factor
|
||||
*/
|
||||
public float getNormalVelocity() {
|
||||
return normalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the normal velocity factor.
|
||||
* @param normalVelocity
|
||||
* the normal velocity factor
|
||||
*/
|
||||
public void setNormalVelocity(float normalVelocity) {
|
||||
this.normalVelocity = normalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the surface tangent factor.
|
||||
* @param surfaceTangentFactor
|
||||
* the surface tangent factor
|
||||
*/
|
||||
public void setSurfaceTangentFactor(float surfaceTangentFactor) {
|
||||
this.surfaceTangentFactor = surfaceTangentFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the surface tangent factor.
|
||||
* @return the surface tangent factor
|
||||
*/
|
||||
public float getSurfaceTangentFactor() {
|
||||
return surfaceTangentFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the surface tangent rotation factor.
|
||||
* @param surfaceTangentRotation
|
||||
* the surface tangent rotation factor
|
||||
*/
|
||||
public void setSurfaceTangentRotation(float surfaceTangentRotation) {
|
||||
this.surfaceTangentRotation = surfaceTangentRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the surface tangent rotation factor.
|
||||
* @return the surface tangent rotation factor
|
||||
*/
|
||||
public float getSurfaceTangentRotation() {
|
||||
return surfaceTangentRotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyVelocityVariation(Particle particle) {
|
||||
temp.set(FastMath.nextRandomFloat() * velocityVariation, FastMath.nextRandomFloat() * velocityVariation, FastMath.nextRandomFloat() * velocityVariation);
|
||||
particle.velocity.addLocal(temp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleInfluencer clone() {
|
||||
NewtonianParticleInfluencer result = new NewtonianParticleInfluencer();
|
||||
result.normalVelocity = normalVelocity;
|
||||
result.startVelocity = startVelocity;
|
||||
result.velocityVariation = velocityVariation;
|
||||
result.surfaceTangentFactor = surfaceTangentFactor;
|
||||
result.surfaceTangentRotation = surfaceTangentRotation;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(normalVelocity, "normalVelocity", 0.0f);
|
||||
oc.write(surfaceTangentFactor, "surfaceTangentFactor", 0.0f);
|
||||
oc.write(surfaceTangentRotation, "surfaceTangentRotation", 0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
super.read(im);
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
normalVelocity = ic.readFloat("normalVelocity", 0.0f);
|
||||
surfaceTangentFactor = ic.readFloat("surfaceTangentFactor", 0.0f);
|
||||
surfaceTangentRotation = ic.readFloat("surfaceTangentRotation", 0.0f);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.jme3.effect.influencers;
|
||||
|
||||
import com.jme3.effect.EmitterShape;
|
||||
import com.jme3.effect.Particle;
|
||||
import com.jme3.effect.ParticleEmitter;
|
||||
import com.jme3.export.Savable;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
* An interface that defines the methods to affect initial velocity of the particles.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public interface ParticleInfluencer extends Savable, Cloneable {
|
||||
/**
|
||||
* This method influences the particle.
|
||||
* @param particle
|
||||
* particle to be influenced
|
||||
* @param emitterShape
|
||||
* the shape of it emitter
|
||||
*/
|
||||
void influenceParticle(Particle particle, EmitterShape emitterShape);
|
||||
|
||||
/**
|
||||
* This method clones the influencer instance.
|
||||
* @return cloned instance
|
||||
*/
|
||||
public ParticleInfluencer clone();
|
||||
|
||||
/**
|
||||
* @param initialVelocity
|
||||
* Set the initial velocity a particle is spawned with,
|
||||
* the initial velocity given in the parameter will be varied according
|
||||
* to the velocity variation set in {@link ParticleEmitter#setVelocityVariation(float) }.
|
||||
* A particle will move toward its velocity unless it is effected by the
|
||||
* gravity.
|
||||
*/
|
||||
void setInitialVelocity(Vector3f initialVelocity);
|
||||
|
||||
/**
|
||||
* This method returns the initial velocity.
|
||||
* @return the initial velocity
|
||||
*/
|
||||
Vector3f getInitialVelocity();
|
||||
|
||||
/**
|
||||
* @param variation
|
||||
* Set the variation by which the initial velocity
|
||||
* of the particle is determined. <code>variation</code> should be a value
|
||||
* from 0 to 1, where 0 means particles are to spawn with exactly
|
||||
* the velocity given in {@link ParticleEmitter#setStartVel(com.jme3.math.Vector3f) },
|
||||
* and 1 means particles are to spawn with a completely random velocity.
|
||||
*/
|
||||
void setVelocityVariation(float variation);
|
||||
|
||||
/**
|
||||
* This method returns the velocity variation.
|
||||
* @return the velocity variation
|
||||
*/
|
||||
float getVelocityVariation();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user