Browse Source

Particle System new features

2 new Particle System Distribution types:

borderellipse - spawns new particles around the border of an elllipse shape defined by dx,dy
borderrectangle - spawns new particles around the border of a rectangle shape defined by dx, dy

New functions:

getAreaSpreadAngle()
getAreaSpreadIsRelativeDirection()
setAreaSpreadAngle(angle in radians)
setAreaSpreadIsRelativeDirection(boolean)

setAreaSpread(distribution, x, y, (optional)angle, (optional)isRelativeDirection)

- angle - defaults 0. In radians the angle to rotate the entire area spread distribution
- isRelativeDirection - defaults false. When false, particles will travel in the direction defined by setDirection(). When true, particles will travel away from the center of the area spread shape

Demo: http://i.imgur.com/sBQ9Xhr.png

The new angle parameter should address: https://bitbucket.org/rude/love/issues/1172

--HG--
branch : particle system new features
lognz 8 years ago
parent
commit
0f99f261e0

+ 78 - 22
src/modules/graphics/ParticleSystem.cpp

@@ -66,6 +66,8 @@ ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size)
 	, emissionRate(0)
 	, emitCounter(0)
 	, areaSpreadDistribution(DISTRIBUTION_NONE)
+	, areaSpreadAngle(0)
+	, areaSpreadIsRelativeDirection(false)
 	, lifetime(-1)
 	, life(0)
 	, particleLifeMin(0)
@@ -122,6 +124,8 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, prevPosition(p.prevPosition)
 	, areaSpreadDistribution(p.areaSpreadDistribution)
 	, areaSpread(p.areaSpread)
+	, areaSpreadAngle(p.areaSpreadAngle)
+	, areaSpreadIsRelativeDirection(p.areaSpreadIsRelativeDirection)
 	, lifetime(p.lifetime)
 	, life(p.lifetime) // Initialize with the maximum life time.
 	, particleLifeMin(p.particleLifeMin)
@@ -265,60 +269,77 @@ void ParticleSystem::initParticle(Particle *p, float t)
 	max = direction + spread/2.0f;
 	float dir = (float) rng.random(min, max);
 
