Falling Particles and a Floor

Hey there.



I want a particle effect with gravity influence (fine so far) and the particles have to collide with and then bounce off the floor of my scene. is this only doable with physics?



how would i do smth. like that?



so long,

Andy

a very simple approach could be an additional particle influence that does something like

if ( particle.pos.y < floorY ) particle.speed.y = -particle.speed.y;

thx, good idea, i am working on a FloorInfluence.



is it possible that a particle has its own coordinate system, since the values i get from Particle.getPosition() are FAR away from where they are in the scene?



thx in advance for the help,

so long,

Andy

They should be in world space iirc.

ok, solved the problem and integrated it into the RenParticleEditor. neat particles with parameterizable "bouncyness factor"  :wink: and flexible floor. shall i somehow check it into CVS (i guess id need rights for that)? or just paste the changed classes?



so long,

Andy

You could put up a patch and it will be looked at :slight_smile:

wow, my fist patch  :lol:



Index: src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java
===================================================================
RCS file: /cvs/jme/src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java,v
retrieving revision 1.3
diff -u -r1.3 ParticleInfluencePanel.java
--- src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java   21 Sep 2007 15:45:33 -0000   1.3
+++ src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java   28 May 2008 19:17:23 -0000
@@ -18,6 +18,7 @@
 
 import com.jme.math.Line;
 import com.jme.math.Vector3f;
+import com.jmex.effects.particles.FloorInfluence;
 import com.jmex.effects.particles.ParticleGeometry;
 import com.jmex.effects.particles.ParticleInfluence;
 import com.jmex.effects.particles.SimpleParticleInfluenceFactory;
@@ -70,7 +71,8 @@
                             "drag",
                             "vortex",
                             "swarm",
-                            "wander"
+                            "wander",
+                            "floor"
                         },
                         null);
 
@@ -91,6 +93,8 @@
                     infl = new SwarmInfluence(new Vector3f(), 3);
                 } else if ("wander".equals(chosen)) {
                     infl = new WanderInfluence();
+                } else if ("floor".equals(chosen)) {
+                    infl = new FloorInfluence(new Vector3f(0, -1, 0), new Vector3f(0, 1, 0), 0.75f);
                 }
                 return infl;
             }
@@ -187,7 +191,13 @@
             influencePanel.updateWidgets();
             influenceParamsPanel.add(influencePanel);
 
-        }
+        } else if (influence instanceof FloorInfluence) {
+          FloorInfluencePanel floorInfluencePanel = new FloorInfluencePanel();
+          floorInfluencePanel.setEdittedInfluence(influence);
+          floorInfluencePanel.updateWidgets();
+          influenceParamsPanel.add(floorInfluencePanel);
+
+      }
         influenceParamsPanel.getParent().validate();
         influenceParamsPanel.getParent().repaint();
     }
@@ -216,6 +226,8 @@
                 return "Swarm";
             } else if (pf instanceof WanderInfluence) {
                 return "Wander";
+            } else if (pf instanceof FloorInfluence) {
+                return "Floor";
             } else {
                 return "???";
             }
