/*
* Copyright (c) 2003-2009 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//package com.jmex.effects.particles;
//import com.jmex.effects.particles.ParticleAppearanceRamp;
import com.jmex.effects.particles.ParticleInfluence;
//import com.jmex.effects.particles.TexAnimation;
import java.io.IOException;
//import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.logging.Logger;
import com.jme.math.FastMath;
import com.jme.math.Line;
import com.jme.math.Matrix3f;
import com.jme.math.Rectangle;
import com.jme.math.Ring;
import com.jme.math.TransformMatrix;
import com.jme.math.Triangle;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
//import com.jme.scene.Controller;
//import com.jme.scene.Geometry;
//import com.jme.scene.Node;
//import com.jme.scene.TriMesh;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
/**
* ParticleGeometry is an abstract class representing a particle system. A
* ParticleController must be attached for the effect to be complete.
*
* @author Joshua Slack
* @version $Id: ParticleSystem.java 4133 2009-03-19 20:40:11Z blaine.dev $
*/
public abstract class ParticleSystem extends Object3D // Node
{
static final long serialVersionUID = 0;
private static final Logger logger = Logger.getLogger(ParticleSystem.class.getName());
// protected static final long serialVersionUID = 2L;
public enum EmitType
{
Point, Line, Rectangle, Ring, Geometry;
}
public enum ParticleType
{
Quad, Triangle, Point, Line, GeomMesh;
}
protected static final float DEFAULT_END_SIZE = 4f;
protected static final float DEFAULT_START_SIZE = 20f;
protected static final float DEFAULT_MAX_ANGLE = 0.7853982f;
protected static final float DEFAULT_MAX_LIFE = 10; // 3.000f;
protected static final float DEFAULT_MIN_LIFE = 1.000f;
protected static final ColorRGBA DEFAULT_START_COLOR = new ColorRGBA(1.0f,
0.0f, 0.0f, 1.0f);
protected static final ColorRGBA DEFAULT_END_COLOR = new ColorRGBA(1.0f,
1.0f, 0.0f, 0.0f);
protected com.jmex.effects.particles.ParticleSystem.ParticleType particleType;
protected EmitType emitType = EmitType.Point;
protected Line psLine;
protected Rectangle psRect;
protected Geometry psGeom;
protected Ring psRing;
protected boolean cameraFacing = true;
protected boolean velocityAligned = false;
protected boolean particlesInWorldCoords = true;
protected float startSize, endSize;
protected ColorRGBA startColor;
protected ColorRGBA endColor;
protected ParticleAppearanceRamp ramp = new ParticleAppearanceRamp();
//protected TexAnimation texAnimation = new TexAnimation();
protected float initialVelocity;
protected float minimumLifeTime, maximumLifeTime;
float density = 1;
float standby = 0;
float creationdelay = 0;
protected float minimumAngle, maximumAngle;
protected float minimumSize = 1, maximumSize = 2;
protected float startSpin, endSpin = (float)Math.PI;
protected float startMass, endMass;
protected int startTexIndex, texQuantity;
protected Vector3f emissionDirection;
protected TransformMatrix emitterTransform = new TransformMatrix();
protected Vector3f worldEmit = new Vector3f();
protected int numParticles;
protected boolean rotateWithScene = false;
protected Matrix3f rotMatrix;
protected float particleOrientation;
protected FloatBuffer geometryCoordinates;
protected FloatBuffer appearanceColors;
protected FloatBuffer sizesBuffer;
// vectors to prevent repeated object creation:
protected Vector3f upXemit, absUpVector, abUpMinUp;
protected Vector3f upVector;
protected Vector3f leftVector;
protected Vector3f invScale;
protected Particle[] particles;
// protected Vector3f particleSpeed;
protected int releaseRate; // particles per second
protected Vector3f originOffset;
protected Vector3f originCenter;
protected static Vector2f workVect2 = new Vector2f();
protected static Vector3f workVect3 = new Vector3f();
protected Geometry particleGeom;
protected ParticleController controller;
/////////
/** Spatial's world absolute scale. */
protected Vector3f worldScale = new Vector3f(1, 1, 1);
//////
public ParticleSystem()
{
}
public ParticleSystem(String name, int numParticles)
{
this(name, numParticles, com.jmex.effects.particles.ParticleSystem.ParticleType.Point); // Quad);
}
public ParticleSystem(String name, int numParticles,
com.jmex.effects.particles.ParticleSystem.ParticleType particleType)
{
super(name);
this.numParticles = numParticles;
this.particleType = particleType;
emissionDirection = new Vector3f(0.0f, 1.0f, 0.0f);
minimumLifeTime = DEFAULT_MIN_LIFE;
maximumLifeTime = DEFAULT_MAX_LIFE;
maximumAngle = DEFAULT_MAX_ANGLE;
startSize = DEFAULT_START_SIZE;
endSize = DEFAULT_END_SIZE;
startColor = DEFAULT_START_COLOR.clone();
endColor = DEFAULT_END_COLOR.clone();
upVector = Vector3f.UNIT_Y.clone();
leftVector = new Vector3f(-1, 0, 0);
originCenter = new Vector3f();
originOffset = new Vector3f();
startSpin = 0;
endSpin = 0;
startMass = 1;
endMass = 1;
startTexIndex = 0;
texQuantity = 1;
releaseRate = numParticles;
// init working vectors.. used to prevent additional object creation.
upXemit = new Vector3f();
absUpVector = new Vector3f();
abUpMinUp = new Vector3f();
initialVelocity = 1.0f;
rotMatrix = new Matrix3f();
initializeParticles(numParticles);
}
protected abstract void initializeParticles(int numParticles);
public static int getVertsForParticleType(com.jmex.effects.particles.ParticleSystem.ParticleType type)
{
switch (type)
{
case Triangle:
case GeomMesh:
return 3;
case Point:
return 1;
case Line:
return 2;
case Quad:
return 4;
}
if (type == null)
{
throw new NullPointerException("type is null");
}
throw new IllegalArgumentException("Invalid ParticleType: " + type);
}
public void forceRespawn()
{
for (int i = particles.length; --i >= 0;)
{
//particles[i].recreateParticle(getRandomLifeSpan(), getRandomSize());
controller.Recreate(i);
// particles[i].setStatus(Particle.Status0.Alive);
// particles[i].updateAndCheck(1);
// particles[i].setStatus(Particle.Status0.Available);
}
if (controller != null)
{
controller.setActive(true);
}
}
public boolean isAllParticleInactive()
{
for (int i = particles.length; --i >= 0;)
{
if (particles[i].getStatus() == Particle.Status0.Alive)
return false;
}
return true;
}
/**
* Setup the rotation matrix used to determine initial particle velocity
* based on emission angle and emission direction. called automatically by
* the set* methods for those parameters.
*/
protected Vector3f oldEmit = new Vector3f(Float.NaN, Float.NaN, Float.NaN);
protected float matData[][] = new float[3][3];
public void updateRotationMatrix()
{
if (oldEmit.equals(worldEmit))
{
return;
}
float upDotEmit = upVector.dot(worldEmit);
if (FastMath.abs(upDotEmit) > 1.0d - FastMath.DBL_EPSILON)
{
absUpVector.x = upVector.x <= 0.0f ? -upVector.x : upVector.x;
absUpVector.y = upVector.y <= 0.0f ? -upVector.y : upVector.y;
absUpVector.z = upVector.z <= 0.0f ? -upVector.z : upVector.z;
if (absUpVector.x < absUpVector.y)
{
if (absUpVector.x < absUpVector.z)
{
absUpVector.x = 1.0f;
absUpVector.y = absUpVector.z = 0.0f;
} else
{
absUpVector.z = 1.0f;
absUpVector.x = absUpVector.y = 0.0f;
}
} else if (absUpVector.y < absUpVector.z)
{
absUpVector.y = 1.0f;
absUpVector.x = absUpVector.z = 0.0f;
} else
{
absUpVector.z = 1.0f;
absUpVector.x = absUpVector.y = 0.0f;
}
absUpVector.subtract(upVector, abUpMinUp);
absUpVector.subtract(worldEmit, upXemit);
float f4 = 2.0f / abUpMinUp.dot(abUpMinUp);
float f6 = 2.0f / upXemit.dot(upXemit);
float f8 = f4 * f6 * abUpMinUp.dot(upXemit);
float af1[] = {abUpMinUp.x, abUpMinUp.y, abUpMinUp.z};
float af2[] = {upXemit.x, upXemit.y, upXemit.z};
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
matData[i][j] = (-f4 * af1[i] * af1[j] - f6 * af2[i] * af2[j]) + f8 * af2[i] * af1[j];
}
matData[i][i]++;
}
} else
{
upVector.cross(worldEmit, upXemit);
float f2 = 1.0f / (1.0f + upDotEmit);
float f5 = f2 * upXemit.x;
float f7 = f2 * upXemit.z;
float f9 = f5 * upXemit.y;
float f10 = f5 * upXemit.z;
float f11 = f7 * upXemit.y;
matData[0][0] = upDotEmit + f5 * upXemit.x;
matData[0][1] = f9 - upXemit.z;
matData[0][2] = f10 + upXemit.y;
matData[1][0] = f9 + upXemit.z;
matData[1][1] = upDotEmit + f2 * upXemit.y * upXemit.y;
matData[1][2] = f11 - upXemit.x;
matData[2][0] = f10 - upXemit.y;
matData[2][1] = f11 + upXemit.x;
matData[2][2] = upDotEmit + f7 * upXemit.z;
}
rotMatrix.set(matData);
oldEmit.set(worldEmit);
}
public /*abstract*/ Geometry getParticleGeometry()
{
return particleGeom;
}
public ParticleController getParticleController()
{
return controller;
}
public void addController(Controller c)
{
// super.addController(c);
if (c instanceof ParticleController)
{
this.controller = (ParticleController) c;
}
}
public Vector3f getEmissionDirection()
{
return emissionDirection;
}
public void setEmissionDirection(Vector3f emissionDirection)
{
this.emissionDirection = emissionDirection;
this.worldEmit.set(emissionDirection);
}
public float getEndSize()
{
return endSize;
}
public void setEndSize(float size)
{
endSize = size >= 0.0f ? size : 0.0f;
}
public float getStartSize()
{
return startSize;
}
public void setStartSize(float size)
{
startSize = size >= 0.0f ? size : 0.0f;
}
/**
* Set the start color for particles. This is the base color of the quad.
*
* @param color
* The start color.
*/
public void setStartColor(ColorRGBA color)
{
this.startColor = color;
}
/**
* getStartColor
returns the starting color.
*
* @return ColorRGBA The begining color.
*/
public ColorRGBA getStartColor()
{
return startColor;
}
/**
* Set the end color for particles. The base color of the quad will linearly
* approach this color from the start color over the lifetime of the
* particle.
*
* @param color
* ColorRGBA The ending color.
*/
public void setEndColor(ColorRGBA color)
{
this.endColor = color;
}
/**
* getEndColor returns the ending color.
*
* @return The ending color
*/
public ColorRGBA getEndColor()
{
return endColor;
}
/**
* Set the start and end spinSpeed of particles managed by this manager.
* Setting it to 0 means no spin.
*
* @param speed
* float
*/
public void setParticleSpinSpeed(float speed)
{
startSpin = speed;
endSpin = speed;
}
public Vector3f getInvScale()
{
return invScale;
}
public void setInvScale(Vector3f invScale)
{
this.invScale = invScale;
}
public void updateInvScale()
{
invScale.set(worldScale);
invScale.set(1f / invScale.x, 1f / invScale.y, 1f / invScale.z);
}
/**
* Add an external influence to the particle controller for this mesh.
*
* @param influence
* ParticleInfluence
*/
public void addInfluence(ParticleInfluence influence)
{
controller.addInfluence(influence);
}
/**
* Remove an influence from the particle controller for this mesh.
*
* @param influence
* ParticleInfluence
* @return true if found and removed.
*/
public boolean removeInfluence(ParticleInfluence influence)
{
return controller.removeInfluence(influence);
}
/**
* Returns the list of influences acting on this particle controller.
*
* @return ArrayList
*/
public ArrayList getInfluences()
{
return controller.getInfluences();
}
public void clearInfluences()
{
controller.clearInfluences();
}
public void setParticleMass(float mass)
{
startMass = endMass = mass;
}
/**
* Set the minimum angle (in radians) that particles can be emitted away
* from the emission direction. Any angle less than 0 is trimmed to 0.
*
* @param f
* The new emission minimum angle.
*/
public void setMinimumAngle(float f)
{
minimumAngle = f >= 0.0f ? f : 0.0f;
}
/**
* getEmissionMinimumAngle returns the minimum emission angle.
*
* @return The minimum emission angle.
*/
public float getMinimumAngle()
{
return minimumAngle;
}
/**
* Set the maximum angle (in radians) that particles can be emitted away
* from the emission direction. Any angle less than 0 is trimmed to 0.
*
* @param f
* The new emission maximum angle.
*/
public void setMaximumAngle(float f)
{
maximumAngle = f >= 0.0f ? f : 0.0f;
}
/**
* getEmissionMaximumAngle returns the maximum emission angle.
*
* @return The maximum emission angle.
*/
public float getMaximumAngle()
{
return maximumAngle;
}
public void setMaximumSize(float f)
{
maximumSize = f >= 0.0f ? f : 0.0f;
}
public float getMaximumSize()
{
return maximumSize;
}
public void setMinimumSize(float f)
{
minimumSize = f >= 0.0f ? f : 0.0f;
}
public float getMinimumSize()
{
return minimumSize;
}
/**
* Set the minimum lifespan of new particles (or recreated) managed by this
* manager. if a value less than zero is given, 1.0f is used.
*
* @param lifeSpan
* in ms
*/
public void setMinimumLifeTime(float lifeSpan)
{
minimumLifeTime = lifeSpan >= 0.0f ? lifeSpan : 0; // 1.0f;
}
/**
* getParticlesMinimumLifeTime returns the minimum life time of a particle.
*
* @return The current minimum life time in ms.
*/
public float getMinimumLifeTime()
{
return minimumLifeTime;
}
/**
* Set the maximum lifespan of new particles (or recreated) managed by this
* manager. if a value less than zero is given, 1.0f is used.
*
* @param lifeSpan
* in ms
*/
public void setMaximumLifeTime(float lifeSpan)
{
maximumLifeTime = lifeSpan >= 0.0f ? lifeSpan : 1.0f;
}
/**
* getParticlesMaximumLifeTime returns the maximum life time of a particle.
*
* @return The current maximum life time in ms.
*/
public float getMaximumLifeTime()
{
return maximumLifeTime;
}
public Matrix3f getRotMatrix()
{
return rotMatrix;
}
public void setRotMatrix(Matrix3f rotMatrix)
{
this.rotMatrix = rotMatrix;
}
public TransformMatrix getEmitterTransform()
{
return emitterTransform;
}
public void setEmitterTransform(TransformMatrix emitterTransform)
{
this.emitterTransform = emitterTransform;
}
public float getParticleOrientation()
{
return particleOrientation;
}
public void setParticleOrientation(float orient)
{
this.particleOrientation = orient;
}
/**
* Set the acceleration for any new particles created (or recreated) by this
* manager.
*
* @param velocity
* particle v0
*/
public void setInitialVelocity(float velocity)
{
this.initialVelocity = velocity;
}
/**
* Get the acceleration set in this manager.
*
* @return The initialVelocity
*/
public float getInitialVelocity()
{
return initialVelocity;
}
/**
* Set the offset for any new particles created (or recreated) by this
* manager. This is applicable only to managers generating from a point (not
* a line, rectangle, etc..)
*
* @param offset
* new offset position
*/
public void setOriginOffset(Vector3f offset)
{
originOffset.set(offset);
}
/**
* Get the offset point set in this manager.
*
* @return origin
*/
public Vector3f getOriginOffset()
{
return originOffset;
}
public Vector3f getWorldEmit()
{
return worldEmit;
}
public void setWorldEmit(Vector3f worldEmit)
{
this.worldEmit = worldEmit;
}
/**
* Get the number of particles the manager should release per second.
*
* @return The number of particles that should be released per second.
*/
public int getReleaseRate()
{
return releaseRate;
}
/**
* Set the number of particles the manager should release per second.
*
* @param particlesPerSecond
* number of particles per second
*/
public void setReleaseRate(int particlesPerSecond)
{
int oldRate = releaseRate;
this.releaseRate = particlesPerSecond;
if (controller != null && !controller.isActive() && controller.isControlFlow() && oldRate == 0)
{
controller.setActive(true);
}
}
public float getEndMass()
{
return endMass;
}
public void setEndMass(float endMass)
{
this.endMass = endMass;
}
public float getEndSpin()
{
return endSpin;
}
public void setEndSpin(float endSpin)
{
this.endSpin = endSpin;
}
public float getStartMass()
{
return startMass;
}
public void setStartMass(float startMass)
{
this.startMass = startMass;
}
public float getStartSpin()
{
return startSpin;
}
public void setStartSpin(float startSpin)
{
this.startSpin = startSpin;
}
public int getTexQuantity()
{
return texQuantity;
}
public void setTexQuantity(int quantity)
{
this.texQuantity = quantity;
}
public int getStartTexIndex()
{
return startTexIndex;
}
public void setStartTexIndex(int startTexIndex)
{
this.startTexIndex = startTexIndex;
}
/**
* Get which emittype method is being used by the underlying system. One of
* EmitType.Point, EmitType.Line, EmitType.Rectangle, EmitType.Ring,
* EmitType.GeomMesh
*
* @return An int representing the current geometry method being used.
*/
public EmitType getEmitType()
{
return emitType;
}
/**
* Set which emittype method is being used by the underlying system. This is
* already done by setGeometry(Line) and setGeometry(Rectangle) You should
* not need to use this method unless you are switching between geometry
* already set by those methods.
*
* @param type
* emit type to use
*/
public void setEmitType(EmitType type)
{
emitType = type;
}
/**
* Get which emittype method is being used by the underlying system. One of
* ParticleType.Quad, ParticleType.Triangle, ParticleType.Point,
* ParticleType.Line, ParticleType.GeomMesh
*
* @return An int representing the type of particle we are emitting.
*/
public com.jmex.effects.particles.ParticleSystem.ParticleType getParticleType()
{
return particleType;
}
/**
* Set what type of particle to emit from this sytem. Does not have an
* effect unless recreate is called.
*
* @param type
* particle type to use, should be one of ParticleType.Quad,
* ParticleType.Triangle, ParticleType.Point, ParticleType.Line,
* ParticleType.GeomMesh
*/
public void setParticleType(com.jmex.effects.particles.ParticleSystem.ParticleType type)
{
particleType = type;
}
/**
* Set a line segment to be used as the "emittor".
*
* @param line
* New emittor line segment.
*/
public void setGeometry(Line line)
{
psLine = line;
emitType = EmitType.Line;
}
/**
* Set a rectangular patch to be used as the "emittor".
*
* @param rect
* New rectangular patch.
*/
public void setGeometry(Rectangle rect)
{
psRect = rect;
emitType = EmitType.Rectangle;
}
/**
* Set a ring or disk to be used as the "emittor".
*
* @param ring
* The new ring area.
*/
public void setGeometry(Ring ring)
{
psRing = ring;
emitType = EmitType.Ring;
}
/**
* Set a Geometry's verts to be the random emission points
*
* @param geom
* The new geometry random verts.
*/
public void setGeometry(Geometry geom)
{
psGeom = geom;
emitType = EmitType.Geometry;
}
/**
* getLine returns the currently set line segment.
*
* @return current line segment.
*/
public Line getLine()
{
return psLine;
}
/**
* getRectangle returns the currently set rectangle segment.
*
* @return current rectangle segment.
*/
public Rectangle getRectangle()
{
return psRect;
}
/**
* getRing returns the currently set ring emission area.
*
* @return current ring.
*/
public Ring getRing()
{
return psRing;
}
/**
* getGeometry returns the currently set Geometry emitter.
*
* @return current Geometry emitter.
*/
public Geometry getGeometry()
{
return psGeom;
}
public void initAllParticlesLocation()
{
for (int i = particles.length; --i >= 0;)
{
initParticleLocation(i);
particles[i].updateVerts(null);
}
}
public void initParticleLocation(int index)
{
Particle p = particles[index];
if (particleType == com.jmex.effects.particles.ParticleSystem.ParticleType.GeomMesh)
{
// Update the triangle model on each new particle creation.
Vector3f[] vertices = new Vector3f[3];
((TriMesh) psGeom).getTriangle(index, vertices);
Triangle t = p.getTriangleModel();
if (t == null)
{
t = new Triangle(vertices[0], vertices[1], vertices[2]);
} else
{
for (int x = 0; x < 3; x++)
{
t.set(x, vertices[x]);
}
}
t.calculateCenter();
t.calculateNormal();
// turn the triangle corners into vector offsets from center
for (int x = 0; x < 3; x++)
{
vertices[x].subtract(t.getCenter(), vertices[x]);
t.set(x, vertices[x]);
}
p.setTriangleModel(t);
// psGeom.localToWorld(t.getCenter(), p.getPosition());
p.getPosition().multLocal(getInvScale());
} else if (getEmitType() == EmitType.Geometry)
{
if (getGeometry() != null && getGeometry() instanceof TriMesh)
{
((TriMesh) getGeometry()).randomPointOnTriangles(p.getPosition(), workVect3);
} else if (getGeometry() != null)
{
getGeometry().randomVertex(p.getPosition());
}
p.getPosition().multLocal(getInvScale());
} else
{
switch (getEmitType())
{
case Line:
getLine().random(p.getPosition());
break;
case Rectangle:
getRectangle().random(p.getPosition());
break;
case Ring:
getRing().random(p.getPosition());
break;
case Point:
default:
p.getPosition().set(originOffset);
break;
}
emitterTransform.multPoint(p.getPosition());
}
}
public boolean isCameraFacing()
{
return cameraFacing;
}
public void setCameraFacing(boolean cameraFacing)
{
this.cameraFacing = cameraFacing;
}
public boolean isVelocityAligned()
{
return velocityAligned;
}
public void setVelocityAligned(boolean velocityAligned)
{
this.velocityAligned = velocityAligned;
}
public Particle getParticle(int i)
{
return particles[i];
}
public boolean isActive()
{
return controller.isActive();
}
public void setSpeed(float f)
{
controller.setTimeStep(f);
}
public void setRepeatType(int type)
{
controller.setRepeatType(type);
}
public void setControlFlow(boolean b)
{
controller.setControlFlow(b);
}
public Vector3f getOriginCenter()
{
return originCenter;
}
public Vector3f getUpVector()
{
return upVector;
}
public void setUpVector(Vector3f upVector)
{
this.upVector = upVector;
}
public Vector3f getLeftVector()
{
return leftVector;
}
public void setLeftVector(Vector3f leftVector)
{
this.leftVector = leftVector;
}
public boolean isRotateWithScene()
{
return rotateWithScene;
}
public void setRotateWithScene(boolean rotate)
{
this.rotateWithScene = rotate;
}
public void resetParticleVelocity(int i)
{
getRandomVelocity(particles[i].getVelocity());
}
/**
* Returns a random angle between the min and max angles.
*
* @return the random angle.
*/
public float getRandomAngle()
{
return getMinimumAngle() + FastMath.nextRandomFloat() * (getMaximumAngle() - getMinimumAngle());
}
/**
* generate a random lifespan between the min and max lifespan of the
* particle system.
*
* @return the generated lifespan value
*/
public float getRandomLifeSpan()
{
return getMinimumLifeTime() + ((getMaximumLifeTime() - getMinimumLifeTime()) * FastMath.nextRandomFloat());
}
public float getRandomSize()
{
return getMinimumSize() + ((getMaximumSize() - getMinimumSize()) * FastMath.nextRandomFloat());
}
/**
* Generate a random velocity within the parameters of max angle and the
* rotation matrix.
*
* @param pSpeed
* a vector to store the results in.
*/
protected Vector3f getRandomVelocity(Vector3f pSpeed)
{
float randDir = FastMath.TWO_PI * FastMath.nextRandomFloat();
float randAngle = getRandomAngle();
if (pSpeed == null)
{
pSpeed = new Vector3f();
}
pSpeed.x = FastMath.cos(randDir) * FastMath.sin(randAngle);
pSpeed.y = FastMath.cos(randAngle);
pSpeed.z = FastMath.sin(randDir) * FastMath.sin(randAngle);
rotateVectorSpeed(pSpeed);
pSpeed.normalize();
float factor = getInitialVelocity();
if (!speedup) // uniform speed
factor *= 1 + FastMath.nextRandomFloat();
pSpeed.multLocal(factor);
return pSpeed;
}
/**
* Apply the rotation matrix to a given vector representing a particle
* velocity.
*
* @param pSpeed
* the velocity vector to be modified.
*/
protected void rotateVectorSpeed(Vector3f pSpeed)
{
float x = pSpeed.x, y = pSpeed.y, z = pSpeed.z;
pSpeed.x = -1 * ((rotMatrix.m00 * x) + (rotMatrix.m10 * y) + (rotMatrix.m20 * z));
pSpeed.y = (getRotMatrix().m01 * x) + (rotMatrix.m11 * y) + (rotMatrix.m21 * z);
pSpeed.z = -1 * ((rotMatrix.m02 * x) + (rotMatrix.m12 * y) + (rotMatrix.m22 * z));
}
public void warmUp(int iterations)
{
if (controller != null)
{
controller.warmUp(iterations);
}
}
public int getNumParticles()
{
return numParticles;
}
public void setNumParticles(int numParticles)
{
this.numParticles = numParticles;
}
public float getReleaseVariance()
{
if (controller != null)
{
return controller.getReleaseVariance();
}
return 0;
}
public void setReleaseVariance(float var)
{
if (controller != null)
{
controller.setReleaseVariance(var);
}
}
public ParticleAppearanceRamp getRamp()
{
return ramp;
}
public void setRamp(ParticleAppearanceRamp ramp)
{
if (ramp == null)
{
logger.warning("Can not set a null ParticleAppearanceRamp.");
return;
}
this.ramp = ramp;
}
// public TexAnimation getTexAnimation()
// {
// return texAnimation;
// }
//
// public void setTexAnimation(TexAnimation texAnimation)
// {
// if (texAnimation == null)
// {
// logger.warning("Can not set a null TexAnimation.");
// return;
// }
// this.texAnimation = texAnimation;
// }
/**
* @return true if the particles are already in world coordinate space
* (default). When true, scene-graph transforms will only affect the
* emission of particles, not particles that are already living.
*/
public boolean isParticlesInWorldCoords()
{
return particlesInWorldCoords;
}
public void setParticlesInWorldCoords(boolean particlesInWorldCoords)
{
this.particlesInWorldCoords = particlesInWorldCoords;
}
/**
* Changes the number of particles in this particle mesh.
*
* @param count
* the desired number of particles to change to.
*/
// public void recreate(int count)
// {
// numParticles = count;
// initializeParticles(numParticles);
// }
// @Override
public void updateWorldBound()
{
; // ignore this since we want it to happen only when we say it can
// happen due to world vectors not being used
}
public void updateWorldBoundManually()
{
//super.updateWorldBound();
}
public void updateGeometricState(float time, boolean initiator)
{
assert(false);
/*
super.updateGeometricState(time, initiator);
if (isRotateWithScene())
{
if (emitType == EmitType.Geometry && getGeometry() != null)
{
getGeometry().getWorldRotation().mult(emissionDirection,
worldEmit);
} else
{
worldRotation.mult(emissionDirection, worldEmit);
}
} else
{
worldEmit.set(emissionDirection);
}
if (particlesInWorldCoords)
{
emitterTransform.set(worldRotation, worldTranslation.divide(worldScale));
originCenter.set(worldTranslation).addLocal(originOffset);
getWorldTranslation().set(0, 0, 0);
getWorldRotation().set(0, 0, 0, 1);
} else
{
originCenter.set(originOffset);
}
*/
}
public void write(JMEExporter e) throws IOException
{
assert(false);
/*
detachAllChildren();
super.write(e);
OutputCapsule capsule = e.getCapsule(this);
capsule.write(emitType, "emitType", EmitType.Point);
capsule.write(particleType, "particleType", ParticleType.Quad);
capsule.write(psLine, "psLine", null);
capsule.write(psRect, "psRect", null);
capsule.write(psRing, "psRing", null);
capsule.write(psGeom, "psGeom", null);
capsule.write(startSize, "startSize", DEFAULT_START_SIZE);
capsule.write(endSize, "endSize", DEFAULT_END_SIZE);
capsule.write(startColor, "startColor", DEFAULT_START_COLOR);
capsule.write(endColor, "endColor", DEFAULT_END_COLOR);
capsule.write(startSpin, "startSpin", 0);
capsule.write(endSpin, "endSpin", 0);
capsule.write(startMass, "startMass", 0);
capsule.write(endMass, "endMass", 0);
capsule.write(startTexIndex, "startTexIndex", 0);
capsule.write(texQuantity, "texQuantity", 1);
capsule.write(initialVelocity, "initialVelocity", 1);
capsule.write(minimumLifeTime, "minimumLifeTime", DEFAULT_MIN_LIFE);
capsule.write(maximumLifeTime, "maximumLifeTime", DEFAULT_MAX_LIFE);
capsule.write(minimumAngle, "minimumAngle", 0);
capsule.write(maximumAngle, "maximumAngle", DEFAULT_MAX_ANGLE);
capsule.write(emissionDirection, "emissionDirection", Vector3f.UNIT_Y);
capsule.write(worldEmit, "worldEmit", Vector3f.ZERO);
capsule.write(upVector, "upVector", Vector3f.UNIT_Y);
capsule.write(leftVector, "leftVector", new Vector3f(-1, 0, 0));
capsule.write(numParticles, "numParticles", 0);
capsule.write(particleOrientation, "particleOrientation", 0);
capsule.write(rotateWithScene, "rotateWithScene", false);
capsule.write(geometryCoordinates, "geometryCoordinates", null);
capsule.write(appearanceColors, "appearanceColors", null);
capsule.write(releaseRate, "releaseRate", numParticles);
capsule.write(originCenter, "originCenter", Vector3f.ZERO);
capsule.write(originOffset, "originOffset", Vector3f.ZERO);
capsule.write(controller, "controller", null);
capsule.write(cameraFacing, "cameraFacing", true);
capsule.write(velocityAligned, "velocityAligned", false);
capsule.write(particlesInWorldCoords, "particlesInWorldCoords", true);
capsule.write(ramp, "ramp", new ParticleAppearanceRamp());
capsule.write(texAnimation, "texAnimation", new TexAnimation());
*/
}
public void read(JMEImporter e) throws IOException
{
assert(false);
/*
detachAllChildren();
super.read(e);
InputCapsule capsule = e.getCapsule(this);
emitType = capsule.readEnum("emitType", EmitType.class, EmitType.Point);
particleType = capsule.readEnum("particleType", ParticleType.class,
ParticleType.Quad);
psLine = (Line) capsule.readSavable("psLine", null);
psRect = (Rectangle) capsule.readSavable("psRect", null);
psRing = (Ring) capsule.readSavable("psRing", null);
psGeom = (Geometry) capsule.readSavable("psGeom", null);
startSize = capsule.readFloat("startSize", DEFAULT_START_SIZE);
endSize = capsule.readFloat("endSize", DEFAULT_END_SIZE);
startColor = (ColorRGBA) capsule.readSavable("startColor",
DEFAULT_START_COLOR.clone());
endColor = (ColorRGBA) capsule.readSavable("endColor",
DEFAULT_END_COLOR.clone());
startSpin = capsule.readFloat("startSpin", 0);
endSpin = capsule.readFloat("endSpin", 0);
startMass = capsule.readFloat("startMass", 0);
endMass = capsule.readFloat("endMass", 0);
startTexIndex = capsule.readInt("startTexIndex", 0);
texQuantity = capsule.readInt("texQuantity", 1);
initialVelocity = capsule.readFloat("initialVelocity", 1);
minimumLifeTime = capsule.readFloat("minimumLifeTime", DEFAULT_MIN_LIFE);
maximumLifeTime = capsule.readFloat("maximumLifeTime", DEFAULT_MAX_LIFE);
minimumAngle = capsule.readFloat("minimumAngle", 0);
maximumAngle = capsule.readFloat("maximumAngle", DEFAULT_MAX_ANGLE);
emissionDirection = (Vector3f) capsule.readSavable("emissionDirection",
Vector3f.UNIT_Y.clone());
worldEmit = (Vector3f) capsule.readSavable("worldEmit", Vector3f.ZERO.clone());
upVector = (Vector3f) capsule.readSavable("upVector", Vector3f.UNIT_Y.clone());
leftVector = (Vector3f) capsule.readSavable("leftVector", new Vector3f(
-1, 0, 0));
numParticles = capsule.readInt("numParticles", 0);
rotateWithScene = capsule.readBoolean("rotateWithScene", false);
geometryCoordinates = capsule.readFloatBuffer("geometryCoordinates",
null);
appearanceColors = capsule.readFloatBuffer("appearanceColors", null);
releaseRate = capsule.readInt("releaseRate", numParticles);
particleOrientation = capsule.readFloat("particleOrientation", 0);
originCenter = (Vector3f) capsule.readSavable("originCenter",
new Vector3f());
originOffset = (Vector3f) capsule.readSavable("originOffset",
new Vector3f());
controller = (ParticleController) capsule.readSavable("controller",
null);
cameraFacing = capsule.readBoolean("cameraFacing", true);
velocityAligned = capsule.readBoolean("velocityAligned", false);
particlesInWorldCoords = capsule.readBoolean("particlesInWorldCoords", true);
ramp = (ParticleAppearanceRamp) capsule.readSavable("ramp",
new ParticleAppearanceRamp());
texAnimation = (TexAnimation) capsule.readSavable("texAnimation",
new TexAnimation());
invScale = new Vector3f();
upXemit = new Vector3f();
absUpVector = new Vector3f();
abUpMinUp = new Vector3f();
rotMatrix = new Matrix3f();
initializeParticles(numParticles);
*/
}
void createEditWindow(GroupEditor callee, boolean newWindow)
{
//editWindow = (new SphereEditor(this, deepCopy(), callee)).GetEditor();
if (newWindow)
{
objectUI = new ParticleEditor(this, deepCopy(), callee);
} else
{
objectUI = new ParticleEditor(this, callee);
}
editWindow = objectUI.GetEditor();
}
}