+	// In this switch statement, variables 'rand_y', 'min', and 'max'
+	// are sometimes reused as data stores for performance reasons
 	float rand_x, rand_y;
 	switch (areaSpreadDistribution)
 	{
 	case DISTRIBUTION_UNIFORM:
-		p->position.x += (float) rng.random(-areaSpread.getX(), areaSpread.getX());
-		p->position.y += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
+		rand_x = (float) rng.random(-areaSpread.getX(), areaSpread.getX());
+		rand_y = (float) rng.random(-areaSpread.getY(), areaSpread.getY());
+		p->position.x += cosf(areaSpreadAngle) * rand_x - sinf(areaSpreadAngle) * rand_y;
+		p->position.y += sinf(areaSpreadAngle) * rand_x + cosf(areaSpreadAngle) * rand_y;
 		break;
 	case DISTRIBUTION_NORMAL:
-		p->position.x += (float) rng.randomNormal(areaSpread.getX());
-		p->position.y += (float) rng.randomNormal(areaSpread.getY());
+		rand_x = (float) rng.randomNormal(areaSpread.getX());
+		rand_y = (float) rng.randomNormal(areaSpread.getY());
+		p->position.x += cosf(areaSpreadAngle) * rand_x - sinf(areaSpreadAngle) * rand_y;
+		p->position.y += sinf(areaSpreadAngle) * rand_x + cosf(areaSpreadAngle) * rand_y;
 		break;
 	case DISTRIBUTION_ELLIPSE:
 		rand_x = (float) rng.random(-1, 1);
 		rand_y = (float) rng.random(-1, 1);
-		p->position.x += areaSpread.getX() * (rand_x * sqrt(1 - 0.5f*pow(rand_y, 2)));
-		p->position.y += areaSpread.getY() * (rand_y * sqrt(1 - 0.5f*pow(rand_x, 2)));
+		min = areaSpread.getX() * (rand_x * sqrt(1 - 0.5f*pow(rand_y, 2)));
+		max = areaSpread.getY() * (rand_y * sqrt(1 - 0.5f*pow(rand_x, 2)));
+		p->position.x += cosf(areaSpreadAngle) * min - sinf(areaSpreadAngle) * max;
+		p->position.y += sinf(areaSpreadAngle) * min + cosf(areaSpreadAngle) * max;
 		break;
 	case DISTRIBUTION_BORDER_ELLIPSE:
 		rand_x = (float) rng.random(0, LOVE_M_PI * 2);
-		p->position.x += cosf(rand_x) * areaSpread.getX();
-		p->position.y += sinf(rand_x) * areaSpread.getY();
-		dir += rand_x;
+		min = cosf(rand_x) * areaSpread.getX();
+		max = sinf(rand_x) * areaSpread.getY();
+		p->position.x += cosf(areaSpreadAngle) * min - sinf(areaSpreadAngle) * max;
+		p->position.y += sinf(areaSpreadAngle) * min + cosf(areaSpreadAngle) * max;
+		// dir += rand_x + areaSpreadAngle;
 		break;
 	case DISTRIBUTION_BORDER_RECTANGLE:
-		// Unwraps rectangle border onto straight line and pick random point
 		rand_x = (float) rng.random((areaSpread.getX() + areaSpread.getY()) * -2, (areaSpread.getX() + areaSpread.getY()) * 2);
-		min = areaSpread.getY() * 2;
-		if (rand_x < -min)
+		rand_y = areaSpread.getY() * 2;
+		if (rand_x < -rand_y)
 		{
-			p->position.x += rand_x + min + areaSpread.getX();
-			p->position.y += -areaSpread.getY();
+			min = rand_x + rand_y + areaSpread.getX();
+			p->position.x += cosf(areaSpreadAngle) * min - sinf(areaSpreadAngle) * -areaSpread.getY();
+			p->position.y += sinf(areaSpreadAngle) * min + cosf(areaSpreadAngle) * -areaSpread.getY();
 		}
 		else if (rand_x < 0)
 		{
-			p->position.x += -areaSpread.getX();
-			p->position.y += rand_x + (areaSpread.getY());
+			max = rand_x + areaSpread.getY();
+			p->position.x += cosf(areaSpreadAngle) * -areaSpread.getX() - sinf(areaSpreadAngle) * max;
+			p->position.y += sinf(areaSpreadAngle) * -areaSpread.getX() + cosf(areaSpreadAngle) * max;
 		}
-		else if (rand_x < min)
+		else if (rand_x < rand_y)
 		{
-			p->position.x += areaSpread.getX();
-			p->position.y += rand_x - (areaSpread.getY());
+			max = rand_x - areaSpread.getY();
+			p->position.x += cosf(areaSpreadAngle) * areaSpread.getX() - sinf(areaSpreadAngle) * max;
+			p->position.y += sinf(areaSpreadAngle) * areaSpread.getX() + cosf(areaSpreadAngle) * max;
 		}
 		else
 		{
-			p->position.x += rand_x - min - areaSpread.getX();
-			p->position.y += areaSpread.getY();
+			min = rand_x - rand_y - areaSpread.getX();
+			p->position.x += cosf(areaSpreadAngle) * min - sinf(areaSpreadAngle) * areaSpread.getY();
+			p->position.y += sinf(areaSpreadAngle) * min + cosf(areaSpreadAngle) * areaSpread.getY();
 		}
-		dir += atan2(p->position.y - pos.getY(), p->position.x - pos.getX());
+		// dir += atan2(p->position.y - pos.getY(), p->position.x - pos.getX());
 		break;
 	case DISTRIBUTION_NONE:
 	default:
 		break;
 	}
 
+	// Determine if the origin of each particle is the center of the area
+	if (areaSpreadIsRelativeDirection)
+		dir = atan2(p->position.y - pos.getY(), p->position.x - pos.getX());
+
 	p->origin = pos;
 
 	min = speedMin;
