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