Home | History | Annotate | Download | only in influencers
      1 package com.badlogic.gdx.graphics.g3d.particles.influencers;
      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.ScaledNumericValue;
      7 import com.badlogic.gdx.math.MathUtils;
      8 import com.badlogic.gdx.math.Matrix4;
      9 import com.badlogic.gdx.math.Quaternion;
     10 import com.badlogic.gdx.math.Vector3;
     11 import com.badlogic.gdx.utils.Json;
     12 import com.badlogic.gdx.utils.JsonValue;
     13 
     14 /** It's the base class for any kind of influencer which operates on angular velocity and acceleration of the particles.
     15  * All the classes that will inherit this base class can and should be used
     16  * only as sub-influencer of an instance of {@link DynamicsInfluencer} .
     17  *  @author Inferno */
     18 public abstract class DynamicsModifier extends Influencer{
     19 	protected static final Vector3 	TMP_V1 = new Vector3(),
     20 		 										TMP_V2 = new Vector3(),
     21 		 										TMP_V3 = new Vector3();
     22 	protected static final Quaternion TMP_Q = new Quaternion();
     23 
     24 	public static class FaceDirection extends DynamicsModifier {
     25 		FloatChannel rotationChannel, accellerationChannel;
     26 
     27 		public FaceDirection(){}
     28 
     29 		public FaceDirection (FaceDirection rotation) {
     30 			super(rotation);
     31 		}
     32 		@Override
     33 		public void allocateChannels() {
     34 			rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation3D);
     35 			accellerationChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
     36 		}
     37 
     38 		@Override
     39 		public void update () {
     40 			for(int 	i=0, accelOffset = 0, c = i +controller.particles.size *rotationChannel.strideSize;
     41 				i < c;
     42 				i +=rotationChannel.strideSize, accelOffset += accellerationChannel.strideSize){
     43 
     44 				Vector3 	axisZ = TMP_V1.set(	accellerationChannel.data[accelOffset +ParticleChannels.XOffset],
     45 																				accellerationChannel.data[accelOffset +ParticleChannels.YOffset],
     46 																				accellerationChannel.data[accelOffset +ParticleChannels.ZOffset]).nor(),
     47 					axisY = TMP_V2.set(TMP_V1).crs(Vector3.Y).nor().crs(TMP_V1).nor(),
     48 					axisX = TMP_V3.set(axisY).crs(axisZ).nor();
     49 				TMP_Q.setFromAxes(false, 	axisX.x,  axisY.x, axisZ.x,
     50 															axisX.y,  axisY.y, axisZ.y,
     51 															axisX.z,  axisY.z, axisZ.z);
     52 				rotationChannel.data[i +ParticleChannels.XOffset] = TMP_Q.x;
     53 				rotationChannel.data[i +ParticleChannels.YOffset] = TMP_Q.y;
     54 				rotationChannel.data[i +ParticleChannels.ZOffset] = TMP_Q.z;
     55 				rotationChannel.data[i +ParticleChannels.WOffset] = TMP_Q.w;
     56 			}
     57 		}
     58 
     59 		@Override
     60 		public ParticleControllerComponent copy () {
     61 			return new FaceDirection(this);
     62 		}
     63 	}
     64 
     65 	public static abstract class Strength extends DynamicsModifier {
     66 		protected FloatChannel strengthChannel;
     67 		public ScaledNumericValue strengthValue;
     68 
     69 		public Strength(){
     70 			strengthValue = new ScaledNumericValue();
     71 		}
     72 
     73 		public Strength (Strength rotation) {
     74 			super(rotation);
     75 			strengthValue = new ScaledNumericValue();
     76 			strengthValue.load(rotation.strengthValue);
     77 		}
     78 
     79 		@Override
     80 		public void allocateChannels() {
     81 			super.allocateChannels();
     82 			ParticleChannels.Interpolation.id = controller.particleChannels.newId();
     83 			strengthChannel = controller.particles.addChannel(ParticleChannels.Interpolation);
     84 		}
     85 
     86 		@Override
     87 		public void activateParticles (int startIndex, int count) {
     88 			float start, diff;
     89 			for(int 	i=startIndex*strengthChannel.strideSize, c = i +count*strengthChannel.strideSize;
     90 				i < c;
     91 				i +=strengthChannel.strideSize){
     92 				start = strengthValue.newLowValue();
     93 				diff = strengthValue.newHighValue();
     94 				if(!strengthValue.isRelative())
     95 					diff -= start;
     96 				strengthChannel.data[i + ParticleChannels.VelocityStrengthStartOffset] = start;
     97 				strengthChannel.data[i + ParticleChannels.VelocityStrengthDiffOffset] = diff;
     98 			}
     99 		}
    100 
    101 		@Override
    102 		public void write (Json json) {
    103 			super.write(json);
    104 			json.writeValue("strengthValue", strengthValue);
    105 		}
    106 
    107 		@Override
    108 		public void read (Json json, JsonValue jsonData) {
    109 			super.read(json, jsonData);
    110 			strengthValue = json.readValue("strengthValue", ScaledNumericValue.class, jsonData);
    111 		}
    112 	}
    113 
    114 	public static abstract class Angular extends Strength {
    115 		protected FloatChannel angularChannel;
    116 		/** Polar angle, XZ plane */
    117 		public ScaledNumericValue thetaValue;
    118 		/** Azimuth, Y */
    119 		public ScaledNumericValue phiValue;
    120 
    121 		public Angular(){
    122 			thetaValue = new ScaledNumericValue();
    123 			phiValue = new ScaledNumericValue();
    124 		}
    125 
    126 		public Angular (Angular value) {
    127 			super(value);
    128 			thetaValue = new ScaledNumericValue();
    129 			phiValue = new ScaledNumericValue();
    130 			thetaValue.load(value.thetaValue);
    131 			phiValue.load(value.phiValue);
    132 		}
    133 
    134 		@Override
    135 		public void allocateChannels() {
    136 			super.allocateChannels();
    137 			ParticleChannels.Interpolation4.id = controller.particleChannels.newId();
    138 			angularChannel = controller.particles.addChannel(ParticleChannels.Interpolation4);
    139 		}
    140 
    141 		@Override
    142 		public void activateParticles (int startIndex, int count) {
    143 			super.activateParticles(startIndex, count);
    144 			float start, diff;
    145 			for(int 	i=startIndex*angularChannel.strideSize, c = i +count*angularChannel.strideSize;
    146 				i < c;
    147 				i +=angularChannel.strideSize){
    148 
    149 				//Theta
    150 				start = thetaValue.newLowValue();
    151 				diff = thetaValue.newHighValue();
    152 				if(!thetaValue.isRelative())
    153 					diff -= start;
    154 				angularChannel.data[i + ParticleChannels.VelocityThetaStartOffset] = start;
    155 				angularChannel.data[i + ParticleChannels.VelocityThetaDiffOffset] = diff;
    156 
    157 				//Phi
    158 				start = phiValue.newLowValue();
    159 				diff = phiValue.newHighValue();
    160 				if(!phiValue.isRelative())
    161 					diff -= start;
    162 				angularChannel.data[i + ParticleChannels.VelocityPhiStartOffset] = start;
    163 				angularChannel.data[i + ParticleChannels.VelocityPhiDiffOffset] = diff;
    164 			}
    165 		}
    166 
    167 		@Override
    168 		public void write (Json json) {
    169 			super.write(json);
    170 			json.writeValue("thetaValue", thetaValue);
    171 			json.writeValue("phiValue", phiValue);
    172 		}
    173 
    174 		@Override
    175 		public void read (Json json, JsonValue jsonData) {
    176 			super.read(json, jsonData);
    177 			thetaValue = json.readValue("thetaValue", ScaledNumericValue.class, jsonData);
    178 			phiValue = json.readValue("phiValue", ScaledNumericValue.class, jsonData);
    179 		}
    180 	}
    181 
    182 
    183 	public static class Rotational2D extends Strength {
    184 		FloatChannel rotationalVelocity2dChannel;
    185 
    186 		public Rotational2D (){}
    187 
    188 		public Rotational2D (Rotational2D rotation) {
    189 			super(rotation);
    190 		}
    191 
    192 		@Override
    193 		public void allocateChannels() {
    194 			super.allocateChannels();
    195 			rotationalVelocity2dChannel = controller.particles.addChannel(ParticleChannels.AngularVelocity2D);
    196 		}
    197 
    198 		@Override
    199 		public void update () {
    200 			for(int 	i=0, l = ParticleChannels.LifePercentOffset, s =0,
    201 				c = i +controller.particles.size*rotationalVelocity2dChannel.strideSize;
    202 				i < c;
    203 				s += strengthChannel.strideSize, i +=rotationalVelocity2dChannel.strideSize,  l +=lifeChannel.strideSize){
    204 				rotationalVelocity2dChannel.data[i] += 	strengthChannel.data[s + ParticleChannels.VelocityStrengthStartOffset] +
    205 																									strengthChannel.data[s + ParticleChannels.VelocityStrengthDiffOffset]* strengthValue.getScale(lifeChannel.data[l]);
    206 			}
    207 		}
    208 
    209 		@Override
    210 		public Rotational2D copy () {
    211 			return new Rotational2D(this);
    212 		}
    213 	}
    214 
    215 	public static class Rotational3D extends Angular {
    216 		FloatChannel rotationChannel, rotationalForceChannel;
    217 
    218 		public Rotational3D(){}
    219 
    220 		public Rotational3D (Rotational3D rotation) {
    221 			super(rotation);
    222 		}
    223 
    224 		@Override
    225 		public void allocateChannels() {
    226 			super.allocateChannels();
    227 			rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation3D);
    228 			rotationalForceChannel = controller.particles.addChannel(ParticleChannels.AngularVelocity3D);
    229 		}
    230 
    231 		@Override
    232 		public void update () {
    233 
    234 			//Matrix3 I_t = defined by the shape, it's the inertia tensor
    235 			//Vector3 r = position vector
    236 			//Vector3 L = r.cross(v.mul(m)), It's the angular momentum, where mv it's the linear momentum
    237 			//Inverse(I_t) = a diagonal matrix where the diagonal is IyIz, IxIz, IxIy
    238 			//Vector3 w = L/I_t = inverse(I_t)*L, It's the angular velocity
    239 			//Quaternion spin = 0.5f*Quaternion(w, 0)*currentRotation
    240 			//currentRotation += spin*dt
    241 			//normalize(currentRotation)
    242 
    243 			//Algorithm 1
    244 			//Consider a simple channel which represent an angular velocity w
    245 			//Sum each w for each rotation
    246 			//Update rotation
    247 
    248 			//Algorithm 2
    249 			//Consider a channel which represent a sort of angular momentum L  (r, v)
    250 			//Sum each L for each rotation
    251 			//Multiply sum by constant quantity k = m*I_to(-1) , m could be optional while I is constant and can be calculated at start
    252 			//Update rotation
    253 
    254 			//Algorithm 3
    255 			//Consider a channel which represent a simple angular momentum L
    256 			//Proceed as Algorithm 2
    257 
    258 			for(int 	i=0, l = ParticleChannels.LifePercentOffset, s =0, a = 0,
    259 				c = controller.particles.size*rotationalForceChannel.strideSize;
    260 				i < c;
    261 				s += strengthChannel.strideSize, i +=rotationalForceChannel.strideSize,
    262 				a += angularChannel.strideSize, l += lifeChannel.strideSize){
    263 
    264 				float 	lifePercent = lifeChannel.data[l],
    265 							strength = 	strengthChannel.data[s + ParticleChannels.VelocityStrengthStartOffset] +
    266 													strengthChannel.data[s + ParticleChannels.VelocityStrengthDiffOffset]* strengthValue.getScale(lifePercent),
    267 							phi = 	angularChannel.data[a + ParticleChannels.VelocityPhiStartOffset] +
    268 											angularChannel.data[a + ParticleChannels.VelocityPhiDiffOffset]* phiValue.getScale(lifePercent),
    269 							theta = 	angularChannel.data[a + ParticleChannels.VelocityThetaStartOffset] +
    270 												angularChannel.data[a + ParticleChannels.VelocityThetaDiffOffset]* thetaValue.getScale(lifePercent);
    271 
    272 				float 	cosTheta = MathUtils.cosDeg(theta), sinTheta = MathUtils.sinDeg(theta),
    273 							cosPhi = MathUtils.cosDeg(phi), sinPhi = MathUtils.sinDeg(phi);
    274 
    275 				TMP_V3.set(cosTheta *sinPhi, cosPhi, sinTheta*sinPhi);
    276 				TMP_V3.scl(strength*MathUtils.degreesToRadians);
    277 
    278 				rotationalForceChannel.data[i +ParticleChannels.XOffset] += TMP_V3.x;
    279 				rotationalForceChannel.data[i +ParticleChannels.YOffset] += TMP_V3.y;
    280 				rotationalForceChannel.data[i +ParticleChannels.ZOffset] += TMP_V3.z;
    281 			}
    282 		}
    283 
    284 		@Override
    285 		public Rotational3D copy () {
    286 			return new Rotational3D(this);
    287 		}
    288 	}
    289 
    290 
    291 	public static class CentripetalAcceleration extends Strength {
    292 		FloatChannel accelerationChannel;
    293 		FloatChannel positionChannel;
    294 		public CentripetalAcceleration(){}
    295 
    296 		public CentripetalAcceleration (CentripetalAcceleration rotation) {
    297 			super(rotation);
    298 		}
    299 
    300 		@Override
    301 		public void allocateChannels() {
    302 			super.allocateChannels();
    303 			accelerationChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
    304 			positionChannel = controller.particles.addChannel(ParticleChannels.Position);
    305 		}
    306 
    307 		@Override
    308 		public void update () {
    309 			float cx = 0, cy = 0, cz = 0;
    310 			if(!isGlobal){
    311 				float[] val = controller.transform.val;
    312 				cx = val[Matrix4.M03];
    313 				cy = val[Matrix4.M13];
    314 				cz = val[Matrix4.M23];
    315 			}
    316 
    317 			int lifeOffset=ParticleChannels.LifePercentOffset, strengthOffset = 0, positionOffset = 0, forceOffset = 0;
    318 			for(int 	i=0,  c= controller.particles.size; i < c; ++i,
    319 				positionOffset += positionChannel.strideSize,
    320 				strengthOffset += strengthChannel.strideSize,
    321 				forceOffset +=accelerationChannel.strideSize,
    322 				lifeOffset += lifeChannel.strideSize){
    323 
    324 				float 	strength = 	strengthChannel.data[strengthOffset + ParticleChannels.VelocityStrengthStartOffset] +
    325 													strengthChannel.data[strengthOffset + ParticleChannels.VelocityStrengthDiffOffset]* strengthValue.getScale(lifeChannel.data[lifeOffset]);
    326 				TMP_V3.set(	positionChannel.data[positionOffset +ParticleChannels.XOffset] -cx,
    327 											positionChannel.data[positionOffset +ParticleChannels.YOffset] -cy,
    328 											positionChannel.data[positionOffset +ParticleChannels.ZOffset] -cz)
    329 								.nor().scl(strength);
    330 				accelerationChannel.data[forceOffset +ParticleChannels.XOffset] += TMP_V3.x;
    331 				accelerationChannel.data[forceOffset +ParticleChannels.YOffset] += TMP_V3.y;
    332 				accelerationChannel.data[forceOffset +ParticleChannels.ZOffset] += TMP_V3.z;
    333 			}
    334 		}
    335 
    336 		@Override
    337 		public CentripetalAcceleration copy () {
    338 			return new CentripetalAcceleration(this);
    339 		}
    340 	}
    341 
    342 	public static class PolarAcceleration extends Angular {
    343 		FloatChannel directionalVelocityChannel;
    344 		public PolarAcceleration(){}
    345 
    346 		public PolarAcceleration (PolarAcceleration rotation) {
    347 			super(rotation);
    348 		}
    349 
    350 		@Override
    351 		public void allocateChannels() {
    352 			super.allocateChannels();
    353 			directionalVelocityChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
    354 		}
    355 
    356 		@Override
    357 		public void update () {
    358 			for(int 	i=0, l = ParticleChannels.LifePercentOffset, s =0, a = 0,
    359 				c = i +controller.particles.size*directionalVelocityChannel.strideSize;
    360 				i < c;
    361 				s += strengthChannel.strideSize, i +=directionalVelocityChannel.strideSize,
    362 				a += angularChannel.strideSize, l += lifeChannel.strideSize){
    363 
    364 				float 	lifePercent = lifeChannel.data[l],
    365 							strength = 	strengthChannel.data[s + ParticleChannels.VelocityStrengthStartOffset] +
    366 													strengthChannel.data[s + ParticleChannels.VelocityStrengthDiffOffset]* strengthValue.getScale(lifePercent),
    367 							phi = 	angularChannel.data[a + ParticleChannels.VelocityPhiStartOffset] +
    368 											angularChannel.data[a + ParticleChannels.VelocityPhiDiffOffset]* phiValue.getScale(lifePercent),
    369 							theta = 	angularChannel.data[a + ParticleChannels.VelocityThetaStartOffset] +
    370 												angularChannel.data[a + ParticleChannels.VelocityThetaDiffOffset]* thetaValue.getScale(lifePercent);
    371 
    372 				float cosTheta = MathUtils.cosDeg(theta), sinTheta = MathUtils.sinDeg(theta),
    373 					cosPhi = MathUtils.cosDeg(phi), sinPhi = MathUtils.sinDeg(phi);
    374 				TMP_V3.set(cosTheta *sinPhi, cosPhi, sinTheta*sinPhi).nor().scl(strength);
    375 				directionalVelocityChannel.data[i +ParticleChannels.XOffset] += TMP_V3.x;
    376 				directionalVelocityChannel.data[i +ParticleChannels.YOffset] += TMP_V3.y;
    377 				directionalVelocityChannel.data[i +ParticleChannels.ZOffset] += TMP_V3.z;
    378 			}
    379 		}
    380 
    381 		@Override
    382 		public PolarAcceleration copy () {
    383 			return new PolarAcceleration(this);
    384 		}
    385 	}
    386 
    387 	public static class TangentialAcceleration extends Angular {
    388 		FloatChannel directionalVelocityChannel, positionChannel;
    389 
    390 		public TangentialAcceleration(){}
    391 
    392 		public TangentialAcceleration (TangentialAcceleration rotation) {
    393 			super(rotation);
    394 		}
    395 
    396 		@Override
    397 		public void allocateChannels() {
    398 			super.allocateChannels();
    399 			directionalVelocityChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
    400 			positionChannel = controller.particles.addChannel(ParticleChannels.Position);
    401 		}
    402 
    403 		@Override
    404 		public void update () {
    405 			for(int 	i=0, l = ParticleChannels.LifePercentOffset, s =0, a = 0, positionOffset = 0,
    406 				c = i +controller.particles.size*directionalVelocityChannel.strideSize;
    407 				i < c;
    408 				s += strengthChannel.strideSize, i +=directionalVelocityChannel.strideSize,
    409 				a += angularChannel.strideSize, l += lifeChannel.strideSize, positionOffset += positionChannel.strideSize ){
    410 
    411 				float 	lifePercent = lifeChannel.data[l],
    412 							strength = 	strengthChannel.data[s + ParticleChannels.VelocityStrengthStartOffset] +
    413 													strengthChannel.data[s + ParticleChannels.VelocityStrengthDiffOffset]* strengthValue.getScale(lifePercent),
    414 							phi = 	angularChannel.data[a + ParticleChannels.VelocityPhiStartOffset] +
    415 											angularChannel.data[a + ParticleChannels.VelocityPhiDiffOffset]* phiValue.getScale(lifePercent),
    416 							theta = 	angularChannel.data[a + ParticleChannels.VelocityThetaStartOffset] +
    417 												angularChannel.data[a + ParticleChannels.VelocityThetaDiffOffset]* thetaValue.getScale(lifePercent);
    418 
    419 				float cosTheta = MathUtils.cosDeg(theta), sinTheta = MathUtils.sinDeg(theta),
    420 					cosPhi = MathUtils.cosDeg(phi), sinPhi = MathUtils.sinDeg(phi);
    421 				TMP_V3.set(cosTheta *sinPhi, cosPhi, sinTheta*sinPhi)
    422 								.crs(	positionChannel.data[positionOffset +ParticleChannels.XOffset],
    423 											positionChannel.data[positionOffset +ParticleChannels.YOffset],
    424 											positionChannel.data[positionOffset +ParticleChannels.ZOffset])
    425 								.nor().scl(strength);
    426 				directionalVelocityChannel.data[i +ParticleChannels.XOffset] += TMP_V3.x;
    427 				directionalVelocityChannel.data[i +ParticleChannels.YOffset] += TMP_V3.y;
    428 				directionalVelocityChannel.data[i +ParticleChannels.ZOffset] += TMP_V3.z;
    429 			}
    430 		}
    431 
    432 		@Override
    433 		public TangentialAcceleration copy () {
    434 			return new TangentialAcceleration(this);
    435 		}
    436 	}
    437 
    438 	public static class BrownianAcceleration extends Strength {
    439 		FloatChannel accelerationChannel;
    440 		public BrownianAcceleration(){}
    441 
    442 		public BrownianAcceleration (BrownianAcceleration rotation) {
    443 			super(rotation);
    444 		}
    445 
    446 		@Override
    447 		public void allocateChannels() {
    448 			super.allocateChannels();
    449 			accelerationChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
    450 		}
    451 
    452 		@Override
    453 		public void update () {
    454 			int lifeOffset=ParticleChannels.LifePercentOffset, strengthOffset = 0, forceOffset = 0;
    455 			for(int 	i=0,  c= controller.particles.size; i < c; ++i,
    456 				strengthOffset += strengthChannel.strideSize,
    457 				forceOffset +=accelerationChannel.strideSize,
    458 				lifeOffset += lifeChannel.strideSize){
    459 
    460 				float 	strength = 	strengthChannel.data[strengthOffset + ParticleChannels.VelocityStrengthStartOffset] +
    461 													strengthChannel.data[strengthOffset + ParticleChannels.VelocityStrengthDiffOffset]* strengthValue.getScale(lifeChannel.data[lifeOffset]);
    462 				TMP_V3.set(MathUtils.random(-1, 1f), MathUtils.random(-1, 1f), MathUtils.random(-1, 1f)).nor().scl(strength);
    463 				accelerationChannel.data[forceOffset +ParticleChannels.XOffset] += TMP_V3.x;
    464 				accelerationChannel.data[forceOffset +ParticleChannels.YOffset] += TMP_V3.y;
    465 				accelerationChannel.data[forceOffset +ParticleChannels.ZOffset] += TMP_V3.z;
    466 			}
    467 		}
    468 
    469 		@Override
    470 		public BrownianAcceleration copy () {
    471 			return new BrownianAcceleration(this);
    472 		}
    473 	}
    474 
    475 
    476 	public boolean isGlobal = false;
    477 	protected FloatChannel lifeChannel;
    478 
    479 	public DynamicsModifier(){}
    480 
    481 	public DynamicsModifier (DynamicsModifier modifier) {
    482 		this.isGlobal = modifier.isGlobal;
    483 	}
    484 
    485 	@Override
    486 	public void allocateChannels() {
    487 		lifeChannel = controller.particles.addChannel(ParticleChannels.Life);
    488 	}
    489 
    490 	@Override
    491 	public void write (Json json) {
    492 		super.write(json);
    493 		json.writeValue("isGlobal", isGlobal);
    494 	}
    495 
    496 	@Override
    497 	public void read (Json json, JsonValue jsonData) {
    498 		super.read(json, jsonData);
    499 		isGlobal = json.readValue("isGlobal", boolean.class, jsonData);
    500 	}
    501 
    502 }
    503 
    504