Particle Emitter :

- fixed setNumParticle method to correctly update the mesh buffers
- added a getMaxNumParticle that returns the max number of particles of the emitter
- changed testMovingParticles and testPointSprite to change the numParticle when hitting the space bar

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7939 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 14 years ago
parent 6c4a84fa92
commit 8f59984d00
  1. 61
      engine/src/core/com/jme3/effect/ParticleEmitter.java
  2. 33
      engine/src/core/com/jme3/effect/ParticlePointMesh.java
  3. 37
      engine/src/core/com/jme3/effect/ParticleTriMesh.java
  4. 35
      engine/src/test/jme3test/effect/TestMovingParticle.java
  5. 18
      engine/src/test/jme3test/effect/TestPointSprite.java

@ -75,7 +75,6 @@ public class ParticleEmitter extends Geometry {
private static final EmitterShape DEFAULT_SHAPE = new EmitterPointShape(Vector3f.ZERO); private static final EmitterShape DEFAULT_SHAPE = new EmitterPointShape(Vector3f.ZERO);
private static final ParticleInfluencer DEFAULT_INFLUENCER = new DefaultParticleInfluencer(); private static final ParticleInfluencer DEFAULT_INFLUENCER = new DefaultParticleInfluencer();
private ParticleEmitterControl control; private ParticleEmitterControl control;
private EmitterShape shape = DEFAULT_SHAPE; private EmitterShape shape = DEFAULT_SHAPE;
private ParticleMesh particleMesh; private ParticleMesh particleMesh;
@ -110,19 +109,19 @@ public class ParticleEmitter extends Geometry {
public static class ParticleEmitterControl implements Control { public static class ParticleEmitterControl implements Control {
ParticleEmitter parentEmitter; ParticleEmitter parentEmitter;
public ParticleEmitterControl(){ public ParticleEmitterControl() {
} }
public ParticleEmitterControl(ParticleEmitter parentEmitter){ public ParticleEmitterControl(ParticleEmitter parentEmitter) {
this.parentEmitter = parentEmitter; this.parentEmitter = parentEmitter;
} }
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
return this; // WARNING: Sets wrong control on spatial. Will be return this; // WARNING: Sets wrong control on spatial. Will be
// fixed automatically by ParticleEmitter.clone() method. // fixed automatically by ParticleEmitter.clone() method.
} }
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
} }
@ -153,21 +152,21 @@ public class ParticleEmitter extends Geometry {
public ParticleEmitter clone() { public ParticleEmitter clone() {
ParticleEmitter clone = (ParticleEmitter) super.clone(); ParticleEmitter clone = (ParticleEmitter) super.clone();
clone.shape = shape.deepClone(); clone.shape = shape.deepClone();
// Reinitialize particle list // Reinitialize particle list
clone.setNumParticles(particles.length); clone.setNumParticles(particles.length);
clone.faceNormal = faceNormal.clone(); clone.faceNormal = faceNormal.clone();
clone.startColor = startColor.clone(); clone.startColor = startColor.clone();
clone.endColor = endColor.clone(); clone.endColor = endColor.clone();
clone.particleInfluencer = particleInfluencer.clone(); clone.particleInfluencer = particleInfluencer.clone();
// remove wrong control // remove wrong control
clone.controls.remove(control); clone.controls.remove(control);
// put correct control // put correct control
clone.controls.add(new ParticleEmitterControl(clone)); clone.controls.add(new ParticleEmitterControl(clone));
// Reinitialize particle mesh // Reinitialize particle mesh
switch (meshType) { switch (meshType) {
case Point: case Point:
@ -183,7 +182,7 @@ public class ParticleEmitter extends Geometry {
} }
clone.particleMesh.initParticleData(clone, clone.particles.length); clone.particleMesh.initParticleData(clone, clone.particles.length);
clone.particleMesh.setImagesXY(clone.imagesX, clone.imagesY); clone.particleMesh.setImagesXY(clone.imagesX, clone.imagesY);
return clone; return clone;
} }
@ -201,8 +200,8 @@ public class ParticleEmitter extends Geometry {
meshType = type; meshType = type;
this.setNumParticles(numParticles);
// Must create clone of shape/influencer so that a reference to a static is // Must create clone of shape/influencer so that a reference to a static is
// not maintained // not maintained
shape = shape.deepClone(); shape = shape.deepClone();
@ -223,7 +222,8 @@ public class ParticleEmitter extends Geometry {
default: default:
throw new IllegalStateException("Unrecognized particle type: " + meshType); throw new IllegalStateException("Unrecognized particle type: " + meshType);
} }
particleMesh.initParticleData(this, particles.length); this.setNumParticles(numParticles);
// particleMesh.initParticleData(this, particles.length);
} }
/** /**
@ -330,10 +330,17 @@ public class ParticleEmitter extends Geometry {
for (int i = 0; i < numParticles; i++) { for (int i = 0; i < numParticles; i++) {
particles[i] = new Particle(); particles[i] = new Particle();
} }
//We have to reinit the mesh's buffers with the new size
particleMesh.initParticleData(this, particles.length);
particleMesh.setImagesXY(this.imagesX, this.imagesY);
firstUnUsed = 0; firstUnUsed = 0;
lastUsed = -1; lastUsed = -1;
} }
public int getMaxNumParticles() {
return particles.length;
}
/** /**
* Returns a list of all particles (shouldn't be used in most cases). * Returns a list of all particles (shouldn't be used in most cases).
* *
@ -1117,12 +1124,12 @@ public class ParticleEmitter extends Geometry {
super.read(im); super.read(im);
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
shape = (EmitterShape) ic.readSavable("shape", DEFAULT_SHAPE); shape = (EmitterShape) ic.readSavable("shape", DEFAULT_SHAPE);
if (shape == DEFAULT_SHAPE){ if (shape == DEFAULT_SHAPE) {
// Prevent reference to static // Prevent reference to static
shape = shape.deepClone(); shape = shape.deepClone();
} }
meshType = ic.readEnum("meshType", ParticleMesh.Type.class, ParticleMesh.Type.Triangle); meshType = ic.readEnum("meshType", ParticleMesh.Type.class, ParticleMesh.Type.Triangle);
int numParticles = ic.readInt("numParticles", 0); int numParticles = ic.readInt("numParticles", 0);
this.setNumParticles(numParticles); this.setNumParticles(numParticles);
@ -1161,17 +1168,17 @@ public class ParticleEmitter extends Geometry {
particleMesh.setImagesXY(imagesX, imagesY); particleMesh.setImagesXY(imagesX, imagesY);
particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER); particleInfluencer = (ParticleInfluencer) ic.readSavable("influencer", DEFAULT_INFLUENCER);
if (particleInfluencer == DEFAULT_INFLUENCER){ if (particleInfluencer == DEFAULT_INFLUENCER) {
particleInfluencer = particleInfluencer.clone(); particleInfluencer = particleInfluencer.clone();
} }
if (im.getFormatVersion() == 0){ if (im.getFormatVersion() == 0) {
// compatibility before the control inside particle emitter // compatibility before the control inside particle emitter
// was changed: // was changed:
// find it in the controls and take it out, then add the proper one in // find it in the controls and take it out, then add the proper one in
for (int i = 0; i < controls.size(); i++){ for (int i = 0; i < controls.size(); i++) {
Object obj = controls.get(i); Object obj = controls.get(i);
if (obj instanceof ParticleEmitter){ if (obj instanceof ParticleEmitter) {
controls.remove(i); controls.remove(i);
// now add the proper one in // now add the proper one in
controls.add(new ParticleEmitterControl(this)); controls.add(new ParticleEmitterControl(this));
@ -1180,11 +1187,11 @@ public class ParticleEmitter extends Geometry {
} }
// compatability before gravity was not a vector but a float // compatability before gravity was not a vector but a float
if (gravity == null){ if (gravity == null) {
gravity = new Vector3f(); gravity = new Vector3f();
gravity.y = ic.readFloat("gravity", 0); gravity.y = ic.readFloat("gravity", 0);
} }
}else{ } else {
// since the parentEmitter is not loaded, it must be // since the parentEmitter is not loaded, it must be
// loaded separately // loaded separately
control = getControl(ParticleEmitterControl.class); control = getControl(ParticleEmitterControl.class);

@ -64,26 +64,51 @@ public class ParticlePointMesh extends ParticleMesh {
FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles); FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles);
VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position); VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
pvb.setupData(Usage.Stream, 3, Format.Float, pb); pvb.setupData(Usage.Stream, 3, Format.Float, pb);
setBuffer(pvb);
//if the buffer is already set only update the data
VertexBuffer buf = getBuffer(VertexBuffer.Type.Position);
if (buf != null) {
buf.updateData(pb);
} else {
setBuffer(pvb);
}
// set colors // set colors
ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4); ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4);
VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color); VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color);
cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb); cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb);
cvb.setNormalized(true); cvb.setNormalized(true);
setBuffer(cvb);
buf = getBuffer(VertexBuffer.Type.Color);
if (buf != null) {
buf.updateData(cb);
} else {
setBuffer(cvb);
}
// set sizes // set sizes
FloatBuffer sb = BufferUtils.createFloatBuffer(numParticles); FloatBuffer sb = BufferUtils.createFloatBuffer(numParticles);
VertexBuffer svb = new VertexBuffer(VertexBuffer.Type.Size); VertexBuffer svb = new VertexBuffer(VertexBuffer.Type.Size);
svb.setupData(Usage.Stream, 1, Format.Float, sb); svb.setupData(Usage.Stream, 1, Format.Float, sb);
setBuffer(svb);
buf = getBuffer(VertexBuffer.Type.Size);
if (buf != null) {
buf.updateData(sb);
} else {
setBuffer(svb);
}
// set UV-scale // set UV-scale
FloatBuffer tb = BufferUtils.createFloatBuffer(numParticles*4); FloatBuffer tb = BufferUtils.createFloatBuffer(numParticles*4);
VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord); VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord);
tvb.setupData(Usage.Stream, 4, Format.Float, tb); tvb.setupData(Usage.Stream, 4, Format.Float, tb);
setBuffer(tvb);
buf = getBuffer(VertexBuffer.Type.TexCoord);
if (buf != null) {
buf.updateData(tb);
} else {
setBuffer(tvb);
}
} }
@Override @Override

@ -66,14 +66,27 @@ public class ParticleTriMesh extends ParticleMesh {
FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles * 4); FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles * 4);
VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position); VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
pvb.setupData(Usage.Stream, 3, Format.Float, pb); pvb.setupData(Usage.Stream, 3, Format.Float, pb);
setBuffer(pvb);
//if the buffer is already set only update the data
VertexBuffer buf = getBuffer(VertexBuffer.Type.Position);
if (buf != null) {
buf.updateData(pb);
} else {
setBuffer(pvb);
}
// set colors // set colors
ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4 * 4); ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4 * 4);
VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color); VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color);
cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb); cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb);
cvb.setNormalized(true); cvb.setNormalized(true);
setBuffer(cvb);
buf = getBuffer(VertexBuffer.Type.Color);
if (buf != null) {
buf.updateData(cb);
} else {
setBuffer(cvb);
}
// set texcoords // set texcoords
VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord); VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord);
@ -89,7 +102,12 @@ public class ParticleTriMesh extends ParticleMesh {
tb.flip(); tb.flip();
tvb.setupData(Usage.Static, 2, Format.Float, tb); tvb.setupData(Usage.Static, 2, Format.Float, tb);
setBuffer(tvb); buf = getBuffer(VertexBuffer.Type.TexCoord);
if (buf != null) {
buf.updateData(tb);
} else {
setBuffer(tvb);
}
// set indices // set indices
ShortBuffer ib = BufferUtils.createShortBuffer(numParticles * 6); ShortBuffer ib = BufferUtils.createShortBuffer(numParticles * 6);
@ -110,9 +128,16 @@ public class ParticleTriMesh extends ParticleMesh {
VertexBuffer ivb = new VertexBuffer(VertexBuffer.Type.Index); VertexBuffer ivb = new VertexBuffer(VertexBuffer.Type.Index);
ivb.setupData(Usage.Static, 3, Format.UnsignedShort, ib); ivb.setupData(Usage.Static, 3, Format.UnsignedShort, ib);
setBuffer(ivb);
buf = getBuffer(VertexBuffer.Type.Index);
if (buf != null) {
buf.updateData(ib);
} else {
setBuffer(ivb);
}
} }
@Override @Override
public void setImagesXY(int imagesX, int imagesY) { public void setImagesXY(int imagesX, int imagesY) {
this.imagesX = imagesX; this.imagesX = imagesX;

@ -29,13 +29,16 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package jme3test.effect; package jme3test.effect;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.effect.ParticleEmitter; import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type; import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape; import com.jme3.effect.shapes.EmitterSphereShape;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.InputListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
@ -45,19 +48,19 @@ import com.jme3.math.Vector3f;
* *
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public class TestMovingParticle extends SimpleApplication { public class TestMovingParticle extends SimpleApplication {
private ParticleEmitter emit; private ParticleEmitter emit;
private float angle = 0; private float angle = 0;
public static void main(String[] args){ public static void main(String[] args) {
TestMovingParticle app = new TestMovingParticle(); TestMovingParticle app = new TestMovingParticle();
app.start(); app.start();
} }
@Override @Override
public void simpleInitApp() { public void simpleInitApp() {
emit = new ParticleEmitter("Emitter", Type.Triangle, 200); emit = new ParticleEmitter("Emitter", Type.Triangle, 300);
emit.setGravity(0, 0, 0); emit.setGravity(0, 0, 0);
emit.setVelocityVariation(1); emit.setVelocityVariation(1);
emit.setLowLife(1); emit.setLowLife(1);
@ -67,17 +70,27 @@ public class TestMovingParticle extends SimpleApplication {
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png")); mat.setTexture("Texture", assetManager.loadTexture("Effects/Smoke/Smoke.png"));
emit.setMaterial(mat); emit.setMaterial(mat);
rootNode.attachChild(emit); rootNode.attachChild(emit);
inputManager.addListener(new ActionListener() {
public void onAction(String name, boolean isPressed, float tpf) {
if ("setNum".equals(name) && isPressed) {
emit.setNumParticles(1000);
}
}
}, "setNum");
inputManager.addMapping("setNum", new KeyTrigger(KeyInput.KEY_SPACE));
} }
@Override @Override
public void simpleUpdate(float tpf){ public void simpleUpdate(float tpf) {
angle += tpf; angle += tpf;
angle %= FastMath.TWO_PI; angle %= FastMath.TWO_PI;
float x = FastMath.cos(angle) * 2; float x = FastMath.cos(angle) * 2;
float y = FastMath.sin(angle) * 2; float y = FastMath.sin(angle) * 2;
emit.setLocalTranslation(x, 0, y); emit.setLocalTranslation(x, 0, y);
} }
} }

@ -36,6 +36,9 @@ import com.jme3.app.SimpleApplication;
import com.jme3.effect.ParticleEmitter; import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type; import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterBoxShape; import com.jme3.effect.shapes.EmitterBoxShape;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
@ -49,13 +52,13 @@ public class TestPointSprite extends SimpleApplication {
@Override @Override
public void simpleInitApp() { public void simpleInitApp() {
ParticleEmitter emit = new ParticleEmitter("Emitter", Type.Point, 10000); final ParticleEmitter emit = new ParticleEmitter("Emitter", Type.Point, 10000);
emit.setShape(new EmitterBoxShape(new Vector3f(-1.8f, -1.8f, -1.8f), emit.setShape(new EmitterBoxShape(new Vector3f(-1.8f, -1.8f, -1.8f),
new Vector3f(1.8f, 1.8f, 1.8f))); new Vector3f(1.8f, 1.8f, 1.8f)));
emit.setGravity(0, 0, 0); emit.setGravity(0, 0, 0);
emit.setLowLife(60); emit.setLowLife(60);
emit.setHighLife(60); emit.setHighLife(60);
emit.setInitialVelocity(new Vector3f(0, 0, 0)); emit.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
emit.setImagesX(15); emit.setImagesX(15);
emit.setStartSize(0.05f); emit.setStartSize(0.05f);
emit.setEndSize(0.05f); emit.setEndSize(0.05f);
@ -70,6 +73,17 @@ public class TestPointSprite extends SimpleApplication {
emit.setMaterial(mat); emit.setMaterial(mat);
rootNode.attachChild(emit); rootNode.attachChild(emit);
inputManager.addListener(new ActionListener() {
public void onAction(String name, boolean isPressed, float tpf) {
if ("setNum".equals(name) && isPressed) {
emit.setNumParticles(5000);
emit.emitAllParticles();
}
}
}, "setNum");
inputManager.addMapping("setNum", new KeyTrigger(KeyInput.KEY_SPACE));
} }

Loading…
Cancel
Save