Переглянути джерело

ParticleEmitter and related classes (ugh) now implement JmeCloneable.
It hasn't replaced the old clone() method yet and is still untested.

Paul Speed 9 роки тому
батько
коміт
7665fef2de

+ 170 - 134
jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java

@@ -65,12 +65,12 @@ import java.io.IOException;
  * Particle emitters can be used to simulate various kinds of phenomena,
  * such as fire, smoke, explosions and much more.
  * <p>
- * Particle emitters have many properties which are used to control the 
- * simulation. The interpretation of these properties depends on the 
+ * Particle emitters have many properties which are used to control the
+ * simulation. The interpretation of these properties depends on the
  * {@link ParticleInfluencer} that has been assigned to the emitter via
  * {@link ParticleEmitter#setParticleInfluencer(com.jme3.effect.influencers.ParticleInfluencer) }.
  * By default the implementation {@link DefaultParticleInfluencer} is used.
- * 
+ *
  * @author Kirill Vainer
  */
 public class ParticleEmitter extends Geometry {
@@ -100,7 +100,7 @@ public class ParticleEmitter extends Geometry {
     private Vector3f faceNormal = new Vector3f(Vector3f.NAN);
     private int imagesX = 1;
     private int imagesY = 1;
-   
+
     private ColorRGBA startColor = new ColorRGBA(0.4f, 0.4f, 0.4f, 0.5f);
     private ColorRGBA endColor = new ColorRGBA(0.1f, 0.1f, 0.1f, 0.0f);
     private float startSize = 0.2f;
@@ -127,20 +127,20 @@ public class ParticleEmitter extends Geometry {
             // fixed automatically by ParticleEmitter.clone() method.
         }
 
-        @Override   
+        @Override
         public Object jmeClone() {
             try {
                 return super.clone();
             } catch( CloneNotSupportedException e ) {
                 throw new RuntimeException("Error cloning", e);
             }
-        }     
+        }
 
-        @Override   
-        public void cloneFields( Cloner cloner, Object original ) { 
+        @Override
+        public void cloneFields( Cloner cloner, Object original ) {
             this.parentEmitter = cloner.clone(parentEmitter);
         }
-             
+
         public void setSpatial(Spatial spatial) {
         }
 
@@ -211,6 +211,42 @@ public class ParticleEmitter extends Geometry {
         return clone;
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        this.shape = cloner.clone(shape);
+        this.control = cloner.clone(control);
+        this.faceNormal = cloner.clone(faceNormal);
+        this.startColor = cloner.clone(startColor);
+        this.endColor = cloner.clone(endColor);
+        this.particleInfluencer = cloner.clone(particleInfluencer);
+
+        // change in behavior: gravity was not cloned before -pspeed
+        this.gravity = cloner.clone(gravity);
+
+        // So, simply setting the mesh type will cause all kinds of things
+        // to happen:
+        // 1) the new mesh gets created.
+        // 2) it is set to the Geometry
+        // 3) the particles array is recreated because setNumParticles()
+        //
+        // ...so this should be equivalent but simpler than half of the old clone()
+        // method.  Note: we do not ever want to share particleMesh so we do not
+        // clone it at all.
+        setMeshType(meshType);
+
+        // change in behavior: temp and lastPos were not cloned before...
+        // perhaps because it was believed that 'transient' fields were exluded
+        // from cloning?  (they aren't)
+        // If it was ok for these to be shared because of how they are used
+        // then they could just as well be made static... else I think it's clearer
+        // to clone them.
+        this.temp = cloner.clone(temp);
+        this.lastPos = cloner.clone(lastPos);
+    }
+
     public ParticleEmitter(String name, Type type, int numParticles) {
         super(name);
         setBatchHint(BatchHint.Never);
@@ -225,7 +261,7 @@ public class ParticleEmitter extends Geometry {
 
         meshType = type;
 
-        // 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
         shape = shape.deepClone();
         particleInfluencer = particleInfluencer.clone();
@@ -267,10 +303,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the {@link ParticleInfluencer} to influence this particle emitter.
-     * 
-     * @param particleInfluencer the {@link ParticleInfluencer} to influence 
+     *
+     * @param particleInfluencer the {@link ParticleInfluencer} to influence
      * this particle emitter.
-     * 
+     *
      * @see ParticleInfluencer
      */
     public void setParticleInfluencer(ParticleInfluencer particleInfluencer) {
@@ -278,12 +314,12 @@ public class ParticleEmitter extends Geometry {
     }
 
     /**
-     * Returns the {@link ParticleInfluencer} that influences this 
+     * Returns the {@link ParticleInfluencer} that influences this
      * particle emitter.
-     * 
-     * @return the {@link ParticleInfluencer} that influences this 
+     *
+     * @return the {@link ParticleInfluencer} that influences this
      * particle emitter.
-     * 
+     *
      * @see ParticleInfluencer
      */
     public ParticleInfluencer getParticleInfluencer() {
@@ -292,12 +328,12 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Returns the mesh type used by the particle emitter.
-     * 
-     * 
+     *
+     *
      * @return the mesh type used by the particle emitter.
-     * 
+     *
      * @see #setMeshType(com.jme3.effect.ParticleMesh.Type)
-     * @see ParticleEmitter#ParticleEmitter(java.lang.String, com.jme3.effect.ParticleMesh.Type, int) 
+     * @see ParticleEmitter#ParticleEmitter(java.lang.String, com.jme3.effect.ParticleMesh.Type, int)
      */
     public ParticleMesh.Type getMeshType() {
         return meshType;
@@ -325,26 +361,26 @@ public class ParticleEmitter extends Geometry {
     }
 
     /**
-     * Returns true if particles should spawn in world space. 
-     * 
-     * @return true if particles should spawn in world space. 
-     * 
-     * @see ParticleEmitter#setInWorldSpace(boolean) 
+     * Returns true if particles should spawn in world space.
+     *
+     * @return true if particles should spawn in world space.
+     *
+     * @see ParticleEmitter#setInWorldSpace(boolean)
      */
     public boolean isInWorldSpace() {
         return worldSpace;
     }
 
     /**
-     * Set to true if particles should spawn in world space. 
-     * 
+     * Set to true if particles should spawn in world space.
+     *
      * <p>If set to true and the particle emitter is moved in the scene,
      * then particles that have already spawned won't be effected by this
      * motion. If set to false, the particles will emit in local space
      * and when the emitter is moved, so are all the particles that
      * were emitted previously.
-     * 
-     * @param worldSpace true if particles should spawn in world space. 
+     *
+     * @param worldSpace true if particles should spawn in world space.
      */
     public void setInWorldSpace(boolean worldSpace) {
         this.setIgnoreTransform(worldSpace);
@@ -353,7 +389,7 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Returns the number of visible particles (spawned but not dead).
-     * 
+     *
      * @return the number of visible particles
      */
     public int getNumVisibleParticles() {
@@ -365,7 +401,7 @@ public class ParticleEmitter extends Geometry {
      * Set the maximum amount of particles that
      * can exist at the same time with this emitter.
      * Calling this method many times is not recommended.
-     * 
+     *
      * @param numParticles the maximum amount of particles that
      * can exist at the same time with this emitter.
      */
@@ -387,13 +423,13 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Returns a list of all particles (shouldn't be used in most cases).
-     * 
+     *
      * <p>
      * This includes both existing and non-existing particles.
      * The size of the array is set to the <code>numParticles</code> value
      * specified in the constructor or {@link ParticleEmitter#setNumParticles(int) }
-     * method. 
-     * 
+     * method.
+     *
      * @return a list of all particles.
      */
     public Particle[] getParticles() {
@@ -401,11 +437,11 @@ public class ParticleEmitter extends Geometry {
     }
 
     /**
-     * Get the normal which particles are facing. 
-     * 
-     * @return the normal which particles are facing. 
-     * 
-     * @see ParticleEmitter#setFaceNormal(com.jme3.math.Vector3f) 
+     * Get the normal which particles are facing.
+     *
+     * @return the normal which particles are facing.
+     *
+     * @see ParticleEmitter#setFaceNormal(com.jme3.math.Vector3f)
      */
     public Vector3f getFaceNormal() {
         if (Vector3f.isValidVector(faceNormal)) {
@@ -416,8 +452,8 @@ public class ParticleEmitter extends Geometry {
     }
 
     /**
-     * Sets the normal which particles are facing. 
-     * 
+     * Sets the normal which particles are facing.
+     *
      * <p>By default, particles
      * will face the camera, but for some effects (e.g shockwave) it may
      * be necessary to face a specific direction instead. To restore
@@ -437,10 +473,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Returns the rotation speed in radians/sec for particles.
-     * 
+     *
      * @return the rotation speed in radians/sec for particles.
-     * 
-     * @see ParticleEmitter#setRotateSpeed(float) 
+     *
+     * @see ParticleEmitter#setRotateSpeed(float)
      */
     public float getRotateSpeed() {
         return rotateSpeed;
@@ -449,7 +485,7 @@ public class ParticleEmitter extends Geometry {
     /**
      * Set the rotation speed in radians/sec for particles
      * spawned after the invocation of this method.
-     * 
+     *
      * @param rotateSpeed the rotation speed in radians/sec for particles
      * spawned after the invocation of this method.
      */
@@ -459,12 +495,12 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Returns true if every particle spawned
-     * should have a random facing angle. 
-     * 
+     * should have a random facing angle.
+     *
      * @return true if every particle spawned
-     * should have a random facing angle. 
-     * 
-     * @see ParticleEmitter#setRandomAngle(boolean) 
+     * should have a random facing angle.
+     *
+     * @see ParticleEmitter#setRandomAngle(boolean)
      */
     public boolean isRandomAngle() {
         return randomAngle;
@@ -472,8 +508,8 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set to true if every particle spawned
-     * should have a random facing angle. 
-     * 
+     * should have a random facing angle.
+     *
      * @param randomAngle if every particle spawned
      * should have a random facing angle.
      */
@@ -484,11 +520,11 @@ public class ParticleEmitter extends Geometry {
     /**
      * Returns true if every particle spawned should get a random
      * image.
-     * 
+     *
      * @return True if every particle spawned should get a random
      * image.
-     * 
-     * @see ParticleEmitter#setSelectRandomImage(boolean) 
+     *
+     * @see ParticleEmitter#setSelectRandomImage(boolean)
      */
     public boolean isSelectRandomImage() {
         return selectRandomImage;
@@ -498,7 +534,7 @@ public class ParticleEmitter extends Geometry {
      * Set to true if every particle spawned
      * should get a random image from a pool of images constructed from
      * the texture, with X by Y possible images.
-     * 
+     *
      * <p>By default, X and Y are equal
      * to 1, thus allowing only 1 possible image to be selected, but if the
      * particle is configured with multiple images by using {@link ParticleEmitter#setImagesX(int) }
@@ -506,7 +542,7 @@ public class ParticleEmitter extends Geometry {
      * can be selected. Setting to false will cause each particle to have an animation
      * of images displayed, starting at image 1, and going until image X*Y when
      * the particle reaches its end of life.
-     * 
+     *
      * @param selectRandomImage True if every particle spawned should get a random
      * image.
      */
@@ -516,10 +552,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Check if particles spawned should face their velocity.
-     * 
+     *
      * @return True if particles spawned should face their velocity.
-     * 
-     * @see ParticleEmitter#setFacingVelocity(boolean) 
+     *
+     * @see ParticleEmitter#setFacingVelocity(boolean)
      */
     public boolean isFacingVelocity() {
         return facingVelocity;
@@ -528,11 +564,11 @@ public class ParticleEmitter extends Geometry {
     /**
      * Set to true if particles spawned should face
      * their velocity (or direction to which they are moving towards).
-     * 
+     *
      * <p>This is typically used for e.g spark effects.
-     * 
+     *
      * @param followVelocity True if particles spawned should face their velocity.
-     * 
+     *
      */
     public void setFacingVelocity(boolean followVelocity) {
         this.facingVelocity = followVelocity;
@@ -540,10 +576,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the end color of the particles spawned.
-     * 
+     *
      * @return the end color of the particles spawned.
-     * 
-     * @see ParticleEmitter#setEndColor(com.jme3.math.ColorRGBA) 
+     *
+     * @see ParticleEmitter#setEndColor(com.jme3.math.ColorRGBA)
      */
     public ColorRGBA getEndColor() {
         return endColor;
@@ -551,12 +587,12 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the end color of the particles spawned.
-     * 
+     *
      * <p>The
      * particle color at any time is determined by blending the start color
      * and end color based on the particle's current time of life relative
      * to its end of life.
-     * 
+     *
      * @param endColor the end color of the particles spawned.
      */
     public void setEndColor(ColorRGBA endColor) {
@@ -565,10 +601,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the end size of the particles spawned.
-     * 
+     *
      * @return the end size of the particles spawned.
-     * 
-     * @see ParticleEmitter#setEndSize(float) 
+     *
+     * @see ParticleEmitter#setEndSize(float)
      */
     public float getEndSize() {
         return endSize;
@@ -576,12 +612,12 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the end size of the particles spawned.
-     * 
+     *
      * <p>The
      * particle size at any time is determined by blending the start size
      * and end size based on the particle's current time of life relative
      * to its end of life.
-     * 
+     *
      * @param endSize the end size of the particles spawned.
      */
     public void setEndSize(float endSize) {
@@ -590,10 +626,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the gravity vector.
-     * 
+     *
      * @return the gravity vector.
-     * 
-     * @see ParticleEmitter#setGravity(com.jme3.math.Vector3f) 
+     *
+     * @see ParticleEmitter#setGravity(com.jme3.math.Vector3f)
      */
     public Vector3f getGravity() {
         return gravity;
@@ -601,7 +637,7 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * This method sets the gravity vector.
-     * 
+     *
      * @param gravity the gravity vector
      */
     public void setGravity(Vector3f gravity) {
@@ -610,7 +646,7 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Sets the gravity vector.
-     * 
+     *
      * @param x the x component of the gravity vector
      * @param y the y component of the gravity vector
      * @param z the z component of the gravity vector
@@ -623,10 +659,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the high value of life.
-     * 
+     *
      * @return the high value of life.
-     * 
-     * @see ParticleEmitter#setHighLife(float) 
+     *
+     * @see ParticleEmitter#setHighLife(float)
      */
     public float getHighLife() {
         return highLife;
@@ -634,10 +670,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the high value of life.
-     * 
+     *
      * <p>The particle's lifetime/expiration
      * is determined by randomly selecting a time between low life and high life.
-     * 
+     *
      * @param highLife the high value of life.
      */
     public void setHighLife(float highLife) {
@@ -646,10 +682,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the number of images along the X axis (width).
-     * 
+     *
      * @return the number of images along the X axis (width).
-     * 
-     * @see ParticleEmitter#setImagesX(int) 
+     *
+     * @see ParticleEmitter#setImagesX(int)
      */
     public int getImagesX() {
         return imagesX;
@@ -657,11 +693,11 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the number of images along the X axis (width).
-     * 
+     *
      * <p>To determine
      * how multiple particle images are selected and used, see the
      * {@link ParticleEmitter#setSelectRandomImage(boolean) } method.
-     * 
+     *
      * @param imagesX the number of images along the X axis (width).
      */
     public void setImagesX(int imagesX) {
@@ -671,10 +707,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the number of images along the Y axis (height).
-     * 
+     *
      * @return the number of images along the Y axis (height).
-     * 
-     * @see ParticleEmitter#setImagesY(int) 
+     *
+     * @see ParticleEmitter#setImagesY(int)
      */
     public int getImagesY() {
         return imagesY;
@@ -682,10 +718,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the number of images along the Y axis (height).
-     * 
+     *
      * <p>To determine how multiple particle images are selected and used, see the
      * {@link ParticleEmitter#setSelectRandomImage(boolean) } method.
-     * 
+     *
      * @param imagesY the number of images along the Y axis (height).
      */
     public void setImagesY(int imagesY) {
@@ -695,10 +731,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the low value of life.
-     * 
+     *
      * @return the low value of life.
-     * 
-     * @see ParticleEmitter#setLowLife(float) 
+     *
+     * @see ParticleEmitter#setLowLife(float)
      */
     public float getLowLife() {
         return lowLife;
@@ -706,10 +742,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the low value of life.
-     * 
+     *
      * <p>The particle's lifetime/expiration
      * is determined by randomly selecting a time between low life and high life.
-     * 
+     *
      * @param lowLife the low value of life.
      */
     public void setLowLife(float lowLife) {
@@ -719,11 +755,11 @@ public class ParticleEmitter extends Geometry {
     /**
      * Get the number of particles to spawn per
      * second.
-     * 
+     *
      * @return the number of particles to spawn per
      * second.
-     * 
-     * @see ParticleEmitter#setParticlesPerSec(float) 
+     *
+     * @see ParticleEmitter#setParticlesPerSec(float)
      */
     public float getParticlesPerSec() {
         return particlesPerSec;
@@ -732,7 +768,7 @@ public class ParticleEmitter extends Geometry {
     /**
      * Set the number of particles to spawn per
      * second.
-     * 
+     *
      * @param particlesPerSec the number of particles to spawn per
      * second.
      */
@@ -740,13 +776,13 @@ public class ParticleEmitter extends Geometry {
         this.particlesPerSec = particlesPerSec;
         timeDifference = 0;
     }
-    
+
     /**
      * Get the start color of the particles spawned.
-     * 
+     *
      * @return the start color of the particles spawned.
-     * 
-     * @see ParticleEmitter#setStartColor(com.jme3.math.ColorRGBA) 
+     *
+     * @see ParticleEmitter#setStartColor(com.jme3.math.ColorRGBA)
      */
     public ColorRGBA getStartColor() {
         return startColor;
@@ -754,11 +790,11 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the start color of the particles spawned.
-     * 
+     *
      * <p>The particle color at any time is determined by blending the start color
      * and end color based on the particle's current time of life relative
      * to its end of life.
-     * 
+     *
      * @param startColor the start color of the particles spawned
      */
     public void setStartColor(ColorRGBA startColor) {
@@ -767,10 +803,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Get the start color of the particles spawned.
-     * 
+     *
      * @return the start color of the particles spawned.
-     * 
-     * @see ParticleEmitter#setStartSize(float) 
+     *
+     * @see ParticleEmitter#setStartSize(float)
      */
     public float getStartSize() {
         return startSize;
@@ -778,11 +814,11 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set the start size of the particles spawned.
-     * 
+     *
      * <p>The particle size at any time is determined by blending the start size
      * and end size based on the particle's current time of life relative
      * to its end of life.
-     * 
+     *
      * @param startSize the start size of the particles spawned.
      */
     public void setStartSize(float startSize) {
@@ -805,10 +841,10 @@ public class ParticleEmitter extends Geometry {
      * gravity.
      *
      * @deprecated
-     * This method is deprecated. 
+     * This method is deprecated.
      * Use ParticleEmitter.getParticleInfluencer().setInitialVelocity(initialVelocity); instead.
      *
-     * @see ParticleEmitter#setVelocityVariation(float) 
+     * @see ParticleEmitter#setVelocityVariation(float)
      * @see ParticleEmitter#setGravity(float)
      */
     @Deprecated
@@ -818,7 +854,7 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * @deprecated
-     * This method is deprecated. 
+     * This method is deprecated.
      * Use ParticleEmitter.getParticleInfluencer().getVelocityVariation(); instead.
      * @return the initial velocity variation factor
      */
@@ -833,9 +869,9 @@ public class ParticleEmitter extends Geometry {
      * 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. 
+     * This method is deprecated.
      * Use ParticleEmitter.getParticleInfluencer().setVelocityVariation(variation); instead.
      */
     @Deprecated
@@ -923,7 +959,7 @@ public class ParticleEmitter extends Geometry {
 
         vars.release();
     }
-    
+
     /**
      * Instantly kills all active particles, after this method is called, all
      * particles will be dead and no longer visible.
@@ -935,12 +971,12 @@ public class ParticleEmitter extends Geometry {
             }
         }
     }
-    
+
     /**
      * Kills the particle at the given index.
-     * 
+     *
      * @param index The index of the particle to kill
-     * @see #getParticles() 
+     * @see #getParticles()
      */
     public void killParticle(int index){
         freeParticle(index);
@@ -995,7 +1031,7 @@ public class ParticleEmitter extends Geometry {
             p.imageIndex = (int) (b * imagesX * imagesY);
         }
     }
-    
+
     private void updateParticleState(float tpf) {
         // Force world transform to update
         this.getWorldTransform();
@@ -1028,7 +1064,7 @@ public class ParticleEmitter extends Geometry {
                 firstUnUsed++;
             }
         }
-        
+
         // Spawns particles within the tpf timeslot with proper age
         float interval = 1f / particlesPerSec;
         float originalTpf = tpf;
@@ -1065,10 +1101,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Set to enable or disable the particle emitter
-     * 
+     *
      * <p>When a particle is
      * disabled, it will be "frozen in time" and not update.
-     * 
+     *
      * @param enabled True to enable the particle emitter
      */
     public void setEnabled(boolean enabled) {
@@ -1077,10 +1113,10 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Check if a particle emitter is enabled for update.
-     * 
+     *
      * @return True if a particle emitter is enabled for update.
-     * 
-     * @see ParticleEmitter#setEnabled(boolean) 
+     *
+     * @see ParticleEmitter#setEnabled(boolean)
      */
     public boolean isEnabled() {
         return enabled;
@@ -1088,7 +1124,7 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Callback from Control.update(), do not use.
-     * @param tpf 
+     * @param tpf
      */
     public void updateFromControl(float tpf) {
         if (enabled) {
@@ -1098,9 +1134,9 @@ public class ParticleEmitter extends Geometry {
 
     /**
      * Callback from Control.render(), do not use.
-     * 
+     *
      * @param rm
-     * @param vp 
+     * @param vp
      */
     private void renderFromControl(RenderManager rm, ViewPort vp) {
         Camera cam = vp.getCamera();
@@ -1236,7 +1272,7 @@ public class ParticleEmitter extends Geometry {
                 gravity.y = ic.readFloat("gravity", 0);
             }
         } else {
-            // since the parentEmitter is not loaded, it must be 
+            // since the parentEmitter is not loaded, it must be
             // loaded separately
             control = getControl(ParticleEmitterControl.class);
             control.parentEmitter = this;

+ 33 - 2
jme3-core/src/main/java/com/jme3/effect/influencers/DefaultParticleInfluencer.java

@@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 
 /**
@@ -49,7 +51,7 @@ import java.io.IOException;
  */
 public class DefaultParticleInfluencer implements ParticleInfluencer {
 
-    //Version #1 : changed startVelocity to initialvelocity for consistency with accessors 
+    //Version #1 : changed startVelocity to initialvelocity for consistency with accessors
     //and also changed it in serialization
     public static final int SAVABLE_VERSION = 1;
     /** Temporary variable used to help with calculations. */
@@ -94,7 +96,7 @@ public class DefaultParticleInfluencer implements ParticleInfluencer {
             initialVelocity = (Vector3f) ic.readSavable("startVelocity", Vector3f.ZERO.clone());
         }else{
             initialVelocity = (Vector3f) ic.readSavable("initialVelocity", Vector3f.ZERO.clone());
-        }       
+        }
         velocityVariation = ic.readFloat("variation", 0.2f);
     }
 
@@ -109,6 +111,35 @@ public class DefaultParticleInfluencer implements ParticleInfluencer {
         }
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        this.initialVelocity = cloner.clone(initialVelocity);
+
+        // Change in behavior: I'm cloning 'for real' the 'temp' field because
+        // otherwise it will be shared across all clones.  Note: if this is
+        // ok because of how its used then it might as well be static and let
+        // everything share it.
+        // Note 2: transient fields _are_ cloned just like anything else so
+        // thinking it wouldn't get cloned is also not right.
+        // -pspeed
+        this.temp = cloner.clone(temp);
+    }
+
     @Override
     public void setInitialVelocity(Vector3f initialVelocity) {
         this.initialVelocity.set(initialVelocity);

+ 21 - 0
jme3-core/src/main/java/com/jme3/effect/influencers/EmptyParticleInfluencer.java

@@ -36,6 +36,8 @@ import com.jme3.effect.shapes.EmitterShape;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 
 /**
@@ -83,4 +85,23 @@ public class EmptyParticleInfluencer implements ParticleInfluencer {
             throw new AssertionError();
         }
     }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+    }
 }

+ 2 - 0
jme3-core/src/main/java/com/jme3/effect/influencers/NewtonianParticleInfluencer.java

@@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.FastMath;
 import com.jme3.math.Matrix3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 
 /**

+ 2 - 1
jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java

@@ -36,12 +36,13 @@ import com.jme3.effect.ParticleEmitter;
 import com.jme3.effect.shapes.EmitterShape;
 import com.jme3.export.Savable;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.JmeCloneable;
 
 /**
  * An interface that defines the methods to affect initial velocity of the particles.
  * @author Marcin Roguski (Kaelthas)
  */
-public interface ParticleInfluencer extends Savable, Cloneable {
+public interface ParticleInfluencer extends Savable, Cloneable, JmeCloneable {
 
     /**
      * This method influences the particle.

+ 15 - 4
jme3-core/src/main/java/com/jme3/effect/influencers/RadialParticleInfluencer.java

@@ -38,6 +38,7 @@ import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
 import java.io.IOException;
 
 /**
@@ -81,7 +82,7 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
 
     /**
      * the origin used for computing the radial velocity direction
-     * @param origin 
+     * @param origin
      */
     public void setOrigin(Vector3f origin) {
         this.origin = origin;
@@ -97,7 +98,7 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
 
     /**
      * the radial velocity
-     * @param radialVelocity 
+     * @param radialVelocity
      */
     public void setRadialVelocity(float radialVelocity) {
         this.radialVelocity = radialVelocity;
@@ -105,7 +106,7 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
 
     /**
      * nullify y component of particle velocity to make the effect expand only on x and z axis
-     * @return 
+     * @return
      */
     public boolean isHorizontal() {
         return horizontal;
@@ -113,12 +114,22 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
 
     /**
      * nullify y component of particle velocity to make the effect expand only on x and z axis
-     * @param horizontal 
+     * @param horizontal
      */
     public void setHorizontal(boolean horizontal) {
         this.horizontal = horizontal;
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        // Change in behavior: the old origin was not cloned -pspeed
+        this.origin = cloner.clone(origin);
+    }
+
+
     @Override
     public void write(JmeExporter ex) throws IOException {
         super.write(ex);

+ 23 - 0
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterBoxShape.java

@@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 
 public class EmitterBoxShape implements EmitterShape {
@@ -86,6 +88,27 @@ public class EmitterBoxShape implements EmitterShape {
         }
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        this.min = cloner.clone(min);
+        this.len = cloner.clone(len);
+    }
+
     public Vector3f getMin() {
         return min;
     }

+ 24 - 1
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java

@@ -40,6 +40,8 @@ import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.util.BufferUtils;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -168,6 +170,27 @@ public class EmitterMeshVertexShape implements EmitterShape {
         }
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        this.vertices = cloner.clone(vertices);
+        this.normals = cloner.clone(normals);
+    }
+
     @Override
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);
@@ -180,7 +203,7 @@ public class EmitterMeshVertexShape implements EmitterShape {
     public void read(JmeImporter im) throws IOException {
         InputCapsule ic = im.getCapsule(this);
         this.vertices = ic.readSavableArrayList("vertices", null);
-        
+
         List<List<Vector3f>> tmpNormals = ic.readSavableArrayList("normals", null);
         if (tmpNormals != null){
             this.normals = tmpNormals;

+ 22 - 0
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterPointShape.java

@@ -35,6 +35,8 @@ import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 
 public class EmitterPointShape implements EmitterShape {
@@ -59,6 +61,26 @@ public class EmitterPointShape implements EmitterShape {
         }
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        this.point = cloner.clone(point);
+    }
+
     @Override
     public void getRandomPoint(Vector3f store) {
         store.set(point);

+ 2 - 1
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterShape.java

@@ -33,12 +33,13 @@ package com.jme3.effect.shapes;
 
 import com.jme3.export.Savable;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.JmeCloneable;
 
 /**
  * This interface declares methods used by all shapes that represent particle emitters.
  * @author Kirill
  */
-public interface EmitterShape extends Savable, Cloneable {
+public interface EmitterShape extends Savable, Cloneable, JmeCloneable {
 
     /**
      * This method fills in the initial position of the particle.

+ 22 - 0
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java

@@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 
 public class EmitterSphereShape implements EmitterShape {
@@ -71,6 +73,26 @@ public class EmitterSphereShape implements EmitterShape {
         }
     }
 
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     *  Called internally by com.jme3.util.clone.Cloner.  Do not call directly.
+     */
+    @Override
+    public void cloneFields( Cloner cloner, Object original ) {
+        this.center = cloner.clone(center);
+    }
+
     @Override
     public void getRandomPoint(Vector3f store) {
         do {