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