Fix issue #437: Area-based particle spawn.

New particles are spawned around the particle emitter position according to a
configurabe distribution.

New functionality:

particlesystem:setAreaSpread(distributon, x, y)
distribution, x, y = particlesystem:getAreaSpread()

Where `distribution' is one of "none", "uniform", "normal".
`x' and `y' take different meaning based on the distribution:

  * none:    no area spread - particles spawned at emitter position.
  * uniform: particle spawned at px,py in the rectangle around emitter
             position ex,ey, i.e.:
             ex - x < px < ex + x, ey - y < py < ey + y,
  * normal:  particle position drawn from a normal/gaussian distribution
             with mean ex,ey and standard deviation x,y, i.e.:
             px \in N(ex, x²), py \in N(ey, y²).
This commit is contained in:
vrld
2012-09-10 19:58:44 +02:00
parent 50fd169e07
commit 4ec7609d37
4 changed files with 140 additions and 11 deletions
+61 -10
View File
@@ -45,10 +45,18 @@ 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 = (rand() / (float(RAND_MAX)+1));
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)
@@ -57,6 +65,7 @@ ParticleSystem::ParticleSystem(Image *sprite, unsigned int buffer)
, active(true)
, emissionRate(0)
, emitCounter(0)
, areaSpreadDistribution(DISTRIBUTION_NONE)
, lifetime(-1)
, life(0)
, particleLifeMin(0)
@@ -108,45 +117,60 @@ void ParticleSystem::add()
if (min == max)
pLast->life = min;
else
pLast->life = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
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 = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->direction = random(min, max);
pLast->origin = position;
min = speedMin;
max = speedMax;
float speed = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
float speed = random(min, max);
pLast->speed = love::Vector(cos(pLast->direction), sin(pLast->direction));
pLast->speed *= speed;
min = gravityMin;
max = gravityMax;
pLast->gravity = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->gravity = random(min, max);
min = radialAccelerationMin;
max = radialAccelerationMax;
pLast->radialAcceleration = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->radialAcceleration = random(min, max);
min = tangentialAccelerationMin;
max = tangentialAccelerationMax;
pLast->tangentialAcceleration = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->tangentialAcceleration = random(min, max);
pLast->sizeOffset = (rand() / (float(RAND_MAX)+1)) * sizeVariation; // time offset for size change
pLast->sizeIntervalSize = (1.0f - (rand() / (float(RAND_MAX)+1)) * sizeVariation) - pLast->sizeOffset;
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 = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;;
pLast->rotation = random(min, max);
pLast->color = colors[0];
@@ -204,6 +228,13 @@ 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;
@@ -349,6 +380,16 @@ 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;
@@ -543,6 +584,16 @@ void ParticleSystem::update(float dt)
} // 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
@@ -72,6 +72,16 @@ struct particle
class ParticleSystem : public Drawable
{
public:
/**
* Type of distribution new particles are drawn from: None, uniform, normal.
*/
enum AreaSpreadDistribution
{
DISTRIBUTION_NONE,
DISTRIBUTION_UNIFORM,
DISTRIBUTION_NORMAL,
DISTRIBUTION_MAX_ENUM,
};
/**
* Creates a particle system with the specified buffersize and sprite.
@@ -122,6 +132,19 @@ public:
**/
void setPosition(float x, float y);
/**
* Sets the emission area spread parameters and distribution type. The interpretation of
* the parameters depends on the distribution type:
*
* * None: Parameters are ignored. No area spread.
* * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
* * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
* @param x First parameter. Interpretation depends on distribution type.
* @param y Second parameter. Interpretation depends on distribution type.
* @param distribution Distribution type
* */
void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
/**
* Sets the direction and the spread of the particle emitter.
* @param direction The direction (in degrees).
@@ -285,6 +308,16 @@ public:
**/
const love::Vector &getPosition() const;
/**
* Returns area spread distribution type.
*/
AreaSpreadDistribution getAreaSpreadDistribution() const;
/**
* Returns area spread parameters.
*/
const love::Vector &getAreaSpreadParameters() const;
/**
* Returns the direction of the emitter (in degrees).
**/
@@ -357,6 +390,9 @@ public:
* @param dt Time since last update.
**/
void update(float dt);
static bool getConstant(const char *in, AreaSpreadDistribution &out);
static bool getConstant(AreaSpreadDistribution in, const char *&out);
protected:
// The max amount of particles.
@@ -386,6 +422,10 @@ protected:
// The relative position of the particle emitter.
love::Vector position;
// Emission area spread.
AreaSpreadDistribution areaSpreadDistribution;
love::Vector areaSpread;
// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
float lifetime;
float life;
@@ -439,6 +479,9 @@ protected:
void add();
void remove(particle *p);
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
};
} // opengl
+1 -1
View File
@@ -172,7 +172,7 @@ std::string PixelEffect::getGLSLVersion()
bool PixelEffect::isSupported()
{
return GLEE_VERSION_2_0 && GLEE_ARB_shader_objects && GLEE_ARB_fragment_shader && getGLSLVersion() >= "1.2";
return (GLEE_VERSION_2_0 || (GLEE_ARB_shader_objects && GLEE_ARB_fragment_shader)) && getGLSLVersion() >= "1.2";
}
std::string PixelEffect::getWarnings() const
@@ -86,6 +86,24 @@ int w_ParticleSystem_setPosition(lua_State *L)
return 0;
}
int w_ParticleSystem_setAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution;
const char *str = luaL_checkstring(L, 2);
if (!ParticleSystem::getConstant(str, distribution))
return luaL_error(L, "Invalid distribution: '%s'", str);
float x = (float)luaL_checknumber(L, 3);
float y = (float)luaL_checknumber(L, 4);
if (x < 0.0f || y < 0.0f)
return luaL_error(L, "Invalid area spread parameters (must be >= 0)");
t->setAreaSpread(distribution, x, y);
return 0;
}
int w_ParticleSystem_setDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -273,6 +291,21 @@ int w_ParticleSystem_getPosition(lua_State *L)
return 2;
}
int w_ParticleSystem_getAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution = t-> getAreaSpreadDistribution();
const char *str;
ParticleSystem::getConstant(distribution, str);
const love::Vector &p = t->getAreaSpreadParameters();
lua_pushstring(L, str);
lua_pushnumber(L, p.x);
lua_pushnumber(L, p.y);
return 3;
}
int w_ParticleSystem_getDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -373,6 +406,7 @@ static const luaL_Reg functions[] =
{ "setLifetime", w_ParticleSystem_setLifetime },
{ "setParticleLife", w_ParticleSystem_setParticleLife },
{ "setPosition", w_ParticleSystem_setPosition },
{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
{ "setDirection", w_ParticleSystem_setDirection },
{ "setSpread", w_ParticleSystem_setSpread },
{ "setRelativeDirection", w_ParticleSystem_setRelativeDirection },
@@ -390,6 +424,7 @@ static const luaL_Reg functions[] =
{ "getX", w_ParticleSystem_getX },
{ "getY", w_ParticleSystem_getY },
{ "getPosition", w_ParticleSystem_getPosition },
{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
{ "getDirection", w_ParticleSystem_getDirection },
{ "getSpread", w_ParticleSystem_getSpread },
{ "getOffsetX", w_ParticleSystem_getOffsetX },