Index: src/com/jmex/editors/swing/particles/FloorInfluencePanel.java
===================================================================
RCS file: src/com/jmex/editors/swing/particles/FloorInfluencePanel.java
diff -N src/com/jmex/editors/swing/particles/FloorInfluencePanel.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/com/jmex/editors/swing/particles/FloorInfluencePanel.java   1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,68 @@
+
+package com.jmex.editors.swing.particles;
+
+import java.awt.BorderLayout;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import com.jme.math.Vector3f;
+import com.jmex.editors.swing.widget.ValuePanel;
+import com.jmex.editors.swing.widget.VectorPanel;
+import com.jmex.effects.particles.FloorInfluence;
+
+
+public class FloorInfluencePanel extends InfluenceEditPanel {
+
+  private static final long serialVersionUID = 1L;
+
+  private VectorPanel posVector = new VectorPanel(-Float.MAX_VALUE, Float.MAX_VALUE, 0.1f);
+
+  private VectorPanel normalVector = new VectorPanel(-Float.MAX_VALUE, Float.MAX_VALUE, 0.1f);
+
+  private ValuePanel bouncynessValue = new ValuePanel("Bouncyness: ", "", 0, Float.MAX_VALUE, 0.01f);

+  public FloorInfluencePanel() {
+    super();
+    setLayout(new BorderLayout());
+    initPanel();
+  }
+
+
+  private void initPanel() {
+    posVector.setBorder(createTitledBorder(" PLANE POSITION "));
+    posVector.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        ((FloorInfluence)getEdittedInfluence()).setNormal(posVector.getValue());
+      }
+    });
+    add(posVector, BorderLayout.NORTH);
+   
+   
+    normalVector.setBorder(createTitledBorder(" PLANE NORMAL "));
+    normalVector.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        ((FloorInfluence)getEdittedInfluence()).setNormal(normalVector.getValue());
+      }
+    });
+    add(normalVector, BorderLayout.CENTER);
+   
+    bouncynessValue.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        ((FloorInfluence)getEdittedInfluence()).setBouncyness(bouncynessValue.getFloatValue());
+      }
+    });
+    add(bouncynessValue, BorderLayout.SOUTH);
+  }
+
+
+  @Override
+  public void updateWidgets() {
+    posVector.setValue(((FloorInfluence)getEdittedInfluence()).getPos());
+    normalVector.setValue(((FloorInfluence)getEdittedInfluence()).getNormal());
+    bouncynessValue.setValue(((FloorInfluence)getEdittedInfluence()).getBouncyness());
+  }
+}
Index: src/com/jmex/effects/particles/FloorInfluence.java
===================================================================
RCS file: src/com/jmex/effects/particles/FloorInfluence.java
diff -N src/com/jmex/effects/particles/FloorInfluence.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/com/jmex/effects/particles/FloorInfluence.java   1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,116 @@
+package com.jmex.effects.particles;
+
+import com.jme.math.Plane;
+import com.jme.math.Quaternion;
+import com.jme.math.Vector3f;
+
+/**
+ * @author andgra
+ *
+ */
+public class FloorInfluence extends ParticleInfluence {
+
+  /**
+   * Bouncyness is the factor of multiplication when bouncing off the floor. A bouncyness factor of
+   * 1 means the ball leaves the floor with the same velocity as it hit the floor.
+   */
+  private float bouncyness = 1;
+
+  /**
+   * The normal vector of the floor. Same semantics as in math.Plane.
+   */
+  private Vector3f normal;
+
+  /**
+   * The position vector for the (imaginary) center of the floor.
+   */
+  private Vector3f pos;
+
+  private Plane floor;
+
+  /**
+   *
+   * @param pos
+   *          The position vector for the (imaginary) center of the floor.
+   * @param normal
+   *          The normal vector of the floor. Same semantics as in math.Plane.
+   * @param bouncynessBouncyness
+   *          is the factor of multiplication when bouncing off the floor. A bouncyness factor of 1
+   *          means the ball leaves the floor with the same velocity as it hit the floor, much like
+   *          a rubber ball.
+   */
+  public FloorInfluence(Vector3f pos, Vector3f normal, float bouncyness) {
+    this.bouncyness = bouncyness;
+    this.normal = normal;
+    this.pos = pos;
+    this.floor = new Plane(normal, normal.dot(pos));
+  }
+
+  @Override
+  public void apply(float dt, Particle particle, int index) {
+
+    if (particle.getStatus() == Particle.ALIVE && floor.pseudoDistance(particle.getPosition()) <= 0) {
+
+      float t = (floor.getNormal().dot(particle.getPosition()) - floor.getConstant())
+          / floor.getNormal().dot(particle.getVelocity());
+      Vector3f s = particle.getPosition().subtract(particle.getVelocity().mult(t));
+
+      this.normal.normalizeLocal();
+      Vector3f v1 = this.normal.cross(s.subtract(pos));
+      Vector3f v2 = this.normal.cross(v1);
+      v1.normalizeLocal();
+      v2.normalizeLocal();
+
+      Vector3f newVel = new Vector3f(particle.getVelocity());
+      newVel.y *= -bouncyness;
+      Quaternion q = new Quaternion();
+      q.fromAxes(v1, this.normal, v2);
+      newVel = q.mult(newVel);
+
+      particle.setVelocity(newVel);
+    } // if
+  }
+
+  public float getBouncyness() {
+    return bouncyness;
+  }
+
+  public void setBouncyness(float bouncyness) {
+    this.bouncyness = bouncyness;
+  }
+
+  public Plane getFloor() {
+    return floor;
+  }
+
+  public void setFloor(Plane floor) {
+
+    this.floor = floor;
+  }
+
+  public Vector3f getNormal() {
+    return normal;
+  }
+
+  public void setNormal(Vector3f normal) {
+    this.normal = normal;
+    this.floor = new Plane(normal, normal.dot(pos));
+  }
+
+  public Vector3f getPos() {
+    return pos;
+  }
+
+  public void setPos(Vector3f pos) {
+    this.pos = pos;
+    this.floor = new Plane(normal, normal.dot(pos));
+  }
+
+}



