@ -38,12 +38,15 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule ;
import com.jme3.export.OutputCapsule ;
import com.jme3.renderer.RenderManager ;
import com.jme3.renderer.RenderManager ;
import com.jme3.renderer.ViewPort ;
import com.jme3.renderer.ViewPort ;
import com.jme3.scene.Node ;
import com.jme3.scene.Spatial ;
import com.jme3.scene.Spatial ;
import com.jme3.scene.Spatial.CullHint ;
import com.jme3.scene.Spatial.CullHint ;
import com.jme3.scene.control.AbstractControl ;
import com.jme3.scene.control.AbstractControl ;
import com.jme3.scene.control.Control ;
import com.jme3.scene.control.Control ;
import com.jme3.util.TempVars ;
import com.jme3.util.TempVars ;
import java.io.IOException ;
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
* EffectTrack is a track to add to an existing animation , to emmit particles during animations
@ -62,8 +65,9 @@ import java.io.IOException;
*
*
* @author Nehon
* @author Nehon
* /
* /
public class EffectTrack implements Track {
public class EffectTrack implements ClonableTrack {
private static final Logger logger = Logger . getLogger ( EffectTrack . class . getName ( ) ) ;
private ParticleEmitter emitter ;
private ParticleEmitter emitter ;
private float startOffset = 0 ;
private float startOffset = 0 ;
private float particlesPerSeconds = 0 ;
private float particlesPerSeconds = 0 ;
@ -71,10 +75,9 @@ public class EffectTrack implements Track {
private boolean emitted = false ;
private boolean emitted = false ;
private boolean initialized = false ;
private boolean initialized = false ;
private boolean stopRequested = false ;
private boolean stopRequested = false ;
//control responsible for disable and cull the emitter once all particles are gone
//control responsible for disable and cull the emitter once all particles are gone
private AbstractControl killParticles = new AbstractControl ( ) {
private AbstractControl killParticles = new AbstractControl ( ) {
@Override
@Override
protected void controlUpdate ( float tpf ) {
protected void controlUpdate ( float tpf ) {
if ( emitter . getNumVisibleParticles ( ) = = 0 ) {
if ( emitter . getNumVisibleParticles ( ) = = 0 ) {
@ -84,29 +87,26 @@ public class EffectTrack implements Track {
stopRequested = false ;
stopRequested = false ;
}
}
}
}
@Override
@Override
protected void controlRender ( RenderManager rm , ViewPort vp ) {
protected void controlRender ( RenderManager rm , ViewPort vp ) {
}
}
public Control cloneForSpatial ( Spatial spatial ) {
public Control cloneForSpatial ( Spatial spatial ) {
return null ;
return null ;
}
}
} ;
} ;
//Anim listener that stops the Emmitter when the animation is finished or changed.
//Anim listener that stops the Emmitter when the animation is finished or changed.
private class OnEndListener implements AnimEventListener {
private class OnEndListener implements AnimEventListener {
public void onAnimCycleDone ( AnimControl control , AnimChannel channel , String animName ) {
public void onAnimCycleDone ( AnimControl control , AnimChannel channel , String animName ) {
if ( ! stopRequested ) {
stop ( ) ;
stop ( ) ;
}
}
}
public void onAnimChange ( AnimControl control , AnimChannel channel , String animName ) {
public void onAnimChange ( AnimControl control , AnimChannel channel , String animName ) {
if ( ! stopRequested ) {
stop ( ) ;
stop ( ) ;
}
}
}
}
}
@ -128,7 +128,9 @@ public class EffectTrack implements Track {
//setting the emmitter to not emmit.
//setting the emmitter to not emmit.
this . emitter . setParticlesPerSec ( 0 ) ;
this . emitter . setParticlesPerSec ( 0 ) ;
this . length = length ;
this . length = length ;
//Marking the emitter with a reference to this track for further use in deserialization.
setUserData ( this ) ;
}
}
/ * *
/ * *
@ -156,14 +158,15 @@ public class EffectTrack implements Track {
//checking fo time to trigger the effect
//checking fo time to trigger the effect
if ( ! emitted & & time > = startOffset ) {
if ( ! emitted & & time > = startOffset ) {
emitted = true ;
emitted = true ;
stopRequested = false ;
emitter . setCullHint ( CullHint . Dynamic ) ;
emitter . setCullHint ( CullHint . Dynamic ) ;
emitter . setEnabled ( true ) ;
emitter . setEnabled ( true ) ;
//if the emitter has 0 particles per seconds emmit all particles in one shot
//if the emitter has 0 particles per seconds emmit all particles in one shot
if ( particlesPerSeconds = = 0 ) {
if ( particlesPerSeconds = = 0 ) {
emitter . emitAllParticles ( ) ;
emitter . emitAllParticles ( ) ;
emitter . addControl ( killParticles ) ;
if ( ! stopRequested ) {
stopRequested = true ;
emitter . addControl ( killParticles ) ;
stopRequested = true ;
}
} else {
} else {
//else reset its former particlePerSec value to let it emmit.
//else reset its former particlePerSec value to let it emmit.
emitter . setParticlesPerSec ( particlesPerSeconds ) ;
emitter . setParticlesPerSec ( particlesPerSeconds ) ;
@ -175,10 +178,15 @@ public class EffectTrack implements Track {
private void stop ( ) {
private void stop ( ) {
emitter . setParticlesPerSec ( 0 ) ;
emitter . setParticlesPerSec ( 0 ) ;
emitted = false ;
emitted = false ;
emitter . addControl ( killParticles ) ;
if ( ! stopRequested ) {
stopRequested = true ;
emitter . addControl ( killParticles ) ;
stopRequested = true ;
}
}
}
/ * *
/ * *
* Retruns the length of the track
* Retruns the length of the track
* @return length of the track
* @return length of the track
@ -194,7 +202,67 @@ public class EffectTrack implements Track {
@Override
@Override
public Track clone ( ) {
public Track clone ( ) {
return new EffectTrack ( emitter , length , startOffset ) ;
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
* /
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 ;
}
//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 ;
}
/ * *
* 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 ) ;
}
}
}
/ * *
/ * *
@ -210,7 +278,12 @@ public class EffectTrack implements Track {
* @param emitter
* @param emitter
* /
* /
public void setEmitter ( ParticleEmitter emitter ) {
public void setEmitter ( ParticleEmitter emitter ) {
if ( this . emitter ! = null ) {
TrackInfo data = ( TrackInfo ) emitter . getUserData ( "TrackInfo" ) ;
data . getTracks ( ) . remove ( this ) ;
}
this . emitter = emitter ;
this . emitter = emitter ;
setUserData ( this ) ;
}
}
/ * *
/ * *
@ -228,6 +301,22 @@ public class EffectTrack implements Track {
public void setStartOffset ( float startOffset ) {
public void setStartOffset ( float startOffset ) {
this . startOffset = 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 ) ;
}
/ * *
/ * *
* Internal use only serialization
* Internal use only serialization
@ -236,10 +325,16 @@ public class EffectTrack implements Track {
* /
* /
public void write ( JmeExporter ex ) throws IOException {
public void write ( JmeExporter ex ) throws IOException {
OutputCapsule out = ex . getCapsule ( this ) ;
OutputCapsule out = ex . getCapsule ( this ) ;
//reseting the particle emission rate on the emitter before saving.
emitter . setParticlesPerSec ( particlesPerSeconds ) ;
//removing eventual unpersisted control off the emitter
emitter . removeControl ( killParticles ) ;
out . write ( emitter , "emitter" , null ) ;
out . write ( emitter , "emitter" , null ) ;
out . write ( particlesPerSeconds , "particlesPerSeconds" , 0 ) ;
out . write ( length , "length" , 0 ) ;
out . write ( length , "length" , 0 ) ;
out . write ( startOffset , "startOffset" , 0 ) ;
out . write ( startOffset , "startOffset" , 0 ) ;
//Setting emission rate to 0 so that this track can go on being used.
emitter . setParticlesPerSec ( 0 ) ;
}
}
/ * *
/ * *
@ -249,8 +344,10 @@ public class EffectTrack implements Track {
* /
* /
public void read ( JmeImporter im ) throws IOException {
public void read ( JmeImporter im ) throws IOException {
InputCapsule in = im . getCapsule ( this ) ;
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 = ( ParticleEmitter ) in . readSavable ( "emitter" , null ) ;
this . particlesPerSeconds = emitter . getParticlesPerSec ( ) ;
emitter . setParticlesPerSec ( 0 ) ;
emitter . setParticlesPerSec ( 0 ) ;
length = in . readFloat ( "length" , length ) ;
length = in . readFloat ( "length" , length ) ;
startOffset = in . readFloat ( "startOffset" , 0 ) ;
startOffset = in . readFloat ( "startOffset" , 0 ) ;