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