Home | History | Annotate | Download | only in influencers
      1 package com.badlogic.gdx.graphics.g3d.particles.influencers;
      2 
      3 import java.util.Arrays;
      4 
      5 import com.badlogic.gdx.graphics.g3d.particles.ParallelArray.FloatChannel;
      6 import com.badlogic.gdx.graphics.g3d.particles.ParticleChannels;
      7 import com.badlogic.gdx.graphics.g3d.particles.ParticleController;
      8 import com.badlogic.gdx.math.MathUtils;
      9 import com.badlogic.gdx.utils.Array;
     10 import com.badlogic.gdx.utils.Json;
     11 import com.badlogic.gdx.utils.JsonValue;
     12 
     13 /** It's an {@link Influencer} which controls the particles dynamics (movement, rotations).
     14  *  @author Inferno */
     15 public class DynamicsInfluencer extends Influencer {
     16 	public Array<DynamicsModifier> velocities;
     17 	private FloatChannel 	accellerationChannel,
     18 												positionChannel, previousPositionChannel,
     19 												rotationChannel, angularVelocityChannel;
     20 	boolean hasAcceleration, has2dAngularVelocity, has3dAngularVelocity;
     21 
     22 	public DynamicsInfluencer(){
     23 		this.velocities = new Array<DynamicsModifier>(true, 3, DynamicsModifier.class);
     24 	}
     25 
     26 	public DynamicsInfluencer(DynamicsModifier...velocities){
     27 		this.velocities = new Array<DynamicsModifier>(true, velocities.length, DynamicsModifier.class);
     28 		for(DynamicsModifier value : velocities){
     29 			this.velocities.add((DynamicsModifier)value.copy());
     30 		}
     31 	}
     32 
     33 	public DynamicsInfluencer (DynamicsInfluencer velocityInfluencer) {
     34 		this((DynamicsModifier[])velocityInfluencer.velocities.toArray(DynamicsModifier.class));
     35 	}
     36 
     37 	@Override
     38 	public void allocateChannels() {
     39 		for(int k=0; k < velocities.size; ++k){
     40 			velocities.items[k].allocateChannels();
     41 		}
     42 
     43 		//Hack, shouldn't be done but after all the modifiers allocated their channels
     44 		//it's possible to check if we need to allocate previous position channel
     45 		accellerationChannel = controller.particles.getChannel(ParticleChannels.Acceleration);
     46 		hasAcceleration = accellerationChannel != null;
     47 		if(hasAcceleration){
     48 			positionChannel = controller.particles.addChannel(ParticleChannels.Position);
     49 			previousPositionChannel = controller.particles.addChannel(ParticleChannels.PreviousPosition);
     50 		}
     51 
     52 		//Angular velocity check
     53 		angularVelocityChannel = controller.particles.getChannel(ParticleChannels.AngularVelocity2D);
     54 		has2dAngularVelocity = angularVelocityChannel != null;
     55 		if(has2dAngularVelocity){
     56 			rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation2D);
     57 			has3dAngularVelocity = false;
     58 		}
     59 		else{
     60 			angularVelocityChannel = controller.particles.getChannel(ParticleChannels.AngularVelocity3D);
     61 			has3dAngularVelocity = angularVelocityChannel != null;
     62 			if(has3dAngularVelocity)
     63 				rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation3D);
     64 		}
     65 	}
     66 
     67 	@Override
     68 	public void set(ParticleController particleController) {
     69 		super.set(particleController);
     70 		for(int k=0; k < velocities.size; ++k){
     71 			velocities.items[k].set(particleController);
     72 		}
     73 	}
     74 
     75 	@Override
     76 	public void init () {
     77 		for(int k=0; k < velocities.size; ++k){
     78 			velocities.items[k].init();
     79 		}
     80 	}
     81 
     82 	public void activateParticles (int startIndex, int count) {
     83 		if(hasAcceleration){
     84 			//Previous position is the current position
     85 			//Attention, this requires that some other influencer setting the position channel must execute before this influencer.
     86 			for(int i=startIndex*positionChannel.strideSize, c = i +count*positionChannel.strideSize; i< c;  i+= positionChannel.strideSize){
     87 				previousPositionChannel.data[i+ParticleChannels.XOffset] = positionChannel.data[i+ParticleChannels.XOffset];
     88 				previousPositionChannel.data[i+ParticleChannels.YOffset] = positionChannel.data[i+ParticleChannels.YOffset];
     89 				previousPositionChannel.data[i+ParticleChannels.ZOffset] = positionChannel.data[i+ParticleChannels.ZOffset];
     90 				/*
     91 				//Euler intialization
     92 				previousPositionChannel.data[i+ParticleChannels.XOffset] =
     93 				previousPositionChannel.data[i+ParticleChannels.YOffset] =
     94 				previousPositionChannel.data[i+ParticleChannels.ZOffset] = 0;
     95 				*/
     96 			}
     97 		}
     98 
     99 		if(has2dAngularVelocity){
    100 			//Rotation back to 0
    101 			for(int i=startIndex*rotationChannel.strideSize, c = i +count*rotationChannel.strideSize; i< c;  i+= rotationChannel.strideSize){
    102 				rotationChannel.data[i+ParticleChannels.CosineOffset] = 1;
    103 				rotationChannel.data[i+ParticleChannels.SineOffset] = 0;
    104 			}
    105 		}
    106 		else if(has3dAngularVelocity){
    107 			//Rotation back to 0
    108 			for(int i=startIndex*rotationChannel.strideSize, c = i +count*rotationChannel.strideSize; i< c;  i+= rotationChannel.strideSize){
    109 				rotationChannel.data[i+ParticleChannels.XOffset] = 0;
    110 				rotationChannel.data[i+ParticleChannels.YOffset] = 0;
    111 				rotationChannel.data[i+ParticleChannels.ZOffset] = 0;
    112 				rotationChannel.data[i+ParticleChannels.WOffset] = 1;
    113 			}
    114 		}
    115 
    116 		for(int k=0; k < velocities.size; ++k){
    117 			velocities.items[k].activateParticles(startIndex, count);
    118 		}
    119 	}
    120 
    121 	public void update(){
    122 		//Clean previouse frame velocities
    123 		if(hasAcceleration)
    124 			Arrays.fill(accellerationChannel.data, 0, controller.particles.size*accellerationChannel.strideSize, 0);
    125 		if(has2dAngularVelocity || has3dAngularVelocity)
    126 			Arrays.fill(angularVelocityChannel.data, 0, controller.particles.size*angularVelocityChannel.strideSize, 0);
    127 
    128 		//Sum all the forces/accelerations
    129 		for(int k=0; k < velocities.size; ++k){
    130 			velocities.items[k].update();
    131 		}
    132 
    133 		//Apply the forces
    134 		if(hasAcceleration){
    135 			/*
    136 			 //Euler Integration
    137 			for(int 	i=0, offset = 0; i < controller.particles.size; ++i, offset +=positionChannel.strideSize){
    138 				previousPositionChannel.data[offset + ParticleChannels.XOffset] += accellerationChannel.data[offset + ParticleChannels.XOffset]*controller.deltaTime;
    139 				previousPositionChannel.data[offset + ParticleChannels.YOffset] += accellerationChannel.data[offset + ParticleChannels.YOffset]*controller.deltaTime;
    140 				previousPositionChannel.data[offset + ParticleChannels.ZOffset] += accellerationChannel.data[offset + ParticleChannels.ZOffset]*controller.deltaTime;
    141 
    142 				positionChannel.data[offset + ParticleChannels.XOffset] += previousPositionChannel.data[offset + ParticleChannels.XOffset]*controller.deltaTime;
    143 				positionChannel.data[offset + ParticleChannels.YOffset] += previousPositionChannel.data[offset + ParticleChannels.YOffset]*controller.deltaTime;
    144 				positionChannel.data[offset + ParticleChannels.ZOffset] += previousPositionChannel.data[offset + ParticleChannels.ZOffset]*controller.deltaTime;
    145 			}
    146 			*/
    147 			//Verlet integration
    148 			for(int 	i=0, offset = 0; i < controller.particles.size; ++i, offset +=positionChannel.strideSize){
    149 				float 	x = positionChannel.data[offset + ParticleChannels.XOffset],
    150 							y = positionChannel.data[offset + ParticleChannels.YOffset],
    151 							z = positionChannel.data[offset + ParticleChannels.ZOffset];
    152 				positionChannel.data[offset + ParticleChannels.XOffset] = 2*x - previousPositionChannel.data[offset + ParticleChannels.XOffset] +
    153 									accellerationChannel.data[offset + ParticleChannels.XOffset]*controller.deltaTimeSqr;
    154 				positionChannel.data[offset + ParticleChannels.YOffset] = 2*y- previousPositionChannel.data[offset + ParticleChannels.YOffset] +
    155 									accellerationChannel.data[offset + ParticleChannels.YOffset]*controller.deltaTimeSqr;
    156 				positionChannel.data[offset + ParticleChannels.ZOffset] = 2*z - previousPositionChannel.data[offset + ParticleChannels.ZOffset] +
    157 									accellerationChannel.data[offset + ParticleChannels.ZOffset]*controller.deltaTimeSqr;
    158 				previousPositionChannel.data[offset + ParticleChannels.XOffset] = x;
    159 				previousPositionChannel.data[offset + ParticleChannels.YOffset] = y;
    160 				previousPositionChannel.data[offset + ParticleChannels.ZOffset] = z;
    161 			}
    162 		}
    163 
    164 		if(has2dAngularVelocity){
    165 			for(int 	i=0, offset = 0; i < controller.particles.size; ++i, offset +=rotationChannel.strideSize){
    166 				float rotation = angularVelocityChannel.data[i]*controller.deltaTime;
    167 				if(rotation != 0){
    168 					float cosBeta = MathUtils.cosDeg(rotation), sinBeta = MathUtils.sinDeg(rotation);
    169 					float currentCosine = rotationChannel.data[offset + ParticleChannels.CosineOffset];
    170 					float currentSine = rotationChannel.data[offset + ParticleChannels.SineOffset];
    171 					float 	newCosine = currentCosine*cosBeta - currentSine*sinBeta,
    172 						newSine = currentSine*cosBeta + currentCosine*sinBeta;
    173 					rotationChannel.data[offset + ParticleChannels.CosineOffset] = newCosine;
    174 					rotationChannel.data[offset + ParticleChannels.SineOffset] = newSine;
    175 				}
    176 			}
    177 		}
    178 		else if(has3dAngularVelocity){
    179 			for(int 	i=0, offset = 0, angularOffset = 0; i < controller.particles.size; ++i,
    180 					offset +=rotationChannel.strideSize, angularOffset += angularVelocityChannel.strideSize){
    181 
    182 				float	wx = angularVelocityChannel.data[angularOffset + ParticleChannels.XOffset],
    183 							wy = angularVelocityChannel.data[angularOffset + ParticleChannels.YOffset],
    184 							wz = angularVelocityChannel.data[angularOffset + ParticleChannels.ZOffset],
    185 							qx = rotationChannel.data[offset + ParticleChannels.XOffset],
    186 							qy = rotationChannel.data[offset + ParticleChannels.YOffset],
    187 							qz = rotationChannel.data[offset + ParticleChannels.ZOffset],
    188 							qw = rotationChannel.data[offset + ParticleChannels.WOffset];
    189 				TMP_Q.set(wx, wy, wz, 0).mul(qx, qy, qz, qw).mul(0.5f*controller.deltaTime).add(qx, qy, qz, qw).nor();
    190 				rotationChannel.data[offset + ParticleChannels.XOffset] = TMP_Q.x;
    191 				rotationChannel.data[offset + ParticleChannels.YOffset] = TMP_Q.y;
    192 				rotationChannel.data[offset + ParticleChannels.ZOffset] = TMP_Q.z;
    193 				rotationChannel.data[offset + ParticleChannels.WOffset] = TMP_Q.w;
    194 			}
    195 		}
    196 	}
    197 
    198 	@Override
    199 	public DynamicsInfluencer copy () {
    200 		return new DynamicsInfluencer(this);
    201 	}
    202 
    203 	@Override
    204 	public void write (Json json) {
    205 		json.writeValue("velocities", velocities, Array.class, DynamicsModifier.class);
    206 	}
    207 
    208 	@Override
    209 	public void read (Json json, JsonValue jsonData) {
    210 		velocities.addAll(json.readValue("velocities", Array.class, DynamicsModifier.class, jsonData));
    211 	}
    212 }
    213