/* * 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(); } }