1 /*
2 Copyright (C) 2007, 2010 - Bit-Blot
3
4 This file is part of Aquaria.
5
6 Aquaria is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program 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.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21 #include "Particles.h"
22
23 ParticleManager *particleManager = 0;
24
25 typedef std::map<std::string, ParticleEffect*> ParticleBank;
26 ParticleBank particleBank;
27
28 std::string ParticleManager::particleBankPath = "";
29
ParticleManager(int size)30 ParticleManager::ParticleManager(int size)
31 {
32 particleManager = this;
33 particles.resize(size);
34 used = 0;
35 free = 0;
36 oldFree = 0;
37
38
39 collideFunction = 0;
40 specialFunction = 0;
41
42 numActive = 0;
43
44 setSize(size);
45 }
46
setSize(int size)47 void ParticleManager::setSize(int size)
48 {
49 // dangerous!
50 for (int i = 0; i < particles.size(); i++)
51 {
52 Particle *p = &particles[i];
53 if (p->emitter)
54 {
55 p->emitter->removeParticle(p);
56 }
57 }
58
59 particles.clear();
60
61 particles.resize(size);
62
63 this->size = size;
64 this->halfSize = size*0.5f;
65
66 free = oldFree = 0;
67 }
68
setNumSuckPositions(int num)69 void ParticleManager::setNumSuckPositions(int num)
70 {
71 suckPositions.resize(num);
72 }
73
setSuckPosition(int idx,const Vector & pos)74 void ParticleManager::setSuckPosition(int idx, const Vector &pos)
75 {
76 if (idx < 0 || idx >= suckPositions.size()) return;
77 suckPositions[idx] = pos;
78 }
79
getSuckPosition(int idx)80 Vector *ParticleManager::getSuckPosition(int idx)
81 {
82 if (idx < 0 || idx >= suckPositions.size()) return 0;
83 return &suckPositions[idx];
84 }
85
updateParticle(Particle * p,float dt)86 void ParticleManager::updateParticle(Particle *p, float dt)
87 {
88 if (!p->active) return;
89 numActive++;
90 if (p->emitter && p->emitter->data.pauseLevel < core->particlesPaused)
91 return;
92
93 p->color.update(dt);
94 p->alpha.update(dt);
95 p->scale.update(dt);
96 p->rot.update(dt);
97 p->pos += p->vel * dt;
98 p->life = p->life - dt;
99 p->vel += p->gvy * dt;
100
101 if (p->emitter)
102 {
103 if (p->emitter->data.influenced)
104 {
105 ParticleInfluence *pinf=0;
106
107 if (collideFunction)
108 {
109 if (collideFunction(p->pos))
110 {
111 const bool bounce = false;
112 if (bounce)
113 {
114 p->pos = p->lpos;
115 p->vel = -p->vel;
116 }
117 else
118 {
119 // fade out
120 p->vel = 0;
121 endParticle(p);
122 return;
123 }
124 }
125 }
126
127 if (specialFunction)
128 {
129 specialFunction(p);
130 }
131 p->lpos = p->pos;
132 Influences::iterator i = influences.begin();
133 for (; i != influences.end(); i++)
134 {
135
136 pinf = &(*i);
137 Vector pos = p->pos;
138 //HACK: what? ->
139 if (p->emitter->data.spawnLocal && p->emitter->getParent())
140 pos += p->emitter->getParent()->position;
141 if ((pinf->pos - pos).isLength2DIn(pinf->size + p->emitter->data.influenced))
142 {
143 Vector dir = pos - pinf->pos;
144 dir.setLength2D(pinf->spd);
145 if (!pinf->pull)
146 p->vel += dir * dt;
147 else
148 p->vel -= dir * dt;
149 }
150 }
151 }
152 if (p->emitter->data.suckIndex > -1)
153 {
154 Vector *suckPos = getSuckPosition(p->emitter->data.suckIndex);
155 if (suckPos)
156 {
157 Vector dir = (*suckPos) - p->emitter->getWorldCollidePosition(p->pos);
158 //HACK: what? ->
159 if (!p->emitter->data.spawnLocal && p->emitter->getParent())
160 dir += p->emitter->getParent()->position;
161 dir.setLength2D(p->emitter->data.suckStr);
162 p->vel += dir * dt;
163 }
164 }
165 if (p->rot.z != 0 || p->rot.isInterpolating())
166 p->emitter->hasRot = true;
167 }
168
169 p->lpos = p->pos;
170
171
172 if (p->life <= 0)
173 {
174 endParticle(p);
175 }
176 }
177
endParticle(Particle * p)178 void ParticleManager::endParticle(Particle *p)
179 {
180 if (!p) return;
181
182 if (p->emitter)
183 {
184 if (!p->emitter->data.deathPrt.empty())
185 {
186 RenderObject *r = p->emitter->getTopParent();
187 if (r)
188 core->createParticleEffect(p->emitter->data.deathPrt, p->pos, r->layer, p->rot.z);
189 }
190 p->emitter->removeParticle(p);
191 }
192 if (p->index != -1)
193 {
194 /*
195 // set free if the neighbours are also free
196 int backupFree = free;
197 int oldFree = p->index;
198 free = oldFree;
199 nextFree();
200 if (!particles[free].active)
201 {
202 free = oldFree;
203 prevFree();
204 if (!particles[free].active)
205 {
206 // good to go!
207 }
208 else
209 {
210 free = backupFree;
211 }
212 }
213 else
214 {
215 free = backupFree;
216 }
217 */
218 //if (p->index > free)
219 //{
220 //free = p->index;
221 //}
222 }
223 p->reset();
224 }
225
nextFree(int jump)226 void ParticleManager::nextFree(int jump)
227 {
228 free+=jump;
229 if (free >= size)
230 free -= size;
231 }
232
prevFree(int jump)233 void ParticleManager::prevFree(int jump)
234 {
235 free -= jump;
236 if (free < 0)
237 free += size;
238 }
239
setFree(int free)240 void ParticleManager::setFree(int free)
241 {
242 if (free != -1)
243 {
244 this->free = free;
245 }
246 }
247
248 const int spread = 8;
249 const int spreadCheck = 128;
250
251 // travel the list until you find an empty or give up
stomp()252 Particle *ParticleManager::stomp()
253 {
254 int c = 0, idx = -1;
255 //int bFree = free;
256 Particle *p = 0;
257 bool exceed = false;
258
259
260 nextFree();
261 do
262 {
263 if (c >= spreadCheck)
264 {
265 exceed = true;
266 break;
267 }
268
269 p = &particles[free];
270 idx = free;
271 nextFree();
272 c++;
273 }
274 while (p->active);
275
276 /*
277 int nFree = free;
278 int pFree = free;
279
280 nextFree();
281 nFree = free;
282
283 free = bFree;
284 prevFree();
285 pFree = free;
286
287 do
288 {
289 free = nFree;
290 p = &particles[free];
291 idx = free;
292 nextFree();
293 nFree = free;
294
295 if (p->active)
296 {
297 free = pFree;
298 p = &particles[free];
299 idx = free;
300 prevFree();
301 pFree = free;
302 }
303 c++;
304 if (c >= spreadCheck)
305 {
306 exceed = true;
307 break;
308 }
309 }
310 while (p->active);
311 */
312 /*
313 if (p->active)
314 {
315 c = 0;
316 free = backupFree;
317 nextFree();
318 do
319 {
320 p = &particles[free];
321 idx = free;
322 nextFree();
323 c++;
324 if (c >= 8)
325 {
326 exceed = true;
327 break;
328 }
329 }
330 while (p->active);
331 }
332 */
333
334 if (exceed)
335 {
336 //debugLog("EXCEEDED");
337 }
338
339 endParticle(p);
340 p->index = idx;
341 return p;
342 }
343
344 /*
345 const int FREELISTSIZE = 32;
346
347 void ParticleManager::getFreeList(int list)
348 {
349 for (int i = 0; i < FREELISTSIZE; i++)
350 {
351 if (freeList[curList] != -1)
352 {
353 Particle *p = &particles[freeList[curList]];
354 freeList[curList] = -1;
355 return p;
356 }
357 }
358 return 0;
359 }
360
361 void ParticleManager::addFreeList()
362 {
363 }
364 */
365
getFreeParticle(Emitter * emitter)366 Particle *ParticleManager::getFreeParticle(Emitter *emitter)
367 {
368 BBGE_PROF(ParticleManager_getFreeParticle);
369 if (size == 0) return 0;
370
371 Particle *p = 0;
372
373 p = &particles[free];
374
375 if (p->active)
376 {
377 p = stomp();
378 }
379 else
380 {
381 endParticle(p);
382 p->index = free;
383 nextFree(spread);
384 }
385
386 /*
387 static int lpstep = 0;
388 if (c > lpstep)
389 lpstep = c;
390 std::ostringstream os;
391 os << "psteps: " << c << " largest: " << lpstep;
392 debugLog(os.str());
393 */
394
395
396 /*
397 p = &particles[free];
398 nextFree();
399 */
400
401
402
403 if (emitter)
404 {
405 p->emitter = emitter;
406
407 emitter->addParticle(p);
408 }
409
410 return p;
411 }
412
loadParticleCallback(const std::string & filename,intptr_t param)413 void loadParticleCallback(const std::string &filename, intptr_t param)
414 {
415 ParticleEffect *e = new ParticleEffect();
416
417 std::string ident;
418 int first = filename.find_last_of('/')+1;
419 ident = filename.substr(first, filename.find_last_of('.')-first);
420 stringToLower(ident);
421
422 e->bankLoad(ident, ParticleManager::particleBankPath);
423
424 particleBank[ident] = e;
425 }
426
loadParticleBank(const std::string & bank1,const std::string & bank2)427 void ParticleManager::loadParticleBank(const std::string &bank1, const std::string &bank2)
428 {
429 clearParticleBank();
430
431 particleBankPath = bank1;
432 forEachFile(bank1, ".txt", loadParticleCallback, 0);
433
434 if (!bank2.empty())
435 {
436 particleBankPath = bank2;
437 forEachFile(bank2, ".txt", loadParticleCallback, 0);
438 }
439
440 particleBankPath = "";
441 }
442
loadParticleEffectFromBank(const std::string & name,ParticleEffect * load)443 void ParticleManager::loadParticleEffectFromBank(const std::string &name, ParticleEffect *load)
444 {
445 std::string realName = name;
446 stringToLower(realName);
447 ParticleEffect *fx = particleBank[realName];
448
449 if (fx)
450 fx->transfer(load);
451 else
452 {
453 std::ostringstream os;
454 os << "Did not find PE [" << name << "] in particle bank";
455 debugLog(os.str());
456 }
457 }
458
clearParticleBank()459 void ParticleManager::clearParticleBank()
460 {
461 for (ParticleBank::iterator i = particleBank.begin(); i != particleBank.end(); i++)
462 {
463 ParticleEffect *e = (*i).second;
464 if (e)
465 {
466 e->destroy();
467 delete e;
468 }
469 }
470 particleBank.clear();
471 }
472
getSize()473 int ParticleManager::getSize()
474 {
475 return size;
476 }
477
update(float dt)478 void ParticleManager::update(float dt)
479 {
480 BBGE_PROF(ParticleManager_update);
481 numActive = 0;
482 for (int i = 0; i < particles.size(); i++)
483 {
484 if (particles[i].active)
485 {
486 updateParticle(&particles[i], dt);
487 }
488 }
489 }
490
clearInfluences()491 void ParticleManager::clearInfluences()
492 {
493 influences.clear();
494 }
495
addInfluence(ParticleInfluence inf)496 void ParticleManager::addInfluence(ParticleInfluence inf)
497 {
498 influences.push_back(inf);
499 }
500
501