mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-12-26 14:45:48 +00:00
500 lines
8.3 KiB
C++
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);
|
|
}
|
|
|