so long,
Andy

heres the 2.0 version:



MOD: There was a mistake in it … fixed it.



Index: src/com/jmex/effects/particles/FloorInfluence.java
===================================================================
--- src/com/jmex/effects/particles/FloorInfluence.java   (revision 0)
+++ src/com/jmex/effects/particles/FloorInfluence.java   (revision 0)
@@ -0,0 +1,115 @@
+package com.jmex.effects.particles;
+
+import com.jme.math.Plane;
+import com.jme.math.Quaternion;
+import com.jme.math.Vector3f;
+
+
+/**
+ * @author andgra
+ */
+public class FloorInfluence extends ParticleInfluence {
+
+  /**
+   * Bouncyness is the factor of multiplication when bouncing off the floor. A bouncyness factor of 1 means the ball
+   * leaves the floor with the same velocity as it hit the floor.
+   */
+  private float bouncyness = 1;
+
+  /**
+   * The normal vector of the floor. Same semantics as in math.Plane.
+   */
+  private Vector3f normal;
+
+  /**
+   * The position vector for the (imaginary) center of the floor.
+   */
+  private Vector3f pos;
+
+  private Plane floor;
+
+
+  /**
+   * @param pos The position vector for the (imaginary) center of the floor.
+   * @param normal The normal vector of the floor. Same semantics as in math.Plane.
+   * @param bouncynessBouncyness is the factor of multiplication when bouncing off the floor. A bouncyness factor of 1
+   *          means the ball leaves the floor with the same velocity as it hit the floor, much like a rubber ball.
+   */
+  public FloorInfluence(Vector3f pos, Vector3f normal, float bouncyness) {
+    this.bouncyness = bouncyness;
+    this.normal = normal;
+    this.pos = pos;
+    this.floor = new Plane(normal, normal.dot(pos));
+  }
+
+
+  @Override
+  public void apply(float dt, Particle particle, int index) {
+
+    if (particle.getStatus() == Particle.Status.Alive && floor.pseudoDistance(particle.getPosition()) <= 0) {
+
+      float t =
+          (floor.getNormal().dot(particle.getPosition()) - floor.getConstant())
+              / floor.getNormal().dot(particle.getVelocity());
+      Vector3f s = particle.getPosition().subtract(particle.getVelocity().mult(t));
+
+      this.normal.normalizeLocal();
+      Vector3f v1 = this.normal.cross(s.subtract(pos));
+      Vector3f v2 = this.normal.cross(v1);
+      v1.normalizeLocal();
+      v2.normalizeLocal();
+
+      Vector3f newVel = new Vector3f(particle.getVelocity());
+      newVel.y *= -bouncyness;
+      Quaternion q = new Quaternion();
+      q.fromAxes(v1, this.normal, v2);
+      newVel = q.mult(newVel);
+
+      particle.setVelocity(newVel);
+    } // if
+  }
+
+
+  public float getBouncyness() {
+    return bouncyness;
+  }
+
+
+  public void setBouncyness(float bouncyness) {
+    this.bouncyness = bouncyness;
+  }
+
+
+  public Plane getFloor() {
+    return floor;
+  }
+
+
+  public void setFloor(Plane floor) {
+
+    this.floor = floor;
+  }
+
+
+  public Vector3f getNormal() {
+    return normal;
+  }
+
+
+  public void setNormal(Vector3f normal) {
+    this.normal = normal;
+    this.floor = new Plane(normal, normal.dot(pos));
+  }
+
+
+  public Vector3f getPos() {
+    return pos;
+  }
+
+
+  public void setPos(Vector3f pos) {
+    this.pos = pos;
+    this.floor = new Plane(normal, normal.dot(pos));
+  }
+
+}
Index: src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java
===================================================================
--- src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java   (revision 3857)
+++ src/com/jmex/editors/swing/particles/ParticleInfluencePanel.java   (working copy)
@@ -18,8 +18,9 @@
 
 import com.jme.math.Line;
 import com.jme.math.Vector3f;
+import com.jmex.effects.particles.FloorInfluence;
+import com.jmex.effects.particles.ParticleInfluence;
 import com.jmex.effects.particles.ParticleSystem;
-import com.jmex.effects.particles.ParticleInfluence;
 import com.jmex.effects.particles.SimpleParticleInfluenceFactory;
 import com.jmex.effects.particles.SwarmInfluence;
 import com.jmex.effects.particles.WanderInfluence;
