All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.animation.EffectTrack Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2012 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.jme3.animation;

import com.jme3.effect.ParticleEmitter;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.Spatial.CullHint;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * EffectTrack is a track to add to an existing animation, to emmit particles
 * during animations for example : exhausts, dust raised by foot steps, shock
 * waves, lightnings etc...
 *
 * usage is
 * 
 * AnimControl control model.getControl(AnimControl.class);
 * EffectTrack track = new EffectTrack(existingEmmitter, control.getAnim("TheAnim").getLength());
 * control.getAnim("TheAnim").addTrack(track);
 * 
* * if the emitter has emmits 0 particles per seconds emmitAllPArticles will be * called on it at time 0 + startOffset. if it he it has more it will start * emmit normally at time 0 + startOffset. * * * @author Nehon */ public class EffectTrack implements ClonableTrack { private static final Logger logger = Logger.getLogger(EffectTrack.class.getName()); private ParticleEmitter emitter; private float startOffset = 0; private float particlesPerSeconds = 0; private float length = 0; private boolean emitted = false; private boolean initialized = false; //control responsible for disable and cull the emitter once all particles are gone private KillParticleControl killParticles = new KillParticleControl(); public static class KillParticleControl extends AbstractControl { ParticleEmitter emitter; boolean stopRequested = false; boolean remove = false; public KillParticleControl() { } @Override public void setSpatial(Spatial spatial) { super.setSpatial(spatial); if (spatial != null) { if (spatial instanceof ParticleEmitter) { emitter = (ParticleEmitter) spatial; } else { throw new IllegalArgumentException("KillParticleEmitter can only ba attached to ParticleEmitter"); } } } @Override protected void controlUpdate(float tpf) { if (remove) { emitter.removeControl(this); return; } if (emitter.getNumVisibleParticles() == 0) { emitter.setCullHint(CullHint.Always); emitter.setEnabled(false); emitter.removeControl(this); stopRequested = false; } } @Override public Object jmeClone() { KillParticleControl c = new KillParticleControl(); //this control should be removed as it shouldn't have been persisted in the first place //In the quest to find the less hackish solution to achieve this, //making it remove itself from the spatial in the first update loop when loaded was the less bad. c.remove = true; c.spatial = spatial; return c; } @Override protected void controlRender(RenderManager rm, ViewPort vp) { } @Override public Control cloneForSpatial(Spatial spatial) { KillParticleControl c = new KillParticleControl(); //this control should be removed as it shouldn't have been persisted in the first place //In the quest to find the less hackish solution to achieve this, //making it remove itself from the spatial in the first update loop when loaded was the less bad. c.remove = true; c.setSpatial(spatial); return c; } }; //Anim listener that stops the Emmitter when the animation is finished or changed. private class OnEndListener implements AnimEventListener { public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) { stop(); } public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { } } /** * default constructor only for serialization */ public EffectTrack() { } /** * Creates and EffectTrack * * @param emitter the emmitter of the track * @param length the length of the track (usually the length of the * animation you want to add the track to) */ public EffectTrack(ParticleEmitter emitter, float length) { this.emitter = emitter; //saving particles per second value this.particlesPerSeconds = emitter.getParticlesPerSec(); //setting the emmitter to not emmit. this.emitter.setParticlesPerSec(0); this.length = length; //Marking the emitter with a reference to this track for further use in deserialization. setUserData(this); } /** * Creates and EffectTrack * * @param emitter the emmitter of the track * @param length the length of the track (usually the length of the * animation you want to add the track to) * @param startOffset the time in second when the emitter will be triggerd * after the animation starts (default is 0) */ public EffectTrack(ParticleEmitter emitter, float length, float startOffset) { this(emitter, length); this.startOffset = startOffset; } /** * Internal use only * * @see Track#setTime(float, float, com.jme3.animation.AnimControl, * com.jme3.animation.AnimChannel, com.jme3.util.TempVars) */ public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { if (time >= length) { return; } //first time adding the Animation listener to stop the track at the end of the animation if (!initialized) { control.addListener(new OnEndListener()); initialized = true; } //checking fo time to trigger the effect if (!emitted && time >= startOffset) { emitted = true; emitter.setCullHint(CullHint.Dynamic); emitter.setEnabled(true); //if the emitter has 0 particles per seconds emmit all particles in one shot if (particlesPerSeconds == 0) { emitter.emitAllParticles(); if (!killParticles.stopRequested) { emitter.addControl(killParticles); killParticles.stopRequested = true; } } else { //else reset its former particlePerSec value to let it emmit. emitter.setParticlesPerSec(particlesPerSeconds); } } } //stops the emmiter to emit. private void stop() { emitter.setParticlesPerSec(0); emitted = false; if (!killParticles.stopRequested) { emitter.addControl(killParticles); killParticles.stopRequested = true; } } /** * Retruns the length of the track * * @return length of the track */ public float getLength() { return length; } @Override public float[] getKeyFrameTimes() { return new float[] { startOffset }; } /** * Clone this track * * @return */ @Override public Track clone() { return new EffectTrack(emitter, length, startOffset); } /** * This method clone the Track and search for the cloned counterpart of the * original emmitter in the given cloned spatial. The spatial is assumed to * be the Spatial holding the AnimControl controling the animation using * this Track. * * @param spatial the Spatial holding the AnimControl * @return the cloned Track with proper reference */ @Override public Track cloneForSpatial(Spatial spatial) { EffectTrack effectTrack = new EffectTrack(); effectTrack.particlesPerSeconds = this.particlesPerSeconds; effectTrack.length = this.length; effectTrack.startOffset = this.startOffset; //searching for the newly cloned ParticleEmitter effectTrack.emitter = findEmitter(spatial); if (effectTrack.emitter == null) { logger.log(Level.WARNING, "{0} was not found in {1} or is not bound to this track", new Object[]{emitter.getName(), spatial.getName()}); effectTrack.emitter = emitter; } removeUserData(this); //setting user data on the new emmitter and marking it with a reference to the cloned Track. setUserData(effectTrack); effectTrack.emitter.setParticlesPerSec(0); return effectTrack; } @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 ) { this.emitter = cloner.clone(emitter); } /** * recursive function responsible for finding the newly cloned Emitter * * @param spat * @return */ private ParticleEmitter findEmitter(Spatial spat) { if (spat instanceof ParticleEmitter) { //spat is a PArticleEmitter ParticleEmitter em = (ParticleEmitter) spat; //getting the UserData TrackInfo so check if it should be attached to this Track TrackInfo t = (TrackInfo) em.getUserData("TrackInfo"); if (t != null && t.getTracks().contains(this)) { return em; } return null; } else if (spat instanceof Node) { for (Spatial child : ((Node) spat).getChildren()) { ParticleEmitter em = findEmitter(child); if (em != null) { return em; } } } return null; } public void cleanUp() { TrackInfo t = (TrackInfo) emitter.getUserData("TrackInfo"); t.getTracks().remove(this); if (t.getTracks().isEmpty()) { emitter.setUserData("TrackInfo", null); } } /** * * @return the emitter used by this track */ public ParticleEmitter getEmitter() { return emitter; } /** * Sets the Emitter to use in this track * * @param emitter */ public void setEmitter(ParticleEmitter emitter) { if (this.emitter != null) { TrackInfo data = (TrackInfo) emitter.getUserData("TrackInfo"); data.getTracks().remove(this); } this.emitter = emitter; //saving particles per second value this.particlesPerSeconds = emitter.getParticlesPerSec(); //setting the emmitter to not emmit. this.emitter.setParticlesPerSec(0); setUserData(this); } /** * * @return the start offset of the track */ public float getStartOffset() { return startOffset; } /** * set the start offset of the track * * @param startOffset */ public void setStartOffset(float startOffset) { this.startOffset = startOffset; } private void setUserData(EffectTrack effectTrack) { //fetching the UserData TrackInfo. TrackInfo data = (TrackInfo) effectTrack.emitter.getUserData("TrackInfo"); //if it does not exist, we create it and attach it to the emitter. if (data == null) { data = new TrackInfo(); effectTrack.emitter.setUserData("TrackInfo", data); } //adding the given Track to the TrackInfo. data.addTrack(effectTrack); } private void removeUserData(EffectTrack effectTrack) { //fetching the UserData TrackInfo. TrackInfo data = (TrackInfo) effectTrack.emitter.getUserData("TrackInfo"); //if it does not exist, we create it and attach it to the emitter. if (data == null) { return; } //removing the given Track to the TrackInfo. data.getTracks().remove(effectTrack); } /** * Internal use only serialization * * @param ex exporter * @throws IOException exception */ public void write(JmeExporter ex) throws IOException { OutputCapsule out = ex.getCapsule(this); //reseting the particle emission rate on the emitter before saving. emitter.setParticlesPerSec(particlesPerSeconds); out.write(emitter, "emitter", null); out.write(particlesPerSeconds, "particlesPerSeconds", 0); out.write(length, "length", 0); out.write(startOffset, "startOffset", 0); //Setting emission rate to 0 so that this track can go on being used. emitter.setParticlesPerSec(0); } /** * Internal use only serialization * * @param im importer * @throws IOException Exception */ public void read(JmeImporter im) throws IOException { InputCapsule in = im.getCapsule(this); this.particlesPerSeconds = in.readFloat("particlesPerSeconds", 0); //reading the emitter even if the track will then reference its cloned counter part if it's loaded with the assetManager. //This also avoid null pointer exception if the model is not loaded via the AssetManager. emitter = (ParticleEmitter) in.readSavable("emitter", null); emitter.setParticlesPerSec(0); //if the emitter was saved with a KillParticleControl we remove it. // Control c = emitter.getControl(KillParticleControl.class); // if(c!=null){ // emitter.removeControl(c); // } //emitter.removeControl(KillParticleControl.class); length = in.readFloat("length", length); startOffset = in.readFloat("startOffset", 0); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy