1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <common/Defines.h>
22 #include <common/VectorLib.h>
23 #include <graph/ParticleEmitter.h>
24 #include <graph/ParticleTypes.h>
25 #include <sprites/DebrisActionRenderer.h>
26 #include <sprites/SmokeActionRenderer.h>
27 #include <sprites/ExplosionTextures.h>
28 #include <sprites/NapalmRenderer.h>
29 #include <sprites/ExplosionNukeRenderer.h>
30 #include <sprites/WallActionRenderer.h>
31 #include <landscape/Landscape.h>
32 #include <math.h>
33 #include <stdlib.h>
34 
randomVector(Vector & result,Vector & start,Vector & end)35 static void randomVector(Vector &result, Vector &start, Vector &end)
36 {
37 	result[0] = start[0] + ((end[0] - start[0]) * RAND);
38 	result[1] = start[1] + ((end[1] - start[1]) * RAND);
39 	result[2] = start[2] + ((end[2] - start[2]) * RAND);
40 }
41 
randomCounterVector(Vector & result,Vector & current,Vector & start,Vector & end,float life)42 static void randomCounterVector(Vector &result, Vector &current,
43 								Vector &start, Vector &end,
44 								float life)
45 {
46 	randomVector(result, start, end);
47 	result -= current;
48 	result /= life;
49 }
50 
randomScalar(float start,float end)51 static float randomScalar(float start, float end)
52 {
53 	return start + ((end - start) * RAND);
54 }
55 
randomCounterScalar(float current,float start,float end,float life)56 static float randomCounterScalar(float current,
57 								float start, float end,
58 								float life)
59 {
60 	float result = randomScalar(start, end);
61 	result -= current;
62 	result /= life;
63 	return result;
64 }
65 
ParticleEmitter()66 ParticleEmitter::ParticleEmitter()
67 {
68 }
69 
~ParticleEmitter()70 ParticleEmitter::~ParticleEmitter()
71 {
72 }
73 
setLife(float life1,float life2)74 void ParticleEmitter::setLife(float life1, float life2)
75 {
76 	life1_ = life1; life2_ = life2;
77 }
78 
setMass(float mass1,float mass2)79 void ParticleEmitter::setMass(float mass1, float mass2)
80 {
81 	mass1_ = mass1; mass2_ = mass2;
82 }
83 
setFriction(float friction1,float friction2)84 void ParticleEmitter::setFriction(float friction1, float friction2)
85 {
86 	friction1_ = friction1; friction2_ = friction2;
87 }
88 
setStartColor(Vector startColor1,float startAlpha1,Vector startColor2,float startAlpha2)89 void ParticleEmitter::setStartColor(
90 		Vector startColor1, float startAlpha1,
91 		Vector startColor2, float startAlpha2)
92 {
93 	startAlpha1_ = startAlpha1; endAlpha1_ = startAlpha1;
94 	startAlpha2_ = startAlpha2; endAlpha2_ = startAlpha2;
95 	startColor1_ = startColor1; endColor1_ = startColor1;
96 	startColor2_ = startColor2; endColor2_ = startColor2;
97 }
98 
setEndColor(Vector endColor1,float endAlpha1,Vector endColor2,float endAlpha2)99 void ParticleEmitter::setEndColor(
100 		Vector endColor1, float endAlpha1,
101 		Vector endColor2, float endAlpha2)
102 {
103 	endColor1_ = endColor1; endAlpha1_ = endAlpha1;
104 	endColor2_ = endColor2; endAlpha2_ = endAlpha2;
105 }
106 
setVelocity(Vector velocity1,Vector velocity2)107 void ParticleEmitter::setVelocity(Vector velocity1, Vector velocity2)
108 {
109 	velocity1_ = velocity1; velocity2_ = velocity2;
110 }
111 
setStartSize(float startX1,float startY1,float startX2,float startY2)112 void ParticleEmitter::setStartSize(
113 		float startX1, float startY1,
114 		float startX2, float startY2)
115 {
116 	startSize1_[0] = startX1; startSize1_[1] = startY1;
117 	startSize2_[0] = startX2; startSize2_[1] = startY2;
118 }
119 
setEndSize(float endX1,float endY1,float endX2,float endY2)120 void ParticleEmitter::setEndSize(
121 		float endX1, float endY1,
122 		float endX2, float endY2)
123 {
124 	endSize1_[0] = endX1; endSize1_[1] = endY1;
125 	endSize2_[0] = endX2; endSize2_[1] = endY2;
126 }
127 
setGravity(Vector gravity)128 void ParticleEmitter::setGravity(Vector gravity)
129 {
130 	gravity_ = gravity;
131 }
132 
setAdditiveTexture(bool additiveTexture)133 void ParticleEmitter::setAdditiveTexture(bool additiveTexture)
134 {
135 	additiveTexture_ = additiveTexture;
136 }
137 
setWindAffect(bool windAffect)138 void ParticleEmitter::setWindAffect(bool windAffect)
139 {
140 	windAffect_ = windAffect;
141 }
142 
setAttributes(float life1,float life2,float mass1,float mass2,float friction1,float friction2,Vector velocity1,Vector velocity2,Vector startColor1,float startAlpha1,Vector startColor2,float startAlpha2,Vector endColor1,float endAlpha1,Vector endColor2,float endAlpha2,float startX1,float startY1,float startX2,float startY2,float endX1,float endY1,float endX2,float endY2,Vector gravity,bool additiveTexture,bool windAffect)143 void ParticleEmitter::setAttributes(
144 	float life1, float life2,
145 	float mass1, float mass2,
146 	float friction1, float friction2,
147 	Vector velocity1, Vector velocity2,
148 	Vector startColor1, float startAlpha1,
149 	Vector startColor2, float startAlpha2,
150 	Vector endColor1, float endAlpha1,
151 	Vector endColor2, float endAlpha2,
152 	float startX1, float startY1,
153 	float startX2, float startY2,
154 	float endX1, float endY1,
155 	float endX2, float endY2,
156 	Vector gravity,
157 	bool additiveTexture,
158 	bool windAffect)
159 {
160 	setLife(life1, life2);
161 	setMass(mass1, mass2);
162 	setFriction(friction1, friction2);
163 	setVelocity(velocity1, velocity2);
164 	setStartColor(startColor1, startAlpha1,
165 		startColor2, startAlpha2);
166 	setEndColor(endColor1, endAlpha1,
167 		endColor2, endAlpha2);
168 	setStartSize(startX1, startY1,
169 		startX2, startY2);
170 	setEndSize(endX1, endY1,
171 		endX2, endY2);
172 	setGravity(gravity);
173 	setAdditiveTexture(additiveTexture);
174 	setWindAffect(windAffect);
175 }
176 
createDefaultParticle(Particle & particle)177 void ParticleEmitter::createDefaultParticle(Particle &particle)
178 {
179 	float life = randomScalar(life1_, life2_);
180 	float mass = randomScalar(mass1_, mass2_);
181 	float friction = randomScalar(friction1_, friction2_);
182 	float alpha = randomScalar(startAlpha1_, startAlpha2_);
183 	float alphac = randomCounterScalar(alpha, endAlpha1_, endAlpha2_, life);
184 
185 	Vector velocity;
186 	randomVector(velocity, velocity1_, velocity2_);
187 	Vector color, colorc;
188 	randomVector(color, startColor1_, startColor2_);
189 	randomCounterVector(colorc, color, endColor1_, endColor2_, life);
190 	Vector size, sizec;
191 	randomVector(size, startSize1_, startSize2_);
192 	randomCounterVector(sizec, size, endSize1_, endSize2_, life);
193 
194 	particle.setParticle(life, mass,
195 		friction, velocity, gravity_,
196 		color, colorc,
197 		size, sizec,
198 		alpha, alphac,
199 		additiveTexture_,
200 		windAffect_);
201 }
202 
emitLinear(int number,Vector & position1,Vector & position2,ParticleEngine & engine,ParticleRenderer * renderer,GLTextureSet * set,bool animate)203 void ParticleEmitter::emitLinear(int number,
204 	Vector &position1, Vector &position2,
205 	ParticleEngine &engine,
206 	ParticleRenderer *renderer,
207 	GLTextureSet *set,
208 	bool animate)
209 {
210 	for (int i=0; i<number; i++)
211 	{
212 		Particle *particle = engine.getNextAliveParticle(ParticleLinear);
213 		if (!particle) return;
214 
215 		Vector position;
216 		randomVector(position, position1, position2);
217 
218 		createDefaultParticle(*particle);
219 		particle->position_ = position;
220 		particle->renderer_ = renderer;
221 		particle->textureCoord_ = int(RAND * 4.0f);
222 		if (animate)
223 		{
224 			particle->textureSet_ = set;
225 		}
226 		else
227 		{
228 			int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
229 			particle->texture_ = set->getTexture(index);
230 		}
231 	}
232 }
233 
emitExplosionRing(int number,Vector & position,Vector & inAxis,ParticleEngine & engine,float width,GLTextureSet * set,bool animate)234 void ParticleEmitter::emitExplosionRing(int number,
235 	Vector &position,
236 	Vector &inAxis,
237 	ParticleEngine &engine,
238 	float width,
239 	GLTextureSet *set,
240 	bool animate)
241 {
242 	for (int i=0; i<number; i++)
243 	{
244 		Particle *particle = engine.getNextAliveParticle(ParticleRing);
245 		if (!particle) return;
246 
247 		createDefaultParticle(*particle);
248 
249 		float ang = RAND * 2.0f * 3.14f;
250 		float speed = width * 4.0f;
251 		Vector axis = inAxis.Normalize();
252 		Vector otheraxis(0.0f, 1.0f, 0.0f);
253 		if (fabs(axis[1]) > 0.7f)
254 		{
255 			otheraxis = Vector(1.0f, 0.0f, 0.0f);
256 		}
257 		Vector p = axis * otheraxis;
258 		Vector velocity = VectorLib::rotationAroundAxis(p, ang, axis) * speed;
259 
260 		particle->velocity_ = velocity;
261 		particle->position_ = position;
262 		particle->renderer_ = ParticleRendererQuads::getInstance();
263 		particle->textureCoord_ = int(RAND * 4.0f);
264 		if (animate)
265 		{
266 			particle->textureSet_ = set;
267 		}
268 		else
269 		{
270 			int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
271 			particle->texture_ = set->getTexture(index);
272 		}
273 	}
274 }
275 
emitDebris(int number,Vector & position,ParticleEngine & engine)276 void ParticleEmitter::emitDebris(int number,
277 	Vector &position,
278 	ParticleEngine &engine)
279 {
280 	for (int i=0; i<number; i++)
281 	{
282 		Particle *particle = engine.getNextAliveParticle(ParticleDebris);
283 		if (!particle) return;
284 
285 		createDefaultParticle(*particle);
286 
287 		float direction = RAND * 3.14f * 2.0f;
288 		float speed = RAND * 25.0f + 5.0f;
289 		float height = RAND * 25.0f + 15.0f;
290 		Vector velocity(getFastSin(direction) * speed,
291 			getFastCos(direction) * speed, height);
292 
293 		if (RAND > 0.5f)
294 		{
295 			DebrisActionRenderer *debris = new DebrisActionRenderer;
296 			particle->velocity_ = velocity;
297 			particle->position_ = position;
298 			particle->renderer_ = ParticleRendererDebris::getInstance();
299 			particle->userData_ = debris;
300 		}
301 		else
302 		{
303 			SmokeActionRenderer *smoke = new SmokeActionRenderer;
304 			particle->velocity_ = velocity;
305 			particle->position_ = position;
306 			particle->renderer_ = ParticleRendererSmoke::getInstance();
307 			particle->userData_ = smoke;
308 		}
309 	}
310 }
311 
emitSmoke(int number,Vector & position,ParticleEngine & engine)312 void ParticleEmitter::emitSmoke(int number,
313 	Vector &position,
314 	ParticleEngine &engine)
315 {
316 	for (int i=0; i<number; i++)
317 	{
318 		Particle *particle = engine.getNextAliveParticle(ParticleSmoke);
319 		if (!particle) return;
320 
321 		createDefaultParticle(*particle);
322 
323 		particle->position_ = position;
324 		particle->renderer_ = ParticleRendererQuads::getInstance();
325 		particle->texture_ = &ExplosionTextures::instance()->smokeTexture;
326 		particle->shadow_ = (RAND > 0.25f);
327 		particle->textureCoord_ = (int) (RAND * 4.0f);
328 	}
329 }
330 
emitNapalm(Vector & position,ParticleEngine & engine,GLTextureSet * set)331 void ParticleEmitter::emitNapalm(
332 	Vector &position,
333 	ParticleEngine &engine,
334 	GLTextureSet *set)
335 {
336 	Particle *particle = engine.getNextAliveParticle(ParticleNapalm);
337 	if (!particle) return;
338 
339 	createDefaultParticle(*particle);
340 
341 	particle->position_ = position;
342 	particle->renderer_ = ParticleRendererNapalm::getInstance();
343 	particle->textureCoord_ = 0;
344 	particle->userData_ = new NapalmRenderer(set);
345 }
346 
emitSpray(Vector & position,ParticleEngine & engine,float width,GLTexture * texture)347 void ParticleEmitter::emitSpray(
348 	Vector &position,
349 	ParticleEngine &engine,
350 	float width,
351 	GLTexture *texture)
352 {
353 	for (int i=0; i<6 + int(width) * 2; i++)
354 	{
355 		Particle *particle = engine.getNextAliveParticle(ParticleSpray);
356 		if (!particle) return;
357 
358 		createDefaultParticle(*particle);
359 
360 		float rotation = RAND * 2.0f * 3.14f;
361 		float x = sinf(rotation);
362 		float y = cosf(rotation);
363 		Vector pos = position;
364 		pos[0] += (x * width * RAND);
365 		pos[1] += (y * width * RAND);
366 		Vector velocity;
367 		velocity[0] = (x * RAND) / 10.0f;
368 		velocity[1] = (y * RAND) / 10.0f;
369 		velocity[2] = 25.0f * RAND + 15.0f;
370 
371 		particle->texture_ = texture;
372 		particle->velocity_ = velocity;
373 		particle->position_ = pos;
374 		particle->renderer_ = ParticleRendererQuads::getInstance();
375 		particle->textureCoord_ = (int) (RAND * 4.0f);
376 	}
377 }
378 
emitTalk(Vector & position,ParticleEngine & engine)379 void ParticleEmitter::emitTalk(
380 	Vector &position,
381 	ParticleEngine &engine)
382 {
383 	Particle *particle = engine.getNextAliveParticle(ParticleTalk);
384 	if (!particle) return;
385 
386 	createDefaultParticle(*particle);
387 
388 	particle->position_ = position;
389 	particle->renderer_ = ParticleRendererQuads::getInstance();
390 	particle->textureCoord_ = 0;
391 	particle->texture_ = &ExplosionTextures::instance()->talkTexture;
392 }
393 
emitWallHit(Vector & position,ParticleEngine & engine,OptionsTransient::WallSide type)394 void ParticleEmitter::emitWallHit(
395 	Vector &position,
396 	ParticleEngine &engine,
397 	OptionsTransient::WallSide type)
398 {
399 	Particle *particle = engine.getNextAliveParticle(ParticleWall);
400 	if (!particle) return;
401 
402 	createDefaultParticle(*particle);
403 
404 	particle->position_ = position;
405 	particle->renderer_ = ParticleRendererWall::getInstance();
406 	particle->userData_ = new WallActionRenderer(position, type);
407 }
408 
emitTransport(Vector & position,ParticleEngine & engine,GLTextureSet * set)409 void ParticleEmitter::emitTransport(
410 	Vector &position,
411 	ParticleEngine &engine,
412 	GLTextureSet *set)
413 {
414 	Particle *particle = engine.getNextAliveParticle(ParticleTransport);
415 	if (!particle) return;
416 
417 	Vector velocity;
418 	createDefaultParticle(*particle);
419 
420 	particle->position_ = position;
421 	particle->renderer_ = ParticleRendererQuads::getInstance();
422 	particle->textureCoord_ = 0;
423 	particle->textureSet_ = set;
424 }
425 
emitExplosion(Vector & position,ParticleEngine & engine,float width,GLTextureSet * set,bool animate)426 void ParticleEmitter::emitExplosion(
427 	Vector &position,
428 	ParticleEngine &engine,
429 	float width,
430 	GLTextureSet *set,
431 	bool animate)
432 {
433 	for (int i=0; i<int(width) * 4; i++)
434 	{
435 		Particle *particle = engine.getNextAliveParticle(ParticleExplosion);
436 		if (!particle) return;
437 
438 		float randRotXY = (RAND * TWOPI);
439 		float randRotXZ = (RAND * TWOPI);
440 		float cosRandRotXZ = (float) cos(randRotXZ);
441 		Vector velocity;
442 		velocity[0] = float(sin(randRotXY) * cosRandRotXZ);
443 		velocity[1] = float(cos(randRotXY) * cosRandRotXZ);
444 		velocity[2] = float(sin(randRotXZ));
445 		velocity *= (width * 2.5f);
446 
447 		float size = RAND * width * 2.0f + 2.0f;
448 
449 		setEndSize(size, size, size, size);
450 
451 		createDefaultParticle(*particle);
452 
453 		particle->velocity_ = velocity;
454 		particle->position_ = position;
455 		particle->renderer_ = ParticleRendererQuads::getInstance();
456 		particle->textureCoord_ = int(RAND * 4.0f);
457 		if (animate)
458 		{
459 			particle->textureSet_ = set;
460 		}
461 		else
462 		{
463 			int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
464 			particle->texture_ = set->getTexture(index);
465 		}
466 	}
467 }
468 
emitMushroom(Vector & position,ParticleEngine & engine,int number,float width,GLTextureSet * set,bool animate)469 void ParticleEmitter::emitMushroom(
470 	Vector &position,
471 	ParticleEngine &engine,
472 	int number,
473 	float width,
474 	GLTextureSet *set,
475 	bool animate)
476 {
477 	for (int i=0; i<number; i++)
478 	{
479 		Particle *particle = engine.getNextAliveParticle(ParticleMushroom);
480 		if (!particle) return;
481 
482 		createDefaultParticle(*particle);
483 
484 		particle->position_ = position;
485 		particle->renderer_ = ParticleRendererMushroom::getInstance();
486 		particle->textureCoord_ = int(RAND * 4.0f);
487 		particle->shadow_ = (RAND > 0.80f);
488 		particle->userData_ = new ExplosionNukeRendererEntry(position, width);
489 
490 		if (animate)
491 		{
492 			particle->textureSet_ = set;
493 		}
494 		else
495 		{
496 			int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
497 			particle->texture_ = set->getTexture(index);
498 		}
499 	}
500 }
501 
emitPrecipitation(Vector & position,ParticleEngine & engine,int number,bool rain)502 void ParticleEmitter::emitPrecipitation(
503 	Vector &position,
504 	ParticleEngine &engine,
505 	int number,
506 	bool rain)
507 {
508 	for (int i=0; i<number; i++)
509 	{
510 		Particle *particle = engine.getNextAliveParticle(ParticlePrecipitation);
511 		if (!particle) return;
512 
513 		createDefaultParticle(*particle);
514 
515 		if (rain)
516 		{
517 			particle->texture_ = &ExplosionTextures::instance()->rainTexture;
518 			particle->renderer_ = ParticleRendererRain::getInstance();
519 		}
520 		else
521 		{
522 			particle->texture_ = &ExplosionTextures::instance()->snowTexture;
523 			particle->renderer_ = ParticleRendererSnow::getInstance();
524 		}
525 		particle->position_[0] = position[0] + RAND * 400.0f - 200.0f;
526 		particle->position_[1] = position[1] + RAND * 400.0f - 200.0f;
527 		particle->position_[2] = 180.0f;
528 		particle->shadow_ = false;
529 	}
530 }
531