@@ -555,6 +576,21 @@ void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x,
 	areaSpreadDistribution = distribution;
 }
 
+void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y, float angle)
+{
+	areaSpread = love::Vector(x, y);
+	areaSpreadDistribution = distribution;
+	areaSpreadAngle = angle;
+}
+
+void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y, float angle, bool isRelativeDirection)
+{
+	areaSpread = love::Vector(x, y);
+	areaSpreadDistribution = distribution;
+	areaSpreadAngle = angle;
+	areaSpreadIsRelativeDirection = isRelativeDirection;
+}
+
 ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
 {
 	return areaSpreadDistribution;
@@ -565,6 +601,26 @@ const love::Vector &ParticleSystem::getAreaSpreadParameters() const
 	return areaSpread;
 }
 
+void ParticleSystem::setAreaSpreadAngle(float angle)
+{
+	this->areaSpreadAngle = angle;
+}
+
+float ParticleSystem::getAreaSpreadAngle() const
+{
+	return areaSpreadAngle;
+}
+
+void ParticleSystem::setAreaSpreadIsRelativeDirection(bool isRelativeDirection)
+{
+	this->areaSpreadIsRelativeDirection = isRelativeDirection;
+}
+
+bool ParticleSystem::getAreaSpreadIsRelativeDirection() const
+{
+	return areaSpreadIsRelativeDirection;
+}
+
 void ParticleSystem::setDirection(float direction)
 {
 	this->direction = direction;

+ 63 - 2
src/modules/graphics/ParticleSystem.h

@@ -197,15 +197,50 @@ public:
 	 * 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.
+	 * * 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.
+	 * * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
+	 * * borderellipse: Parameter causes particle distribution around outside of ellipse
+	 * * borderrectangle: Parameter causes particle distribution around outside of rectangle
 	 * @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 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.
+	 * * borderellipse: Parameter causes particle distribution around outside of ellipse
+	 * * borderrectangle: Parameter causes particle distribution around outside of rectangle
+	 * @param x First parameter. Interpretation depends on distribution type.
+	 * @param y Second parameter. Interpretation depends on distribution type.
+	 * @param distribution Distribution type
+	 * @param angle Angle for the distribution to be rotated by
+	 **/
+	void setAreaSpread(AreaSpreadDistribution distribution, float x, float y, float angle);
+
+	/**
+	 * 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.
+	 * * borderellipse: Parameter causes particle distribution around outside of ellipse
+	 * * borderrectangle: Parameter causes particle distribution around outside of rectangle
+	 * @param x First parameter. Interpretation depends on distribution type.
+	 * @param y Second parameter. Interpretation depends on distribution type.
+	 * @param distribution Distribution type
+	 * @param angle Angle for the distribution to be rotated by
+	 * @param isRelativeDirection whether to set direction of each particle to away from center
+	 **/
+	void setAreaSpread(AreaSpreadDistribution distribution, float x, float y, float angle, bool isRelativeDirection);
+
 	/**
 	 * Returns area spread distribution type.
 	 **/
@@ -216,6 +251,30 @@ public:
 	 **/
 	const love::Vector &getAreaSpreadParameters() const;
 
+	/**
+	 * Returns the angle of the area distribution (in radians).
+	 **/
+	float getAreaSpreadAngle() const;
+
+	/**
+	 * Sets the angle of the area distribution
+	 * @param angle The angle (in radians).
+	 **/
+	void setAreaSpreadAngle(float angle);
+
+	/**
+	 * Returns true if particles spawn relative to the center of the 
+	 * shape area or false if they will use the setDirection parameter
+	 **/
+	bool getAreaSpreadIsRelativeDirection() const;
+
+	/**
+	 * Sets if particles starting direction is away from the center of the
+	 * area spread or the setDirection parameter
+	 * @param isRelativeDirection boolean use relative direction from center
+	 **/
+	void setAreaSpreadIsRelativeDirection(bool isRelativeDirection);
+
 	/**
 	 * Sets the direction of the particle emitter.
 	 * @param direction The direction (in degrees).
@@ -595,6 +654,8 @@ protected:
 	// Emission area spread.
 	AreaSpreadDistribution areaSpreadDistribution;
 	love::Vector areaSpread;
+	float areaSpreadAngle;
+	bool areaSpreadIsRelativeDirection;
 
 	// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
 	float lifetime;

+ 39 - 2
src/modules/graphics/wrap_ParticleSystem.cpp

@@ -197,7 +197,8 @@ int w_ParticleSystem_setAreaSpread(lua_State *L)
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 
 	ParticleSystem::AreaSpreadDistribution distribution = ParticleSystem::DISTRIBUTION_NONE;
-	float x = 0.f, y = 0.f;
+	float x = 0.f, y = 0.f, angle = 0.f;
+	bool isRelativeDirection = false;
 
 	const char *str = lua_isnoneornil(L, 2) ? 0 : luaL_checkstring(L, 2);
 	if (str && !ParticleSystem::getConstant(str, distribution))
@@ -209,9 +210,11 @@ int w_ParticleSystem_setAreaSpread(lua_State *L)
 		y = (float) luaL_checknumber(L, 4);
 		if (x < 0.0f || y < 0.0f)
 			return luaL_error(L, "Invalid area spread parameters (must be >= 0)");
+		angle = (float) luaL_optnumber(L, 5, 0.0);
+		isRelativeDirection = luax_optboolean(L, 6, false);
 	}
 
-	t->setAreaSpread(distribution, x, y);
+	t->setAreaSpread(distribution, x, y, angle, isRelativeDirection);
 	return 0;
 }
 
@@ -230,6 +233,36 @@ int w_ParticleSystem_getAreaSpread(lua_State *L)
 	return 3;
 }
 
+int w_ParticleSystem_setAreaSpreadAngle(lua_State *L)
+{
+	ParticleSystem *t = luax_checkparticlesystem(L, 1);
+	float arg1 = (float)luaL_checknumber(L, 2);
+	t->setAreaSpreadAngle(arg1);
+	return 0;
+}
+
+int w_ParticleSystem_getAreaSpreadAngle(lua_State *L)
+{
+	ParticleSystem *t = luax_checkparticlesystem(L, 1);
+	lua_pushnumber(L, t->getAreaSpreadAngle());
+	return 1;
+}
+
+int w_ParticleSystem_setAreaSpreadIsRelativeDirection(lua_State *L)
+{
+	ParticleSystem *t = luax_checkparticlesystem(L, 1);
+	bool arg1 = luax_toboolean(L, 2);
+	t->setAreaSpreadIsRelativeDirection(arg1);
+	return 0;
+}
+
+int w_ParticleSystem_getAreaSpreadIsRelativeDirection(lua_State *L)
+{
+	ParticleSystem *t = luax_checkparticlesystem(L, 1);
+	lua_pushnumber(L, t->getAreaSpreadIsRelativeDirection());
+	return 1;
+}
+
 int w_ParticleSystem_setDirection(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -720,6 +753,10 @@ static const luaL_Reg w_ParticleSystem_functions[] =
 	{ "moveTo", w_ParticleSystem_moveTo },
 	{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
 	{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
+	{ "setAreaSpreadAngle", w_ParticleSystem_setAreaSpreadAngle },
+	{ "getAreaSpreadAngle", w_ParticleSystem_getAreaSpreadAngle },
+	{ "setAreaSpreadIsRelativeDirection", w_ParticleSystem_setAreaSpreadIsRelativeDirection },
+	{ "getAreaSpreadIsRelativeDirection", w_ParticleSystem_getAreaSpreadIsRelativeDirection },
 	{ "setDirection", w_ParticleSystem_setDirection },
 	{ "getDirection", w_ParticleSystem_getDirection },
 	{ "setSpread", w_ParticleSystem_setSpread },