Home | History | Annotate | Download | only in particles
      1 package com.badlogic.gdx.graphics.g3d.particles;
      2 
      3 import com.badlogic.gdx.assets.AssetManager;
      4 import com.badlogic.gdx.graphics.g3d.particles.ParallelArray.FloatChannel;
      5 import com.badlogic.gdx.graphics.g3d.particles.emitters.Emitter;
      6 import com.badlogic.gdx.graphics.g3d.particles.influencers.Influencer;
      7 import com.badlogic.gdx.graphics.g3d.particles.renderers.ParticleControllerRenderer;
      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.math.collision.BoundingBox;
     12 import com.badlogic.gdx.utils.Array;
     13 import com.badlogic.gdx.utils.Json;
     14 import com.badlogic.gdx.utils.JsonValue;
     15 import com.badlogic.gdx.utils.reflect.ClassReflection;
     16 
     17 /** Base class of all the particle controllers.
     18  * Encapsulate the generic structure of a controller and methods to update the particles simulation.
     19  * @author Inferno */
     20 public class ParticleController implements Json.Serializable, ResourceData.Configurable{
     21 
     22 	/** the default time step used to update the simulation */
     23 	protected static final float DEFAULT_TIME_STEP = 1f/60;
     24 
     25 	/** Name of the controller */
     26 	public String name;
     27 
     28 	/** Controls the emission of the particles */
     29 	public Emitter emitter;
     30 
     31 	/** Update the properties of the particles */
     32 	public Array<Influencer> influencers;
     33 
     34 	/** Controls the graphical representation of the particles */
     35 	public  ParticleControllerRenderer<?, ?> renderer;
     36 
     37 	/** Particles components */
     38 	public ParallelArray particles;
     39 	public ParticleChannels particleChannels;
     40 
     41 	/** Current transform of the controller
     42 	 *	 DO NOT CHANGE MANUALLY */
     43 	public Matrix4 transform;
     44 
     45 	/** Transform flags */
     46 	public Vector3 scale;
     47 
     48 	/** Not used by the simulation, it should represent the bounding box containing all the particles*/
     49 	protected BoundingBox boundingBox;
     50 
     51 	/** Time step, DO NOT CHANGE MANUALLY */
     52 	public float deltaTime, deltaTimeSqr;
     53 
     54 	public ParticleController(){
     55 		transform = new Matrix4();
     56 		scale = new Vector3(1,1,1);
     57 		influencers = new Array<Influencer>(true, 3, Influencer.class);
     58 		setTimeStep(DEFAULT_TIME_STEP);
     59 	}
     60 
     61 	public ParticleController(String name, Emitter emitter, ParticleControllerRenderer<?, ?> renderer, Influencer...influencers){
     62 		this();
     63 		this.name = name;
     64 		this.emitter = emitter;
     65 		this.renderer = renderer;
     66 		this.particleChannels = new ParticleChannels();
     67 		this.influencers = new Array<Influencer>(influencers);
     68 	}
     69 
     70 	/**Sets the delta used to step the simulation */
     71 	private void setTimeStep (float timeStep) {
     72 		deltaTime = timeStep;
     73 		deltaTimeSqr = deltaTime*deltaTime;
     74 	}
     75 
     76 	/** Sets the current transformation to the given one.
     77 	 * @param transform the new transform matrix */
     78 	public void setTransform (Matrix4 transform) {
     79 		this.transform.set(transform);
     80 		transform.getScale(scale);
     81 	}
     82 
     83 	/** Sets the current transformation. */
     84 	public void setTransform(float x, float y, float z, float qx, float qy, float qz, float qw, float scale ){
     85 		transform.set(x, y, z, qx, qy, qz, qw, scale, scale, scale);
     86 		this.scale.set(scale, scale, scale);
     87 	}
     88 
     89 	/** Post-multiplies the current transformation with a rotation matrix represented by the given quaternion.*/
     90 	public void rotate(Quaternion rotation){
     91 		this.transform.rotate(rotation);
     92 	}
     93 
     94 	/** Post-multiplies the current transformation with a rotation matrix by the given angle around the given axis.
     95 	 * @param axis the rotation axis
     96 	 * @param angle the rotation angle in degrees*/
     97 	public void rotate(Vector3 axis, float angle){
     98 		this.transform.rotate(axis, angle);
     99 	}
    100 
    101 	/** Postmultiplies the current transformation with a translation matrix represented by the given translation.*/
    102 	public void translate(Vector3 translation){
    103 		this.transform.translate(translation);
    104 	}
    105 
    106 	public void setTranslation (Vector3 translation) {
    107 		this.transform.setTranslation(translation);
    108 	}
    109 
    110 	/** Postmultiplies the current transformation with a scale matrix represented by the given scale on x,y,z.*/
    111 	public void scale(float scaleX, float scaleY, float scaleZ){
    112 		this.transform.scale(scaleX, scaleY, scaleZ);
    113 		this.transform.getScale(scale);
    114 	}
    115 
    116 	/** Postmultiplies the current transformation with a scale matrix represented by the given scale vector.*/
    117 	public void scale(Vector3 scale){
    118 		scale(scale.x, scale.y, scale.z);
    119 	}
    120 
    121 	/** Postmultiplies the current transformation with the given matrix.*/
    122 	public void mul(Matrix4 transform){
    123 		this.transform.mul(transform);
    124 		this.transform.getScale(scale);
    125 	}
    126 
    127 	/** Set the given matrix to the current transformation matrix.*/
    128 	public void getTransform(Matrix4 transform){
    129 		transform.set(this.transform);
    130 	}
    131 
    132 	public boolean isComplete() {
    133 		return emitter.isComplete();
    134 	}
    135 
    136 	/** Initialize the controller.
    137 	 *  All the sub systems will be initialized and binded to the controller.
    138 	 *  Must be called before any other method. */
    139 	public void init(){
    140 		bind();
    141 		if(particles != null) {
    142 			end();
    143 			particleChannels.resetIds();
    144 		}
    145 		allocateChannels(emitter.maxParticleCount);
    146 
    147 		emitter.init();
    148 		for(Influencer influencer : influencers)
    149 			influencer.init();
    150 		renderer.init();
    151 	}
    152 
    153 	protected void allocateChannels (int maxParticleCount){
    154 		particles = new ParallelArray(maxParticleCount);
    155 		//Alloc additional channels
    156 		emitter.allocateChannels();
    157 		for(Influencer influencer : influencers)
    158 			influencer.allocateChannels();
    159 		renderer.allocateChannels();
    160 	}
    161 
    162 	/** Bind the sub systems to the controller
    163 	 *  Called once during the init phase.*/
    164 	protected void bind(){
    165 		emitter.set(this);
    166 		for(Influencer influencer : influencers)
    167 			influencer.set(this);
    168 		renderer.set(this);
    169 	}
    170 
    171 	/** Start the simulation. */
    172 	public void start () {
    173 		emitter.start();
    174 		for(Influencer influencer : influencers)
    175 			influencer.start();
    176 	}
    177 
    178 	/** Reset the simulation. */
    179 	public void reset(){
    180 		end();
    181 		start();
    182 	}
    183 
    184 	/** End the simulation. */
    185 	public void end () {
    186 		for(Influencer influencer : influencers)
    187 			influencer.end();
    188 		emitter.end();
    189 	}
    190 
    191 	/** Generally called by the Emitter.
    192 	 * This method will notify all the sub systems that a given amount
    193 	 * of particles has been activated. */
    194 	public void activateParticles (int startIndex, int count) {
    195 		emitter.activateParticles(startIndex, count);
    196 		for(Influencer influencer : influencers)
    197 			influencer.activateParticles(startIndex, count);
    198 	}
    199 
    200 	/** Generally called by the Emitter.
    201 	 * This method will notify all the sub systems that a given amount
    202 	 * of particles has been killed. */
    203 	public void killParticles (int startIndex, int count){
    204 		emitter.killParticles(startIndex, count);
    205 		for(Influencer influencer : influencers)
    206 			influencer.killParticles(startIndex, count);
    207 	}
    208 
    209 	/** Updates the particles data */
    210 	public void update(){
    211 		emitter.update();
    212 		for(Influencer influencer : influencers)
    213 			influencer.update();
    214 	}
    215 
    216 	/**Updates the renderer used by this controller, usually this means the particles will be draw inside a batch. */
    217 	public void draw () {
    218 		if(particles.size > 0){
    219 			renderer.update();
    220 		}
    221 	}
    222 
    223 	/** @return a copy of this controller*/
    224 	public ParticleController copy () {
    225 		Emitter emitter = (Emitter)this.emitter.copy();
    226 		Influencer[] influencers = new Influencer[this.influencers.size];
    227 		int i=0;
    228 		for(Influencer influencer : this.influencers){
    229 			influencers[i++] = (Influencer)influencer.copy();
    230 		}
    231 		return new ParticleController(new String(this.name), emitter, (ParticleControllerRenderer<?, ?>)renderer.copy(), influencers);
    232 	}
    233 
    234 	public void dispose(){
    235 		emitter.dispose();
    236 		for(Influencer influencer : influencers)
    237 			influencer.dispose();
    238 	}
    239 
    240 	/** @return a copy of this controller, should be used after the particle effect has been loaded. */
    241 	public BoundingBox getBoundingBox (){
    242 		if(boundingBox == null) boundingBox = new BoundingBox();
    243 		calculateBoundingBox();
    244 		return boundingBox;
    245 	}
    246 
    247 	/** Updates the bounding box using the position channel. */
    248 	protected void calculateBoundingBox () {
    249 		boundingBox.clr();
    250 		FloatChannel positionChannel = particles.getChannel(ParticleChannels.Position);
    251 		for(int pos = 0, c = positionChannel.strideSize*particles.size ; pos < c; pos += positionChannel.strideSize){
    252 			boundingBox.ext(	positionChannel.data[pos + ParticleChannels.XOffset],
    253 												positionChannel.data[pos + ParticleChannels.YOffset],
    254 												positionChannel.data[pos + ParticleChannels.ZOffset]);
    255 		}
    256 	}
    257 
    258 	/** @return the index of the Influencer of the given type. */
    259 	private <K extends Influencer> int findIndex(Class<K> type){
    260 		for(int i = 0; i< influencers.size; ++i){
    261 			Influencer influencer = influencers.get(i);
    262 			if(ClassReflection.isAssignableFrom(type, influencer.getClass())){
    263 				return i;
    264 			}
    265 		}
    266 		return -1;
    267 	}
    268 
    269 	/** @return the influencer having the given type. */
    270 	public <K extends Influencer> K findInfluencer (Class<K> influencerClass) {
    271 		int index = findIndex(influencerClass);
    272 		return index >-1 ? (K)influencers.get(index) : null;
    273 	}
    274 
    275 	/** Removes the Influencer of the given type. */
    276 	public  <K extends Influencer> void removeInfluencer (Class<K> type) {
    277 		int index = findIndex(type);
    278 		if(index > -1 )
    279 			influencers.removeIndex(index);
    280 	}
    281 
    282 	/** Replaces the Influencer of the given type with the one passed as parameter. */
    283 	public <K extends Influencer> boolean replaceInfluencer (Class<K> type, K newInfluencer) {
    284 		int index = findIndex(type);
    285 		if(index > -1){
    286 			influencers.insert(index, newInfluencer);
    287 			influencers.removeIndex(index+1);
    288 			return true;
    289 		}
    290 		return false;
    291 	}
    292 
    293 	@Override
    294 	public void write (Json json) {
    295       json.writeValue("name", name);
    296       json.writeValue("emitter", emitter, Emitter.class);
    297       json.writeValue("influencers", influencers, Array.class, Influencer.class);
    298       json.writeValue("renderer", renderer, ParticleControllerRenderer.class);
    299    }
    300 
    301 	@Override
    302 	public void read (Json json, JsonValue jsonMap) {
    303 		name = json.readValue("name", String.class, jsonMap);
    304 		emitter = json.readValue("emitter", Emitter.class, jsonMap);
    305 		influencers.addAll(json.readValue("influencers", Array.class, Influencer.class, jsonMap));
    306 		renderer = json.readValue("renderer", ParticleControllerRenderer.class, jsonMap);
    307 	}
    308 
    309 	@Override
    310 	public void save (AssetManager manager, ResourceData data) {
    311 		emitter.save(manager, data);
    312 		for(Influencer influencer : influencers)
    313 			influencer.save(manager, data);
    314 		renderer.save(manager, data);
    315 	}
    316 
    317 	@Override
    318 	public void load (AssetManager manager, ResourceData data) {
    319 		emitter.load(manager, data);
    320 		for(Influencer influencer : influencers)
    321 			influencer.load(manager, data);
    322 		renderer.load(manager, data);
    323 	}
    324 }
    325