123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /**
- * Copyright (c) 2006-2012 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
- #include "ParticleSystem.h"
- #include "common/math.h"
- #include "GLee.h"
- #include <cmath>
- #include <cstdlib>
- namespace love
- {
- namespace graphics
- {
- namespace opengl
- {
- namespace
- {
- Colorf colorToFloat(const Color &c)
- {
- return Colorf((GLfloat)c.r/255.0f, (GLfloat)c.g/255.0f, (GLfloat)c.b/255.0f, (GLfloat)c.a/255.0f);
- }
- }
- float calculate_variation(float inner, float outer, float var)
- {
- float low = inner - (outer/2.0f)*var;
- float high = inner + (outer/2.0f)*var;
- float r = random();
- return low*(1-r)+high*r;
- }
- StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] = {
- { "none", ParticleSystem::DISTRIBUTION_NONE },
- { "uniform", ParticleSystem::DISTRIBUTION_UNIFORM },
- { "normal", ParticleSystem::DISTRIBUTION_NORMAL },
- };
- StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
- ParticleSystem::ParticleSystem(Image *sprite, unsigned int buffer)
- : pStart(0)
- , pLast(0)
- , pEnd(0)
- , active(true)
- , emissionRate(0)
- , emitCounter(0)
- , areaSpreadDistribution(DISTRIBUTION_NONE)
- , lifetime(-1)
- , life(0)
- , particleLifeMin(0)
- , particleLifeMax(0)
- , direction(0)
- , spread(0)
- , relative(false)
- , speedMin(0)
- , speedMax(0)
- , gravityMin(0)
- , gravityMax(0)
- , radialAccelerationMin(0)
- , radialAccelerationMax(0)
- , tangentialAccelerationMin(0)
- , tangentialAccelerationMax(0)
- , sizeVariation(0)
- , rotationMin(0)
- , rotationMax(0)
- , spinStart(0)
- , spinEnd(0)
- , spinVariation(0)
- , offsetX(sprite->getWidth()*0.5f)
- , offsetY(sprite->getHeight()*0.5f)
- {
- this->sprite = sprite;
- sprite->retain();
- sizes.push_back(1.0f);
- colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
- setBufferSize(buffer);
- }
- ParticleSystem::~ParticleSystem()
- {
- if (this->sprite != 0)
- this->sprite->release();
- if (pStart != 0)
- delete [] pStart;
- }
- void ParticleSystem::add()
- {
- if (isFull()) return;
- float min,max;
- min = particleLifeMin;
- max = particleLifeMax;
- if (min == max)
- pLast->life = min;
- else
- pLast->life = random(min, max);
- pLast->lifetime = pLast->life;
- pLast->position[0] = position.getX();
- pLast->position[1] = position.getY();
- switch (areaSpreadDistribution)
- {
- case DISTRIBUTION_UNIFORM:
- pLast->position[0] += random(-areaSpread.getX(), areaSpread.getX());
- pLast->position[1] += random(-areaSpread.getY(), areaSpread.getY());
- break;
- case DISTRIBUTION_NORMAL:
- pLast->position[0] += random_normal(areaSpread.getX());
- pLast->position[1] += random_normal(areaSpread.getY());
- break;
- case DISTRIBUTION_NONE:
- default:
- break;
- }
- min = direction - spread/2.0f;
- max = direction + spread/2.0f;
- pLast->direction = random(min, max);
- pLast->origin = position;
- min = speedMin;
- max = speedMax;
- float speed = random(min, max);
- pLast->speed = love::Vector(cos(pLast->direction), sin(pLast->direction));
- pLast->speed *= speed;
- min = gravityMin;
- max = gravityMax;
- pLast->gravity = random(min, max);
- min = radialAccelerationMin;
- max = radialAccelerationMax;
- pLast->radialAcceleration = random(min, max);
- min = tangentialAccelerationMin;
- max = tangentialAccelerationMax;
- pLast->tangentialAcceleration = random(min, max);
- pLast->sizeOffset = random(sizeVariation); // time offset for size change
- pLast->sizeIntervalSize = (1.0f - random(sizeVariation)) - pLast->sizeOffset;
- pLast->size = sizes[(size_t)(pLast->sizeOffset - .5f) * (sizes.size() - 1)];
- min = rotationMin;
- max = rotationMax;
- pLast->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
- pLast->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
- pLast->rotation = random(min, max);
- pLast->color = colors[0];
- pLast++;
- }
- void ParticleSystem::remove(particle *p)
- {
- if (!isEmpty())
- {
- *p = *(--pLast);
- }
- }
- void ParticleSystem::setSprite(Image *image)
- {
- if (sprite != 0)
- sprite->release();
- sprite = image;
- sprite->retain();
- }
- void ParticleSystem::setBufferSize(unsigned int size)
- {
- // delete previous data
- delete [] pStart;
- pLast = pStart = new particle[size];
- pEnd = pStart + size;
- }
- void ParticleSystem::setEmissionRate(int rate)
- {
- emissionRate = rate;
- }
- void ParticleSystem::setLifetime(float life)
- {
- this->life = lifetime = life;
- }
- void ParticleSystem::setParticleLife(float min, float max)
- {
- particleLifeMin = min;
- if (max == 0)
- particleLifeMax = min;
- else
- particleLifeMax = max;
- }
- void ParticleSystem::setPosition(float x, float y)
- {
- position = love::Vector(x, y);
- }
- void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
- {
- areaSpread = love::Vector(x, y);
- areaSpreadDistribution = distribution;
- }
- void ParticleSystem::setDirection(float direction)
- {
- this->direction = direction;
- }
- void ParticleSystem::setSpread(float spread)
- {
- this->spread = spread;
- }
- void ParticleSystem::setRelativeDirection(bool relative)
- {
- this->relative = relative;
- }
- void ParticleSystem::setSpeed(float speed)
- {
- speedMin = speedMax = speed;
- }
- void ParticleSystem::setSpeed(float min, float max)
- {
- speedMin = min;
- speedMax = max;
- }
- void ParticleSystem::setGravity(float gravity)
- {
- gravityMin = gravityMax = gravity;
- }
- void ParticleSystem::setGravity(float min, float max)
- {
- gravityMin = min;
- gravityMax = max;
- }
- void ParticleSystem::setRadialAcceleration(float acceleration)
- {
- radialAccelerationMin = radialAccelerationMax = acceleration;
- }
- void ParticleSystem::setRadialAcceleration(float min, float max)
- {
- radialAccelerationMin = min;
- radialAccelerationMax = max;
- }
- void ParticleSystem::setTangentialAcceleration(float acceleration)
- {
- tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
- }
- void ParticleSystem::setTangentialAcceleration(float min, float max)
- {
- tangentialAccelerationMin = min;
- tangentialAccelerationMax = max;
- }
- void ParticleSystem::setSize(float size)
- {
- sizes.resize(1);
- sizes[0] = size;
- }
- void ParticleSystem::setSize(const std::vector<float> &newSizes, float variation)
- {
- sizes = newSizes;
- sizeVariation = variation;
- }
- void ParticleSystem::setSizeVariation(float variation)
- {
- sizeVariation = variation;
- }
- void ParticleSystem::setRotation(float rotation)
- {
- rotationMin = rotationMax = rotation;
- }
- void ParticleSystem::setRotation(float min, float max)
- {
- rotationMin = min;
- rotationMax = max;
- }
- void ParticleSystem::setSpin(float spin)
- {
- spinStart = spin;
- spinEnd = spin;
- }
- void ParticleSystem::setSpin(float start, float end)
- {
- spinStart = start;
- spinEnd = end;
- }
- void ParticleSystem::setSpin(float start, float end, float variation)
- {
- spinStart = start;
- spinEnd = end;
- spinVariation = variation;
- }
- void ParticleSystem::setSpinVariation(float variation)
- {
- spinVariation = variation;
- }
- void ParticleSystem::setColor(const Color &color)
- {
- colors.resize(1);
- colors[0] = colorToFloat(color);
- }
- void ParticleSystem::setColor(const std::vector<Color> &newColors)
- {
- colors.resize(newColors.size());
- for (size_t i = 0; i < newColors.size(); ++i)
- colors[i] = colorToFloat(newColors[i]);
- }
- void ParticleSystem::setOffset(float x, float y)
- {
- offsetX = x;
- offsetY = y;
- }
- float ParticleSystem::getX() const
- {
- return position.getX();
- }
- float ParticleSystem::getY() const
- {
- return position.getY();
- }
- const love::Vector &ParticleSystem::getPosition() const
- {
- return position;
- }
- ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
- {
- return areaSpreadDistribution;
- }
- const love::Vector &ParticleSystem::getAreaSpreadParameters() const
- {
- return areaSpread;
- }
- float ParticleSystem::getDirection() const
- {
- return direction;
- }
- float ParticleSystem::getSpread() const
- {
- return spread;
- }
- float ParticleSystem::getOffsetX() const
- {
- return offsetX;
- }
- float ParticleSystem::getOffsetY() const
- {
- return offsetY;
- }
- int ParticleSystem::count() const
- {
- return (int)(pLast - pStart);
- }
- void ParticleSystem::start()
- {
- active = true;
- }
- void ParticleSystem::stop()
- {
- active = false;
- life = lifetime;
- emitCounter = 0;
- }
- void ParticleSystem::pause()
- {
- active = false;
- }
- void ParticleSystem::reset()
- {
- pLast = pStart;
- life = lifetime;
- emitCounter = 0;
- }
- bool ParticleSystem::isActive() const
- {
- return active;
- }
- bool ParticleSystem::isEmpty() const
- {
- return pStart == pLast;
- }
- bool ParticleSystem::isFull() const
- {
- return pLast == pEnd;
- }
- void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
- {
- if (sprite == 0) return; // just in case of failure
- glPushMatrix();
- glPushAttrib(GL_CURRENT_BIT);
- Matrix t;
- t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
- glMultMatrixf((const GLfloat *)t.getElements());
- particle *p = pStart;
- while (p != pLast)
- {
- glPushMatrix();
- glColor4f(p->color.r, p->color.g, p->color.b, p->color.a);
- sprite->draw(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
- glPopMatrix();
- p++;
- }
- glPopAttrib();
- glPopMatrix();
- }
- void ParticleSystem::update(float dt)
- {
- // Traverse all particles and update.
- particle *p = pStart;
- // Make some more particles.
- if (active)
- {
- float rate = 1.0f / emissionRate; // the amount of time between each particle emit
- emitCounter += dt;
- while (emitCounter > rate)
- {
- add();
- emitCounter -= rate;
- }
- /*int particles = (int)(emissionRate * dt);
- for (int i = 0; i != particles; i++)
- add();*/
- life -= dt;
- if (lifetime != -1 && life < 0)
- stop();
- }
- while (p != pLast)
- {
- // Decrease lifespan.
- p->life -= dt;
- if (p->life > 0)
- {
- // Temp variables.
- love::Vector radial, tangential, gravity(0, p->gravity);
- love::Vector ppos(p->position[0], p->position[1]);
- // Get vector from particle center to particle.
- radial = ppos - p->origin;
- radial.normalize();
- tangential = radial;
- // Resize radial acceleration.
- radial *= p->radialAcceleration;
- // Calculate tangential acceleration.
- {
- float a = tangential.getX();
- tangential.setX(-tangential.getY());
- tangential.setY(a);
- }
- // Resize tangential.
- tangential *= p->tangentialAcceleration;
- // Update position.
- p->speed += (radial+tangential+gravity)*dt;
- // Modify position.
- ppos += p->speed * dt;
- p->position[0] = ppos.getX();
- p->position[1] = ppos.getY();
- const float t = 1.0f - p->life / p->lifetime;
- // Rotate.
- p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt;
- // Change size according to given intervals:
- // i = 0 1 2 3 n-1
- // |-------|-------|------|--- ... ---|
- // t = 0 1/(n-1) 3/(n-1) 1
- //
- // `s' is the interpolation variable scaled to the current
- // interval width, e.g. if n = 5 and t = 0.3, then the current
- // indices are 1,2 and s = 0.3 - 0.25 = 0.05
- float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
- s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
- size_t i = (size_t)s;
- size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
- s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
- p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
- // Update color according to given intervals (as above)
- s = t * (float)(colors.size() - 1);
- i = (size_t)s;
- k = (i == colors.size() - 1) ? i : i + 1;
- s -= (float)i; // 0 <= s <= 1
- p->color = colors[i] * (1.0f - s) + colors[k] * s;
- // Next particle.
- p++;
- }
- else
- {
- remove(p);
- if (p >= pLast)
- return;
- } // else
- } // while
- }
- bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
- {
- return distributions.find(in, out);
- }
- bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
- {
- return distributions.find(in, out);
- }
- } // opengl
- } // graphics
- } // love
|