@@ -70,7 +71,8 @@
                             "drag",
                             "vortex",
                             "swarm",
-                            "wander"
+                            "wander",
+                            "floor"
                         },
                         null);
 
@@ -91,6 +93,8 @@
                     infl = new SwarmInfluence(new Vector3f(), 3);
                 } else if ("wander".equals(chosen)) {
                     infl = new WanderInfluence();
+                }else if ("floor".equals(chosen)) {
+                    infl = new FloorInfluence(new Vector3f(0, -1, 0), new Vector3f(0, 1, 0), 0.75f);
                 }
                 return infl;
             }
@@ -187,6 +191,11 @@
             influencePanel.updateWidgets();
             influenceParamsPanel.add(influencePanel);
 
+        } else if (influence instanceof FloorInfluence) {
+          FloorInfluencePanel floorInfluencePanel = new FloorInfluencePanel();
+          floorInfluencePanel.setEdittedInfluence(influence);
+          floorInfluencePanel.updateWidgets();
+          influenceParamsPanel.add(floorInfluencePanel);
         }
         influenceParamsPanel.getParent().validate();
         influenceParamsPanel.getParent().repaint();
@@ -216,6 +225,8 @@
                 return "Swarm";
             } else if (pf instanceof WanderInfluence) {
                 return "Wander";
+            } else if (pf instanceof FloorInfluence) {
+                return "Floor";
             } else {
                 return "???";
             }
Index: src/com/jmex/editors/swing/particles/FloorInfluencePanel.java
===================================================================
--- src/com/jmex/editors/swing/particles/FloorInfluencePanel.java   (revision 0)
+++ src/com/jmex/editors/swing/particles/FloorInfluencePanel.java   (revision 0)
@@ -0,0 +1,67 @@
+
+package com.jmex.editors.swing.particles;
+
+import java.awt.BorderLayout;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import com.jmex.editors.swing.widget.ValuePanel;
+import com.jmex.editors.swing.widget.VectorPanel;
+import com.jmex.effects.particles.FloorInfluence;
+
+
+public class FloorInfluencePanel extends InfluenceEditPanel {
+
+  private static final long serialVersionUID = 1L;
+
+  private VectorPanel posVector = new VectorPanel(-Float.MAX_VALUE, Float.MAX_VALUE, 0.1f);
+
+  private VectorPanel normalVector = new VectorPanel(-Float.MAX_VALUE, Float.MAX_VALUE, 0.1f);
+
+  private ValuePanel bouncynessValue = new ValuePanel("Bouncyness: ", "", 0, Float.MAX_VALUE, 0.01f);
+
+
+  public FloorInfluencePanel() {
+    super();
+    setLayout(new BorderLayout());
+    initPanel();
+  }
+
+
+  private void initPanel() {
+    posVector.setBorder(createTitledBorder(" PLANE POSITION "));
+    posVector.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        ((FloorInfluence)getEdittedInfluence()).setPos(posVector.getValue());
+      }
+    });
+    add(posVector, BorderLayout.NORTH);
+
+    normalVector.setBorder(createTitledBorder(" PLANE NORMAL "));
+    normalVector.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        ((FloorInfluence)getEdittedInfluence()).setNormal(normalVector.getValue());
+      }
+    });
+    add(normalVector, BorderLayout.CENTER);
+
+    bouncynessValue.addChangeListener(new ChangeListener() {
+
+      public void stateChanged(ChangeEvent e) {
+        ((FloorInfluence)getEdittedInfluence()).setBouncyness(bouncynessValue.getFloatValue());
+      }
+    });
+    add(bouncynessValue, BorderLayout.SOUTH);
+  }
+
+
+  @Override
+  public void updateWidgets() {
+    posVector.setValue(((FloorInfluence)getEdittedInfluence()).getPos());
+    normalVector.setValue(((FloorInfluence)getEdittedInfluence()).getNormal());
+    bouncynessValue.setValue(((FloorInfluence)getEdittedInfluence()).getBouncyness());
+  }
+}



... and thanks for all the fish.

Don't suppose you'd port that to 2.0 would you?  :stuck_out_tongue:

well … i would. the patch is for the current cvs version … how can i get my hands on 2.0?



so long,

Andy

dhdd said:

how can i get my hands on 2.0?


http://code.google.com/p/jmonkeyengine/

Not bad.  I've implemented this into 2.0.  I wonder if particle mass could be usefully incorporated into this influence…