• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FaceDirection()27 		public FaceDirection(){}
28 
FaceDirection(FaceDirection rotation)29 		public FaceDirection (FaceDirection rotation) {
30 			super(rotation);
31 		}
32 		@Override
allocateChannels()33 		public void allocateChannels() {
34 			rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation3D);
35 			accellerationChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
36 		}
37 
38 		@Override
update()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
copy()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 
Strength()69 		public Strength(){
70 			strengthValue = new ScaledNumericValue();
71 		}
72 
Strength(Strength rotation)73 		public Strength (Strength rotation) {
74 			super(rotation);
75 			strengthValue = new ScaledNumericValue();
76 			strengthValue.load(rotation.strengthValue);
77 		}
78 
79 		@Override
allocateChannels()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
activateParticles(int startIndex, int count)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
write(Json json)102 		public void write (Json json) {
103 			super.write(json);
104 			json.writeValue("strengthValue", strengthValue);
105 		}
106 
107 		@Override
read(Json json, JsonValue jsonData)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 
Angular()121 		public Angular(){
122 			thetaValue = new ScaledNumericValue();
123 			phiValue = new ScaledNumericValue();
124 		}
125 
Angular(Angular value)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
allocateChannels()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
activateParticles(int startIndex, int count)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
write(Json json)168 		public void write (Json json) {
169 			super.write(json);
170 			json.writeValue("thetaValue", thetaValue);
171 			json.writeValue("phiValue", phiValue);
172 		}
173 
174 		@Override
read(Json json, JsonValue jsonData)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 
Rotational2D()186 		public Rotational2D (){}
187 
Rotational2D(Rotational2D rotation)188 		public Rotational2D (Rotational2D rotation) {
189 			super(rotation);
190 		}
191 
192 		@Override
allocateChannels()193 		public void allocateChannels() {
194 			super.allocateChannels();
195 			rotationalVelocity2dChannel = controller.particles.addChannel(ParticleChannels.AngularVelocity2D);
196 		}
197 
198 		@Override
update()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
copy()210 		public Rotational2D copy () {
211 			return new Rotational2D(this);
212 		}
213 	}
214 
215 	public static class Rotational3D extends Angular {
216 		FloatChannel rotationChannel, rotationalForceChannel;
217 
Rotational3D()218 		public Rotational3D(){}
219 
Rotational3D(Rotational3D rotation)220 		public Rotational3D (Rotational3D rotation) {
221 			super(rotation);
222 		}
223 
224 		@Override
allocateChannels()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
update()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
copy()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;
CentripetalAcceleration()294 		public CentripetalAcceleration(){}
295 
CentripetalAcceleration(CentripetalAcceleration rotation)296 		public CentripetalAcceleration (CentripetalAcceleration rotation) {
297 			super(rotation);
298 		}
299 
300 		@Override
allocateChannels()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
update()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
copy()337 		public CentripetalAcceleration copy () {
338 			return new CentripetalAcceleration(this);
339 		}
340 	}
341 
342 	public static class PolarAcceleration extends Angular {
343 		FloatChannel directionalVelocityChannel;
PolarAcceleration()344 		public PolarAcceleration(){}
345 
PolarAcceleration(PolarAcceleration rotation)346 		public PolarAcceleration (PolarAcceleration rotation) {
347 			super(rotation);
348 		}
349 
350 		@Override
allocateChannels()351 		public void allocateChannels() {
352 			super.allocateChannels();
353 			directionalVelocityChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
354 		}
355 
356 		@Override
update()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
copy()382 		public PolarAcceleration copy () {
383 			return new PolarAcceleration(this);
384 		}
385 	}
386 
387 	public static class TangentialAcceleration extends Angular {
388 		FloatChannel directionalVelocityChannel, positionChannel;
389 
TangentialAcceleration()390 		public TangentialAcceleration(){}
391 
TangentialAcceleration(TangentialAcceleration rotation)392 		public TangentialAcceleration (TangentialAcceleration rotation) {
393 			super(rotation);
394 		}
395 
396 		@Override
allocateChannels()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
update()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
copy()433 		public TangentialAcceleration copy () {
434 			return new TangentialAcceleration(this);
435 		}
436 	}
437 
438 	public static class BrownianAcceleration extends Strength {
439 		FloatChannel accelerationChannel;
BrownianAcceleration()440 		public BrownianAcceleration(){}
441 
BrownianAcceleration(BrownianAcceleration rotation)442 		public BrownianAcceleration (BrownianAcceleration rotation) {
443 			super(rotation);
444 		}
445 
446 		@Override
allocateChannels()447 		public void allocateChannels() {
448 			super.allocateChannels();
449 			accelerationChannel = controller.particles.addChannel(ParticleChannels.Acceleration);
450 		}
451 
452 		@Override
update()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
copy()470 		public BrownianAcceleration copy () {
471 			return new BrownianAcceleration(this);
472 		}
473 	}
474 
475 
476 	public boolean isGlobal = false;
477 	protected FloatChannel lifeChannel;
478 
DynamicsModifier()479 	public DynamicsModifier(){}
480 
DynamicsModifier(DynamicsModifier modifier)481 	public DynamicsModifier (DynamicsModifier modifier) {
482 		this.isGlobal = modifier.isGlobal;
483 	}
484 
485 	@Override
allocateChannels()486 	public void allocateChannels() {
487 		lifeChannel = controller.particles.addChannel(ParticleChannels.Life);
488 	}
489 
490 	@Override
write(Json json)491 	public void write (Json json) {
492 		super.write(json);
493 		json.writeValue("isGlobal", isGlobal);
494 	}
495 
496 	@Override
read(Json json, JsonValue jsonData)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