Browse Source

Merged in lognz/love-minor2/particle system new features (pull request #85)

Particle System New Features

--HG--
branch : minor
Alex Szpakowski 8 years ago
parent
commit
7fdea9a5d1

+ 98 - 10
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)
@@ -261,38 +265,85 @@ void ParticleSystem::initParticle(Particle *p, float t)
 
 	p->position = pos;
 
+	min = direction - spread/2.0f;
+	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);
+		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;
+		break;
+	case DISTRIBUTION_BORDER_RECTANGLE:
+		rand_x = (float) rng.random((areaSpread.getX() + areaSpread.getY()) * -2, (areaSpread.getX() + areaSpread.getY()) * 2);
+		rand_y = areaSpread.getY() * 2;
+		if (rand_x < -rand_y)
+		{
+			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)
+		{
+			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 < rand_y)
+		{
+			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
+		{
+			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();
+		}
 		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;
 	max = speedMax;
 	float speed = (float) rng.random(min, max);
 
-	min = direction - spread/2.0f;
-	max = direction + spread/2.0f;
-	float dir = (float) rng.random(min, max);
-
 	p->velocity = love::Vector(cosf(dir), sinf(dir)) * speed;
 
 	p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
@@ -523,6 +574,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;
@@ -533,6 +599,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;
@@ -1038,6 +1124,8 @@ StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_M
 	{ "uniform", DISTRIBUTION_UNIFORM },
 	{ "normal",  DISTRIBUTION_NORMAL },
 	{ "ellipse",  DISTRIBUTION_ELLIPSE },
+	{ "borderellipse",  DISTRIBUTION_BORDER_ELLIPSE },
+	{ "borderrectangle",  DISTRIBUTION_BORDER_RECTANGLE }
 };
 
 StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));

+ 66 - 3
src/modules/graphics/ParticleSystem.h

@@ -52,7 +52,7 @@ public:
 	static love::Type type;
 
 	/**
-	 * Type of distribution new particles are drawn from: None, uniform, normal, ellipse.
+	 * Type of distribution new particles are drawn from: None, uniform, normal, ellipse, borderellipse, borderrectangle.
 	 */
 	enum AreaSpreadDistribution
 	{
@@ -60,6 +60,8 @@ public:
 		DISTRIBUTION_UNIFORM,
 		DISTRIBUTION_NORMAL,
 		DISTRIBUTION_ELLIPSE,
+		DISTRIBUTION_BORDER_ELLIPSE,
+		DISTRIBUTION_BORDER_RECTANGLE,
 		DISTRIBUTION_MAX_ENUM
 	};
 
@@ -195,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.
 	 **/
@@ -214,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).
@@ -593,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 },