Home | History | Annotate | Download | only in emitters
      1 package com.badlogic.gdx.graphics.g3d.particles.emitters;
      2 
      3 import com.badlogic.gdx.graphics.g3d.particles.ParallelArray.FloatChannel;
      4 import com.badlogic.gdx.graphics.g3d.particles.ParticleChannels;
      5 import com.badlogic.gdx.graphics.g3d.particles.ParticleControllerComponent;
      6 import com.badlogic.gdx.graphics.g3d.particles.values.RangedNumericValue;
      7 import com.badlogic.gdx.graphics.g3d.particles.values.ScaledNumericValue;
      8 import com.badlogic.gdx.utils.Json;
      9 import com.badlogic.gdx.utils.JsonValue;
     10 
     11 /** It's a generic use {@link Emitter} which fits most of the particles simulation scenarios.
     12  * @author Inferno */
     13 public class RegularEmitter extends Emitter implements Json.Serializable {
     14 
     15 	/** Possible emission modes. Emission mode does not affect already emitted particles. */
     16 	public enum EmissionMode {
     17 		/** New particles can be emitted. */
     18 		Enabled,
     19 		/** Only valid for continuous emitters.
     20 		 * It will only emit particles until the end of the effect duration.
     21 		 * After that emission cycle will not be restarted.*/
     22 		EnabledUntilCycleEnd,
     23 		/** Prevents new particle emission.*/
     24 		Disabled
     25 	}
     26 
     27 	public RangedNumericValue delayValue, durationValue;
     28 	public ScaledNumericValue 	lifeOffsetValue,
     29 								lifeValue,
     30 								emissionValue;
     31 	protected int emission, emissionDiff, emissionDelta;
     32 	protected int lifeOffset, lifeOffsetDiff;
     33 	protected int life, lifeDiff;
     34 	protected float duration, delay, durationTimer, delayTimer;
     35 	private boolean continuous;
     36 	private EmissionMode emissionMode;
     37 
     38 	private FloatChannel lifeChannel;
     39 
     40 	public RegularEmitter(){
     41 		delayValue = new RangedNumericValue();
     42 		durationValue = new RangedNumericValue();
     43 		lifeOffsetValue = new ScaledNumericValue();
     44 		lifeValue = new ScaledNumericValue();
     45 		emissionValue = new ScaledNumericValue();
     46 
     47 		durationValue.setActive(true);
     48 		emissionValue.setActive(true);
     49 		lifeValue.setActive(true);
     50 		continuous = true;
     51 		emissionMode = EmissionMode.Enabled;
     52 	}
     53 
     54 	public RegularEmitter (RegularEmitter regularEmitter) {
     55 		this();
     56 		set(regularEmitter);
     57 	}
     58 
     59 	@Override
     60 	public void allocateChannels() {
     61 		lifeChannel = controller.particles.addChannel(ParticleChannels.Life);
     62 	}
     63 
     64 	@Override
     65 	public void start () {
     66 		delay = delayValue.active ? delayValue.newLowValue() : 0;
     67 		delayTimer = 0;
     68 		durationTimer = 0f;
     69 
     70 		duration = durationValue.newLowValue();
     71 		percent = durationTimer / (float)duration;
     72 
     73 		emission = (int)emissionValue.newLowValue();
     74 		emissionDiff = (int)emissionValue.newHighValue();
     75 		if (!emissionValue.isRelative())
     76 			emissionDiff -= emission;
     77 
     78 		life = (int)lifeValue.newLowValue();
     79 		lifeDiff = (int)lifeValue.newHighValue();
     80 		if (!lifeValue.isRelative())
     81 			lifeDiff -= life;
     82 
     83 		lifeOffset = lifeOffsetValue.active ? (int)lifeOffsetValue.newLowValue() : 0;
     84 		lifeOffsetDiff = (int)lifeOffsetValue.newHighValue();
     85 		if (!lifeOffsetValue.isRelative())
     86 			lifeOffsetDiff -= lifeOffset;
     87 	}
     88 
     89 	public void init(){
     90 		super.init();
     91 		emissionDelta = 0;
     92 		durationTimer = duration;
     93 	}
     94 
     95 	public void activateParticles (int startIndex, int count){
     96 		int 	currentTotaLife = life + (int)(lifeDiff * lifeValue.getScale(percent)),
     97 				currentLife = currentTotaLife;
     98 		int offsetTime = (int)(lifeOffset + lifeOffsetDiff * lifeOffsetValue.getScale(percent));
     99 		if (offsetTime > 0) {
    100 			if (offsetTime >= currentLife)
    101 				offsetTime = currentLife - 1;
    102 			currentLife -= offsetTime;
    103 		}
    104 		float lifePercent = 1 - currentLife / (float)currentTotaLife;
    105 
    106 		for(int i=startIndex*lifeChannel.strideSize, c = i +count*lifeChannel.strideSize; i < c;  i+=lifeChannel.strideSize){
    107 			lifeChannel.data[i+ParticleChannels.CurrentLifeOffset] = currentLife;
    108 			lifeChannel.data[i+ParticleChannels.TotalLifeOffset] = currentTotaLife;
    109 			lifeChannel.data[i+ParticleChannels.LifePercentOffset] = lifePercent;
    110 		}
    111 	}
    112 
    113 	public void update () {
    114 		int deltaMillis = (int)(controller.deltaTime * 1000);
    115 
    116 		if (delayTimer < delay) {
    117 			delayTimer += deltaMillis;
    118 		} else {
    119 			boolean emit = emissionMode != EmissionMode.Disabled;
    120 			//End check
    121 			if (durationTimer < duration) {
    122 				durationTimer += deltaMillis;
    123 				percent = durationTimer / (float)duration;
    124 			}
    125 			else {
    126 				if (continuous && emit && emissionMode == EmissionMode.Enabled)
    127 					controller.start();
    128 				else
    129 					emit = false;
    130 			}
    131 
    132 			if(emit) {
    133 				//Emit particles
    134 				emissionDelta += deltaMillis;
    135 				float emissionTime = emission + emissionDiff * emissionValue.getScale(percent);
    136 				if (emissionTime > 0) {
    137 					emissionTime = 1000 / emissionTime;
    138 					if (emissionDelta >= emissionTime) {
    139 						int emitCount = (int)(emissionDelta / emissionTime);
    140 						emitCount = Math.min(emitCount, maxParticleCount - controller.particles.size);
    141 						emissionDelta -= emitCount * emissionTime;
    142 						emissionDelta %= emissionTime;
    143 						addParticles(emitCount);
    144 					}
    145 				}
    146 				if (controller.particles.size < minParticleCount)
    147 					addParticles(minParticleCount - controller.particles.size);
    148 			}
    149 		}
    150 
    151 		//Update particles
    152 		int activeParticles = controller.particles.size;
    153 		for (int i = 0, k=0; i < controller.particles.size; ) {
    154 			if ( (lifeChannel.data[k+ParticleChannels.CurrentLifeOffset] -= deltaMillis) <= 0) {
    155 				controller.particles.removeElement(i);
    156 				continue;
    157 			}
    158 			else {
    159 				lifeChannel.data[k+ParticleChannels.LifePercentOffset] = 1- lifeChannel.data[k+ParticleChannels.CurrentLifeOffset]/lifeChannel.data[k+ParticleChannels.TotalLifeOffset];
    160 			}
    161 			++i;
    162 			k+=lifeChannel.strideSize;
    163 		}
    164 
    165 		if(controller.particles.size < activeParticles){
    166 			controller.killParticles(controller.particles.size, activeParticles - controller.particles.size);
    167 		}
    168 	}
    169 
    170 	private void addParticles (int count) {
    171 		count = Math.min(count, maxParticleCount - controller.particles.size);
    172 		if (count <= 0) return;
    173 		controller.activateParticles (controller.particles.size, count);
    174 		controller.particles.size += count;
    175 	}
    176 
    177 	public ScaledNumericValue getLife () {
    178 		return lifeValue;
    179 	}
    180 
    181 	public ScaledNumericValue getEmission () {
    182 		return emissionValue;
    183 	}
    184 
    185 	public RangedNumericValue getDuration () {
    186 		return durationValue;
    187 	}
    188 
    189 	public RangedNumericValue getDelay () {
    190 		return delayValue;
    191 	}
    192 
    193 	public ScaledNumericValue getLifeOffset () {
    194 		return lifeOffsetValue;
    195 	}
    196 
    197 	public boolean isContinuous () {
    198 		return continuous;
    199 	}
    200 
    201 	public void setContinuous (boolean continuous) {
    202 		this.continuous = continuous;
    203 	}
    204 
    205 	/** Gets current emission mode.
    206 	 * @return Current emission mode.*/
    207 	public EmissionMode getEmissionMode(){
    208 		return emissionMode;
    209 	}
    210 
    211 	/** Sets emission mode. Emission mode does not affect already emitted particles.
    212 	 * @param emissionMode Emission mode to set.*/
    213 	public void setEmissionMode(EmissionMode emissionMode){
    214 		this.emissionMode = emissionMode;
    215 	}
    216 
    217 	@Override
    218 	public boolean isComplete () {
    219 		if (delayTimer < delay) return false;
    220 		return durationTimer >= duration && controller.particles.size == 0;
    221 	}
    222 
    223 	public float getPercentComplete () {
    224 		if (delayTimer < delay) return 0;
    225 		return Math.min(1, durationTimer / (float)duration);
    226 	}
    227 
    228 	public void set (RegularEmitter emitter) {
    229 		super.set(emitter);
    230 		delayValue.load(emitter.delayValue);
    231 		durationValue.load(emitter.durationValue);
    232 		lifeOffsetValue.load(emitter.lifeOffsetValue);
    233 		lifeValue.load(emitter.lifeValue);
    234 		emissionValue.load(emitter.emissionValue);
    235 		emission = emitter.emission;
    236 		emissionDiff = emitter.emissionDiff;
    237 		emissionDelta = emitter.emissionDelta;
    238 		lifeOffset = emitter.lifeOffset;
    239 		lifeOffsetDiff = emitter.lifeOffsetDiff;
    240 		life = emitter.life;
    241 		lifeDiff = emitter.lifeDiff;
    242 		duration = emitter.duration;
    243 		delay = emitter.delay;
    244 		durationTimer = emitter.durationTimer;
    245 		delayTimer = emitter.delayTimer;
    246 		continuous = emitter.continuous;
    247 	}
    248 
    249 	@Override
    250 	public ParticleControllerComponent copy () {
    251 		return new RegularEmitter(this);
    252 	}
    253 
    254 	@Override
    255 	public void write (Json json) {
    256 		super.write(json);
    257 		json.writeValue("continous", continuous);
    258 		json.writeValue("emission", emissionValue);
    259 		json.writeValue("delay", delayValue);
    260 		json.writeValue("duration", durationValue);
    261 		json.writeValue("life", lifeValue);
    262 		json.writeValue("lifeOffset", lifeOffsetValue);
    263 	}
    264 
    265 	@Override
    266 	public void read (Json json, JsonValue jsonData) {
    267 		super.read(json, jsonData);
    268 		continuous = json.readValue("continous", boolean.class, jsonData);
    269 		emissionValue = json.readValue("emission", ScaledNumericValue.class, jsonData);
    270 		delayValue = json.readValue("delay", RangedNumericValue.class, jsonData);
    271 		durationValue = json.readValue("duration", RangedNumericValue.class, jsonData);
    272 		lifeValue = json.readValue("life", ScaledNumericValue.class, jsonData);
    273 		lifeOffsetValue = json.readValue("lifeOffset", ScaledNumericValue.class, jsonData);
    274 	}
    275 }
    276