• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ParticleController()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 
ParticleController(String name, Emitter emitter, ParticleControllerRenderer<?, ?> renderer, Influencer...influencers)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 */
setTimeStep(float timeStep)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 */
setTransform(Matrix4 transform)78 	public void setTransform (Matrix4 transform) {
79 		this.transform.set(transform);
80 		transform.getScale(scale);
81 	}
82 
83 	/** Sets the current transformation. */
setTransform(float x, float y, float z, float qx, float qy, float qz, float qw, float scale )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.*/
rotate(Quaternion rotation)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*/
rotate(Vector3 axis, float angle)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.*/
translate(Vector3 translation)102 	public void translate(Vector3 translation){
103 		this.transform.translate(translation);
104 	}
105 
setTranslation(Vector3 translation)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.*/
scale(float scaleX, float scaleY, float scaleZ)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.*/
scale(Vector3 scale)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.*/
mul(Matrix4 transform)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.*/
getTransform(Matrix4 transform)128 	public void getTransform(Matrix4 transform){
129 		transform.set(this.transform);
130 	}
131 
isComplete()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. */
init()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 
allocateChannels(int maxParticleCount)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.*/
bind()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. */
start()172 	public void start () {
173 		emitter.start();
174 		for(Influencer influencer : influencers)
175 			influencer.start();
176 	}
177 
178 	/** Reset the simulation. */
reset()179 	public void reset(){
180 		end();
181 		start();
182 	}
183 
184 	/** End the simulation. */
end()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. */
activateParticles(int startIndex, int count)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. */
killParticles(int startIndex, int count)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 */
update()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. */
draw()217 	public void draw () {
218 		if(particles.size > 0){
219 			renderer.update();
220 		}
221 	}
222 
223 	/** @return a copy of this controller*/
copy()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 
dispose()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. */
getBoundingBox()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. */
calculateBoundingBox()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. */
findIndex(Class<K> 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. */
findInfluencer(Class<K> influencerClass)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. */
removeInfluencer(Class<K> 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. */
replaceInfluencer(Class<K> type, K newInfluencer)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
write(Json json)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
read(Json json, JsonValue jsonMap)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
save(AssetManager manager, ResourceData data)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
load(AssetManager manager, ResourceData data)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