1 //
2 // Emitter.cpp - Implementation of particle emitter classes.
3 // Copyright (C) 2006  Nick Gasson
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 
20 #include "Emitter.hpp"
21 #include "OpenGL.hpp"
22 
23 #include <cmath>
24 #include <string>
25 
26 //
27 // Creates a new particle emitter.
28 //	x, y -> Starting position.
29 //	r, g, b -> Base colour components.
30 //	createnew -> New particles will be created on initialisation.
31 //	deviation -> Randomness in particle colours.
32 //	xg, yg -> X and Y components of acceleration.
33 //  life -> Average life of each particle.
34 //	max_speed -> Maximum speed of all particles.
35 //	size -> Size of particles.
36 //	slowdown -> Rate of speed decrease.
37 //
Emitter(int x,int y,float r,float g,float b,bool createnew,float deviation,float xg,float yg,float life,float max_speed,float size,float slowdown)38 Emitter::Emitter(int x, int y, float r, float g, float b, bool createnew,
39                  float deviation, float xg, float yg, float life,
40                  float max_speed, float size, float slowdown)
41   : partsize(size), r(r), g(g), b(b), deviation(deviation), xg(xg), yg(yg),
42     life(life), maxspeed(max_speed), xpos((float)x), ypos((float)y),
43     slowdown(slowdown), createrate(128.0f), xi_bias(0.0f), yi_bias(0.0f),
44     texture_(LoadTexture("images/particle.png"))
45 {
46    // Set up the particles
47    for (int i = 0; i < MAX_PARTICLES; i++) {
48       if (createnew)
49          NewParticle(i);
50       else {
51          particle[i].active = false;
52          particle[i].life = -1.0f;
53       }
54    }
55 }
56 
57 
58 //
59 // Resets the emitter.
60 //
Reset()61 void Emitter::Reset()
62 {
63    for (int i = 0; i < MAX_PARTICLES; i++)	{
64       particle[i].active = false;
65       particle[i].life = -1.0f;
66    }
67 }
68 
69 
70 //
71 // Creates a new cluster of particles at (x, y).
72 //
NewCluster(int x,int y)73 void Emitter::NewCluster(int x, int y)
74 {
75    int i, created=0;
76    float oldx, oldy;
77 
78    oldx = xpos;
79    oldy = ypos;
80    xpos = (float)x;
81    ypos = (float)y;
82 
83    for (i = 0; i < MAX_PARTICLES; i++)	{
84       if ((particle[i].life < 0.0f || !particle[i].active)
85           && created < MAX_PARTICLES/createrate) {
86          NewParticle(i);
87          created++;
88       }
89    }
90 
91    xpos = oldx;
92    ypos = oldy;
93 }
94 
95 
96 //
97 // Draws all the particles created by this emitter.
98 //
Draw(float adjust_x,float adjust_y) const99 void Emitter::Draw(float adjust_x, float adjust_y) const
100 {
101    glEnable(GL_TEXTURE_2D);
102    glEnable(GL_BLEND);
103    glBlendFunc(GL_SRC_ALPHA,GL_ONE);
104    glLoadIdentity();
105 
106    glBindTexture(GL_TEXTURE_2D, texture_->GetGLTexture());
107 
108    for (int i = 0; i < MAX_PARTICLES; i++)	{
109       if (particle[i].active)	{
110          float x = particle[i].x - adjust_x;
111          float y = particle[i].y - adjust_y;
112 
113          glColor4f(particle[i].r, particle[i].g, particle[i].b, particle[i].life);
114          glBegin(GL_TRIANGLE_STRIP);
115          glTexCoord2d(1, 1); glVertex3f(x+partsize, y+partsize, 0);
116          glTexCoord2d(0, 1); glVertex3f(x-partsize, y+partsize, 0);
117          glTexCoord2d(1, 0); glVertex3f(x+partsize, y-partsize, 0);
118          glTexCoord2d(0, 0); glVertex3f(x-partsize, y-partsize, 0);
119          glEnd();
120 
121 
122       }
123    }
124 
125    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
126 }
127 
Process(bool createnew,bool evolve)128 void Emitter::Process(bool createnew, bool evolve)
129 {
130    int created = 0;
131    for (int i = 0; i < MAX_PARTICLES; i++) {
132       if (particle[i].active) {
133          if (evolve)	{
134             // Move particle
135             particle[i].x += particle[i].xi/(slowdown*1000);
136             particle[i].y += particle[i].yi/(slowdown*1000);
137 
138             // Apply gravity
139             particle[i].xi+=particle[i].xg;
140             particle[i].yi+=particle[i].yg;
141 
142             // Fade particle
143             particle[i].life -= particle[i].fade;
144 
145             // Apply special effect
146             ProcessEffect(i);
147 
148             // See if particle died
149             if (particle[i].life < 0.0f
150                 && createnew
151                 && created < MAX_PARTICLES/createrate) {
152                NewParticle(i);
153                created++;
154             }
155             else if (particle[i].life < 0.0f)
156                particle[i].active = false;
157          }
158       }
159       else if (createnew && created < MAX_PARTICLES/createrate)	{
160          NewParticle(i);
161          created++;
162       }
163    }
164 }
165 
166 //
167 // Creates one new particle at the specified index.
168 //
NewParticle(int index)169 void Emitter::NewParticle(int index)
170 {
171    float d[3];
172    int i;
173 
174    for (i = 0; i < 3; i++)	{
175       d[i] = (float)(rand()%100) - 50.0f;
176       d[i] /= 100.0f;
177       d[i] *= deviation;
178    }
179 
180    particle[index].active = true;
181    particle[index].life = life;
182    particle[index].fade = (float)(rand()%100)/1000.0f+0.003f;
183    particle[index].r = r + d[0] >= 1.0f ? 1.0f : r + d[0];
184    particle[index].g = g + d[1] >= 1.0f ? 1.0f : g + d[1];
185    particle[index].b = b + d[2] >= 1.0f ? 1.0f : b + d[2];
186    particle[index].x = xpos;
187    particle[index].y = ypos;
188    particle[index].xg = xg;
189    particle[index].yg = yg;
190 
191    do {
192       particle[index].xi = (float)((rand()%50)-26.0f)*maxspeed;
193       particle[index].yi = (float)((rand()%50)-25.0f)*maxspeed;
194    } while (pow(particle[index].yi, 2) + pow(particle[index].xi, 2) > pow(25.0f*maxspeed, 2));
195 
196    particle[index].xi += xi_bias;
197    particle[index].yi += yi_bias;
198 }
199 
200 
201 //
202 // Smoke trail constructor. Sets special Emitter constants.
203 //
SmokeTrail(float r,float g,float b,float dr,float dg,float db)204 SmokeTrail::SmokeTrail(float r, float g, float b,
205                        float dr, float dg, float db)
206   : Emitter(0, 0, r, g, b,
207             false, 0.2f,
208             0.0f, 0.0f,
209             0.3f, 0.0f, 4.0f, 0.001f),
210     dr(dr), dg(dg), db(db)
211 {
212    createrate = 64.0f;
213 }
214 
BlueSmokeTrail()215 BlueSmokeTrail::BlueSmokeTrail()
216    : SmokeTrail(0.6f, 0.6f, 0.9f, 0.03f, 0.03f, 0.02f)
217 {
218 
219 }
220 
OrangeSmokeTrail()221 OrangeSmokeTrail::OrangeSmokeTrail()
222    : SmokeTrail(0.9f, 0.6f, 0.6f, 0.02f, 0.03f, 0.03f)
223 {
224 
225 }
226 
227 //
228 // Processes the smoke trail effect for particle p.
229 //
ProcessEffect(int p)230 void SmokeTrail::ProcessEffect(int p)
231 {
232    if (particle[p].g > 0.5f)
233       particle[p].g -= 0.025f;
234    else
235       {
236          if (particle[p].r > 0.1f)
237             particle[p].r -= dr;
238          if (particle[p].b > 0.1f)
239             particle[p].b -= db;
240          if (particle[p].g > 0.1f)
241             particle[p].g -= dg;
242       }
243 }
244 
245 
246 //
247 // Explosion constructor. Sets Emitter constants to create a pretty explosion.
248 //
Explosion()249 Explosion::Explosion()
250   : Emitter(0, 0, 0.7f, 0.7f, 0.0f, false, 0.3f, 0.0f, 0.0f, 1.0f, 120.0f, 8.5f, 2.0f)
251 {
252    // Make a BIG explosion
253    createrate = 20.0f;
254 }
255 
256 
257 //
258 // Processes the explosion effect for particle p.
259 //
ProcessEffect(int p)260 void Explosion::ProcessEffect(int p)
261 {
262    if (particle[p].g > 0.5f)
263       particle[p].g -= 0.025f;
264    else {
265       if (particle[p].r > 0.1f)
266          particle[p].r -= 0.025f;
267       if (particle[p].b < 0.1f)
268          particle[p].b += 0.025f;
269       if (particle[p].g > 0.1f)
270          particle[p].g -= 0.025f;
271    }
272 }
273