1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-12-27 07:06:00 +00:00
Aquaria/BBGE/ParticleManager.cpp

500 lines
8.3 KiB
C++

/*
Copyright (C) 2007, 2010 - Bit-Blot
This file is part of Aquaria.
Aquaria is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "Particles.h"
ParticleManager *particleManager = 0;
typedef std::map<std::string, ParticleEffect*> ParticleBank;
ParticleBank particleBank;
std::string ParticleManager::particleBankPath = "";
ParticleManager::ParticleManager(int size)
{
particleManager = this;
particles.resize(size);
used = 0;
free = 0;
oldFree = 0;
collideFunction = 0;
specialFunction = 0;
numActive = 0;
setSize(size);
}
void ParticleManager::setSize(int size)
{
// dangerous!
for (int i = 0; i < particles.size(); i++)
{
Particle *p = &particles[i];
if (p->emitter)
{
p->emitter->removeParticle(p);
}
}
particles.clear();
particles.resize(size);
this->size = size;
this->halfSize = size*0.5f;
free = oldFree = 0;
}
void ParticleManager::setNumSuckPositions(int num)
{
suckPositions.resize(num);
}
void ParticleManager::setSuckPosition(int idx, const Vector &pos)
{
if (idx < 0 || idx >= suckPositions.size()) return;
suckPositions[idx] = pos;
}
Vector *ParticleManager::getSuckPosition(int idx)
{
if (idx < 0 || idx >= suckPositions.size()) return 0;
return &suckPositions[idx];
}
void ParticleManager::updateParticle(Particle *p, float dt)
{
if (!p->active) return;
numActive++;
if (p->emitter && p->emitter->data.pauseLevel < core->particlesPaused)
return;
p->color.update(dt);
p->alpha.update(dt);
p->scale.update(dt);
p->rot.update(dt);
p->pos += p->vel * dt;
p->life = p->life - dt;
p->vel += p->gvy * dt;
if (p->emitter)
{
if (p->emitter->data.influenced)
{
ParticleInfluence *pinf=0;
if (collideFunction)
{
if (collideFunction(p->pos))
{
const bool bounce = false;
if (bounce)
{
p->pos = p->lpos;
p->vel = -p->vel;
}
else
{
// fade out
p->vel = 0;
endParticle(p);
return;
}
}
}
if (specialFunction)
{
specialFunction(p);
}
p->lpos = p->pos;
Influences::iterator i = influences.begin();
for (; i != influences.end(); i++)
{
pinf = &(*i);
Vector pos = p->pos;
//HACK: what? ->
if (p->emitter->data.spawnLocal && p->emitter->getParent())
pos += p->emitter->getParent()->position;
if ((pinf->pos - pos).isLength2DIn(pinf->size + p->emitter->data.influenced))
{
Vector dir = pos - pinf->pos;
dir.setLength2D(pinf->spd);
if (!pinf->pull)
p->vel += dir * dt;
else
p->vel -= dir * dt;
}
}
}
if (p->emitter->data.suckIndex > -1)
{
Vector *suckPos = getSuckPosition(p->emitter->data.suckIndex);
if (suckPos)
{
Vector dir = (*suckPos) - p->emitter->getWorldCollidePosition(p->pos);
//HACK: what? ->
if (!p->emitter->data.spawnLocal && p->emitter->getParent())
dir += p->emitter->getParent()->position;
dir.setLength2D(p->emitter->data.suckStr);
p->vel += dir * dt;
}
}
if (p->rot.z != 0 || p->rot.isInterpolating())
p->emitter->hasRot = true;
}
p->lpos = p->pos;
if (p->life <= 0)
{
endParticle(p);
}
}
void ParticleManager::endParticle(Particle *p)
{
if (!p) return;
if (p->emitter)
{
if (!p->emitter->data.deathPrt.empty())
{
RenderObject *r = p->emitter->getTopParent();
if (r)
core->createParticleEffect(p->emitter->data.deathPrt, p->pos, r->layer, p->rot.z);
}
p->emitter->removeParticle(p);
}
if (p->index != -1)
{
/*
// set free if the neighbours are also free
int backupFree = free;
int oldFree = p->index;
free = oldFree;
nextFree();
if (!particles[free].active)
{
free = oldFree;
prevFree();
if (!particles[free].active)
{
// good to go!
}
else
{
free = backupFree;
}
}
else
{
free = backupFree;
}
*/
//if (p->index > free)
//{
//free = p->index;
//}
}
p->reset();
}
void ParticleManager::nextFree(int jump)
{
free+=jump;
if (free >= size)
free -= size;
}
void ParticleManager::prevFree(int jump)
{
free -= jump;
if (free < 0)
free += size;
}
void ParticleManager::setFree(int free)
{
if (free != -1)
{
this->free = free;
}
}
const int spread = 8;
const int spreadCheck = 128;
// travel the list until you find an empty or give up
Particle *ParticleManager::stomp()
{
int c = 0, idx = -1;
//int bFree = free;
Particle *p = 0;
bool exceed = false;
nextFree();
do
{
if (c >= spreadCheck)
{
exceed = true;
break;
}
p = &particles[free];
idx = free;
nextFree();
c++;
}
while (p->active);
/*
int nFree = free;
int pFree = free;
nextFree();
nFree = free;
free = bFree;
prevFree();
pFree = free;
do
{
free = nFree;
p = &particles[free];
idx = free;
nextFree();
nFree = free;
if (p->active)
{
free = pFree;
p = &particles[free];
idx = free;
prevFree();
pFree = free;
}
c++;
if (c >= spreadCheck)
{
exceed = true;
break;
}
}
while (p->active);
*/
/*
if (p->active)
{
c = 0;
free = backupFree;
nextFree();
do
{
p = &particles[free];
idx = free;
nextFree();
c++;
if (c >= 8)
{
exceed = true;
break;
}
}
while (p->active);
}
*/
if (exceed)
{
//debugLog("EXCEEDED");
}
endParticle(p);
p->index = idx;
return p;
}
/*
const int FREELISTSIZE = 32;
void ParticleManager::getFreeList(int list)
{
for (int i = 0; i < FREELISTSIZE; i++)
{
if (freeList[curList] != -1)
{
Particle *p = &particles[freeList[curList]];
freeList[curList] = -1;
return p;
}
}
return 0;
}
void ParticleManager::addFreeList()
{
}
*/
Particle *ParticleManager::getFreeParticle(Emitter *emitter)
{
BBGE_PROF(ParticleManager_getFreeParticle);
if (size == 0) return 0;
Particle *p = 0;
p = &particles[free];
if (p->active)
{
p = stomp();
}
else
{
endParticle(p);
p->index = free;
nextFree(spread);
}
/*
static int lpstep = 0;
if (c > lpstep)
lpstep = c;
std::ostringstream os;
os << "psteps: " << c << " largest: " << lpstep;
debugLog(os.str());
*/
/*
p = &particles[free];
nextFree();
*/
if (emitter)
{
p->emitter = emitter;
emitter->addParticle(p);
}
return p;
}
void loadParticleCallback(const std::string &filename, intptr_t param)
{
ParticleEffect *e = new ParticleEffect();
std::string ident;
int first = filename.find_last_of('/')+1;
ident = filename.substr(first, filename.find_last_of('.')-first);
stringToLower(ident);
e->bankLoad(ident, ParticleManager::particleBankPath);
particleBank[ident] = e;
}
void ParticleManager::loadParticleBank(const std::string &bank1, const std::string &bank2)
{
clearParticleBank();
particleBankPath = bank1;
forEachFile(bank1, ".txt", loadParticleCallback, 0);
if (!bank2.empty())
{
particleBankPath = bank2;
forEachFile(bank2, ".txt", loadParticleCallback, 0);
}
particleBankPath = "";
}
void ParticleManager::loadParticleEffectFromBank(const std::string &name, ParticleEffect *load)
{
std::string realName = name;
stringToLower(realName);
ParticleEffect *fx = particleBank[realName];
if (fx)
fx->transfer(load);
else
{
std::ostringstream os;
os << "Did not find PE [" << name << "] in particle bank";
debugLog(os.str());
}
}
void ParticleManager::clearParticleBank()
{
for (ParticleBank::iterator i = particleBank.begin(); i != particleBank.end(); i++)
{
ParticleEffect *e = (*i).second;
if (e)
{
e->destroy();
delete e;
}
}
particleBank.clear();
}
int ParticleManager::getSize()
{
return size;
}
void ParticleManager::update(float dt)
{
BBGE_PROF(ParticleManager_update);
numActive = 0;
for (int i = 0; i < particles.size(); i++)
{
if (particles[i].active)
{
updateParticle(&particles[i], dt);
}
}
}
void ParticleManager::clearInfluences()
{
influences.clear();
}
void ParticleManager::addInfluence(ParticleInfluence inf)
{
influences.push_back(inf);
}