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
3.0
Kae..pl 14 years ago
parent 5a79adc4fe
commit 49116be02d
  1. 28
      engine/src/core/com/jme3/effect/EmitterBoxShape.java
  2. 34
      engine/src/core/com/jme3/effect/EmitterMeshConvexHullShape.java
  3. 81
      engine/src/core/com/jme3/effect/EmitterMeshFaceShape.java
  4. 120
      engine/src/core/com/jme3/effect/EmitterMeshVertexShape.java
  5. 23
      engine/src/core/com/jme3/effect/EmitterPointShape.java
  6. 27
      engine/src/core/com/jme3/effect/EmitterShape.java
  7. 30
      engine/src/core/com/jme3/effect/EmitterSphereShape.java
  8. 307
      engine/src/core/com/jme3/effect/ParticleEmitter.java
  9. 89
      engine/src/core/com/jme3/effect/influencers/DefaultParticleInfluencer.java
  10. 51
      engine/src/core/com/jme3/effect/influencers/EmptyParticleInfluencer.java
  11. 142
      engine/src/core/com/jme3/effect/influencers/NewtonianParticleInfluencer.java
  12. 60
      engine/src/core/com/jme3/effect/influencers/ParticleInfluencer.java

@ -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(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);
}
/**
* 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(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);
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();
}
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.write(vertices, "vertices", null);
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;
}
/**
* @param gravity Set the gravity, in units/sec/sec, of particles
* spawned.
*/
public void setGravity(float gravity) {
this.gravity = 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;
}
/**
* 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…
Cancel
Save