Commits

Boolsheet committed 782144e

Error checking.
Different behaviour of setSpin.
Reverting the update code. Probably better this way.

  • Participants
  • Parent commits e140b3d
  • Branches particles2

Comments (0)

Files changed (3)

File src/modules/graphics/opengl/ParticleSystem.cpp

 {
 
 const float float_inf = std::numeric_limits<float>::infinity();
-
 love::math::RandomGenerator rng;
 
-float calculate_variation(float inner, float outer, float var)
+bool isInf(float n)
 {
-	float low = inner - (outer/2.0f)*var;
-	float high = inner + (outer/2.0f)*var;
-	float r = (float) rng.random();
-	return low*(1-r)+high*r;
+	return n == float_inf || n == -float_inf;
+}
+
+bool isNaN(float n)
+{
+	return !(n == float_inf || n < float_inf);
+}
+
+Colorf colorToFloat(const Color &c)
+{
+	// Not dividing by 255 as the vertex structure needs 8-bit values anyway.
+	return Colorf((float) c.r, (float) c.g, (float) c.b, (float) c.a);
 }
 
 }
 	, image(image)
 	, activeParticles(0)
 	, maxParticles(0)
-	, active(true)
+	, active(false)
 	, insertMode(INSERT_MODE_TOP)
 	, emissionRate(10)
 	, emitCounter(float_inf)
 	, areaSpreadDistribution(DISTRIBUTION_NONE)
 	, lifetime(-1)
 	, lifetimeLeft(0)
-	, particleLifeMin(0)
-	, particleLifeMax(0)
+	, particleLifeMin(4)
+	, particleLifeMax(4)
 	, direction(0)
 	, spread(0)
-	, speedMin(0)
-	, speedMax(0)
+	, speedMin(25)
+	, speedMax(25)
 	, accelerationCenter(ACCELERATION_CENTER_PARTICLE_ORIGIN)
 	, radialAccelerationMin(0)
 	, radialAccelerationMax(0)
 	, rotationMax(0)
 	, spinStart(0)
 	, spinEnd(0)
-	, spinVariation(0)
+	, spinStartVariation(0)
+	, spinEndVariation(0)
+	, spinRandomSign(false)
 	, offset(image->getWidth()*0.5f, image->getHeight()*0.5f)
 {
 	sizes.push_back(1.0f);
-	colors.push_back(Colorf(255.0f, 255.0f, 255.0f, 255.0f));
+	colors.push_back(Color(255, 255, 255, 255));
 	setBufferSize(size);
 	image->retain();
 }
 		pFree = pMem = new particle[size];
 		vertexBuffer = new love::vertex[size * 4];
 		elementBuffer = new VertexIndex(size);
-		maxParticles = size;
+		maxParticles = (uint32) size;
 	}
 	catch (std::bad_alloc &)
 	{
 
 	min = rotationMin;
 	max = rotationMax;
-	p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
-	p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
 	p->rotation = (float) rng.random(min, max);
+	p->spinStart = spinStart + (float) rng.random(-spinStartVariation, spinStartVariation);
+	p->spinEnd = spinEnd + (float) rng.random(-spinEndVariation, spinEndVariation);
+	if (spinRandomSign)
+	{
+		float sign = (float) ((int64) (rng.rand() & 1) * 2 - 1);
+		p->spinStart *= sign;
+		p->spinEnd *= sign;
+	}
 }
 
 void ParticleSystem::insertTop(particle *p)
 	insertMode = mode;
 }
 
-ParticleSystem::InsertMode ParticleSystem::getInsertMode()
+ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
 {
 	return insertMode;
 }
 
 void ParticleSystem::setEmissionRate(float rate)
 {
-	if (rate < 0.0f || !(rate < float_inf))
+	if (rate < 0.0f || isNaN(rate))
 		throw love::Exception("Invalid emission rate");
 	emissionRate = rate;
 }
 
 void ParticleSystem::setLifetime(float life)
 {
-	lifetimeLeft = lifetime = life;
+	if (isNaN(life))
+		throw love::Exception("Invalid emitter lifetime");
+	lifetime = life;
 }
 
 float ParticleSystem::getLifetime() const
 	return lifetime;
 }
 
-void ParticleSystem::setParticleLife(float min, float max)
+void ParticleSystem::setParticleLifetime(float min, float max)
 {
+	if (min < 0.0f || isNaN(min))
+		throw love::Exception("Invalid minimum particle lifetime");
+	else if (max < 0.0f || isNaN(max))
+		throw love::Exception("Invalid maximum particle lifetime");
 	particleLifeMin = min;
 	if (max == 0)
 		particleLifeMax = min;
 		particleLifeMax = max;
 }
 
-void ParticleSystem::getParticleLife(float *min, float *max) const
+void ParticleSystem::getParticleLifetime(float *min, float *max) const
 {
 	if (min)
 		*min = particleLifeMin;
 
 void ParticleSystem::setPosition(const love::Vector &pos)
 {
+	if (isNaN(pos.getX()) || isNaN(pos.getY()))
+		throw love::Exception("Invalid emitter position");
 	position = pos;
 }
 
 
 void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, const love::Vector &spread)
 {
+	if (isInf(spread.getX()) || isInf(spread.getY()) || isNaN(spread.getX()) || isNaN(spread.getY()))
+		throw love::Exception("Invalid area spread");
+	else if (spread.getX() < 0.0f || spread.getY() < 0.0f)
+		throw love::Exception("Invalid area spread parameters (must be >= 0)");
 	areaSpread = spread;
 	areaSpreadDistribution = distribution;
 }
 
 void ParticleSystem::setDirection(float direction)
 {
+	if (isInf(direction) || isNaN(direction))
+		throw love::Exception("Invalid particle direction");
 	this->direction = direction;
 }
 
 
 void ParticleSystem::setSpread(float spread)
 {
+	if (isInf(spread) || isNaN(spread))
+		throw love::Exception("Invalid particle spread");
 	this->spread = spread;
 }
 
 	accelerationCenter = point;
 }
 
-ParticleSystem::AccelerationCenterPoint ParticleSystem::getAccelerationCenter()
+ParticleSystem::AccelerationCenterPoint ParticleSystem::getAccelerationCenter() const
 {
 	return accelerationCenter;
 }
 
-void ParticleSystem::setSpeed(float speed)
-{
-	speedMin = speedMax = speed;
-}
-
 void ParticleSystem::setSpeed(float min, float max)
 {
+	if (isInf(min) || isNaN(min))
+		throw love::Exception("Invalid minimum particle speed");
+	else if (isInf(max) || isNaN(max))
+		throw love::Exception("Invalid maximum particle speed");
 	speedMin = min;
 	speedMax = max;
 }
 		*max = speedMax;
 }
 
-void ParticleSystem::setLinearAcceleration(const love::Vector &accel)
-{
-	linearAccelerationMin = accel;
-	linearAccelerationMax = accel;
-}
-
 void ParticleSystem::setLinearAcceleration(const love::Vector &min, const love::Vector &max)
 {
+	if (isInf(min.getX()) || isNaN(min.getX()) || isInf(min.getY()) || isNaN(min.getY()))
+		throw love::Exception("Invalid minimum linear acceleration");
+	else if (isInf(max.getX()) || isNaN(max.getX()) || isInf(max.getY()) || isNaN(max.getY()))
+		throw love::Exception("Invalid maximum linear acceleration");
 	linearAccelerationMin = min;
 	linearAccelerationMax = max;
 }
 		*max = linearAccelerationMax;
 }
 
-void ParticleSystem::setRadialAcceleration(float acceleration)
-{
-	radialAccelerationMin = radialAccelerationMax = acceleration;
-}
-
 void ParticleSystem::setRadialAcceleration(float min, float max)
 {
+	if (isInf(min) || isNaN(min))
+		throw love::Exception("Invalid minimum radial acceleration");
+	else if (isInf(max) || isNaN(max))
+		throw love::Exception("Invalid maximum radial acceleration");
 	radialAccelerationMin = min;
 	radialAccelerationMax = max;
 }
 		*max = radialAccelerationMax;
 }
 
-void ParticleSystem::setTangentialAcceleration(float acceleration)
-{
-	tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
-}
-
 void ParticleSystem::setTangentialAcceleration(float min, float max)
 {
+	if (isInf(min) || isNaN(min))
+		throw love::Exception("Invalid minimum tangential acceleration");
+	else if (isInf(max) || isNaN(max))
+		throw love::Exception("Invalid maximum tangential acceleration");
 	tangentialAccelerationMin = min;
 	tangentialAccelerationMax = max;
 }
 		*max = tangentialAccelerationMax;
 }
 
-void ParticleSystem::setSize(float size)
+void ParticleSystem::setSize(const std::vector<float> &newSizes)
 {
-	sizes.resize(1);
-	sizes[0] = size;
-}
-
-void ParticleSystem::setSize(const std::vector<float> &newSizes, float variation)
-{
+	size_t numSizes = newSizes.size();
+	if (numSizes < 1 || numSizes > 8)
+		throw love::Exception("At most eight (8) sizes may be used.");
+	for (size_t i = 0; i < numSizes; i++)
+	{
+		if (isInf(newSizes[i]) || isNaN(newSizes[i]))
+			throw love::Exception("Invalid particle size");
+	}
 	sizes = newSizes;
-	sizeVariation = variation;
 }
 
 const std::vector<float> &ParticleSystem::getSize() const
 
 void ParticleSystem::setSizeVariation(float variation)
 {
+	if (isInf(variation) || isNaN(variation))
+		throw love::Exception("Invalid size variation");
+	else if (variation < 0.0f || variation > 1.0f)
+		throw love::Exception("Size variation has to be between 0 and 1, inclusive.");
 	sizeVariation = variation;
 }
 
 	return sizeVariation;
 }
 
-void ParticleSystem::setRotation(float rotation)
-{
-	rotationMin = rotationMax = rotation;
-}
-
 void ParticleSystem::setRotation(float min, float max)
 {
+	if (isInf(min) || isNaN(min))
+		throw love::Exception("Invalid minimum particle rotation");
+	else if (isInf(max) || isNaN(max))
+		throw love::Exception("Invalid maximum particle rotation");
 	rotationMin = min;
 	rotationMax = max;
 }
 		*max = rotationMax;
 }
 
-void ParticleSystem::setSpin(float spin)
+void ParticleSystem::setSpin(float start, float end, bool randomSign)
 {
-	spinStart = spin;
-	spinEnd = spin;
+	if (isInf(start) || isNaN(start))
+		throw love::Exception("Invalid start spin");
+	else if (isInf(end) || isNaN(end))
+		throw love::Exception("Invalid end spin");
+	spinStart = start;
+	spinEnd = end;
+	spinRandomSign = randomSign;
 }
 
-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::getSpin(float *start, float *end) const
+void ParticleSystem::getSpin(float *start, float *end, bool *randomSign) const
 {
 	if (start)
 		*start = spinStart;
 	if (end)
 		*end = spinEnd;
+	if (randomSign)
+		*randomSign = spinRandomSign;
 }
 
-void ParticleSystem::setSpinVariation(float variation)
+void ParticleSystem::setSpinVariation(float startVariation, float endVariation)
 {
-	spinVariation = variation;
+	if (isInf(startVariation) || isNaN(startVariation))
+		throw love::Exception("Invalid start spin");
+	else if (isInf(endVariation) || isNaN(endVariation))
+		throw love::Exception("Invalid end spin");
+	spinStartVariation = startVariation;
+	spinEndVariation = endVariation;
 }
 
-float ParticleSystem::getSpinVariation() const
+void ParticleSystem::getSpinVariation(float *startVariation, float *endVariation) const
 {
-	return spinVariation;
+	if (startVariation)
+		*startVariation = spinStartVariation;
+	if (endVariation)
+		*endVariation = spinEndVariation;
 }
 
 void ParticleSystem::setOffset(const love::Vector &offset)
 {
+	if (isInf(offset.getX()) || isNaN(offset.getX()) || isInf(offset.getY()) || isNaN(offset.getY()))
+		throw love::Exception("Invalid offset");
 	this->offset = offset;
 }
 
 	return offset;
 }
 
-void ParticleSystem::setColor(const Color &newColor)
+void ParticleSystem::setColor(const std::vector<Color> &newColors)
 {
-	colors.resize(1);
-	colors[0] = Colorf(newColor.r, newColor.g, newColor.b, newColor.a);
+	size_t numColors = newColors.size();
+	if (numColors < 1 || numColors > 8)
+		throw love::Exception("At most eight (8) sizes may be used.");
+	colors = newColors;
 }
 
-void ParticleSystem::setColor(const std::vector<Color> &newColors)
+const std::vector<Color> &ParticleSystem::getColor() const
 {
-	colors.resize(newColors.size());
-	for (size_t i = 0; i < newColors.size(); ++i)
-		colors[i] = Colorf(newColors[i].r, newColors[i].g, newColors[i].b, newColors[i].a);
+	return colors;
 }
 
-std::vector<Color> ParticleSystem::getColor() const
-{
-	// We store colors as floats...
-	std::vector<Color> ncolors(colors.size());
-
-	for (size_t i = 0; i < colors.size(); ++i)
-	{
-		uint8 r, g, b, a;
-		r = (uint8) colors[i].r;
-		g = (uint8) colors[i].g;
-		b = (uint8) colors[i].b;
-		a = (uint8) colors[i].a;
-		ncolors[i] = Color(r, g, b, a);
-	}
-
-	return ncolors;
-}
-
-uint32 ParticleSystem::getParticleCount() const
+uint32 ParticleSystem::getCount() const
 {
 	return activeParticles;
 }
 	if (!active)
 		return;
 
+	num = std::min(num, maxParticles - activeParticles);
+
 	while(num--)
-	{
-		if (isFull())
-			return;
-
 		addParticle();
-	}
 }
 
 bool ParticleSystem::isActive() const
 
 void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
 {
-	const uint32 pCount = getParticleCount();
+	const uint32 pCount = getCount();
 
 	if (pCount == 0 || image == NULL || vertexBuffer == NULL)
 		return;
 			const float rate = 1.0f / emissionRate;
 			uint32 newParticles = 0;
 
-			if (rate == float_inf)
+			if (isInf(rate))
 			{
 				newParticles = maxNewParticles;
 				emitCounter = rate;
 	const uint8 sizeLimit = sizes.size() - 1;
 	const uint8 colorLimit = colors.size() - 1;
 
-	const love::vertex *imgVertex = image->getVertices();
+	const love::vertex *const imgVertex = image->getVertices();
 	love::vertex *pVertex = vertexBuffer;
 	particle *p = pHead;
 
 	while (p)
 	{
-		love::Vector linear = p->linearAcceleration, radial, tangential;
-		const float t = 1.0f - p->lifetimeLeft / p->lifetime;
+		p->lifetimeLeft -= dt;
+		if (p->lifetimeLeft < 0.0f)
+			p = removeParticle(p);
+		else
+		{
+			love::Vector linear = p->linearAcceleration, radial, tangential;
+			const float t = 1.0f - p->lifetimeLeft / p->lifetime;
 
-		if (p->lifetimeLeft != p->lifetime)
-		{
 			// Calculates and normalizes the vector to the acceleration center.
 			switch (accelerationCenter)
 			{
 			// Update position and rotation.
 			p->position += p->velocity * dt;
 			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) sizeLimit; // 0 <= s < sizes.size()
+			size_t i = (size_t) s;
+			size_t k = (i == sizeLimit) ? 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
+			float pSize = sizes[i] * (1.0f - s) + sizes[k] * s;
+
+			// Update color according to given intervals (as above)
+			s = t * (float) colorLimit;
+			i = (size_t) s;
+			k = (i == colorLimit) ? i : i + 1;
+			s -= (float) i; // 0 <= s <= 1
+			Colorf pColor = colorToFloat(colors[i]) * (1.0f - s) + colorToFloat(colors[k]) * s;
+
+			// Transforms the vectors.
+			for (int i = 0; i < 4; i++)
+				pVertex[i] = imgVertex[i];
+
+			love::Matrix pMat;
+			pMat.setTransformation(p->position.getX(), p->position.getY(),
+				p->rotation, pSize, pSize, offset.getX(), offset.getY(), 0.0f, 0.0f);
+			pMat.transform(pVertex, pVertex, 4);
+
+			// Copies the vertex colors.
+			pVertex[0].r = (uint8) pColor.r;
+			pVertex[0].g = (uint8) pColor.g;
+			pVertex[0].b = (uint8) pColor.b;
+			pVertex[0].a = (uint8) pColor.a;
+
+			memcpy(&pVertex[1].r, &pVertex[0].r, 4);
+			memcpy(&pVertex[2].r, &pVertex[0].r, 4);
+			memcpy(&pVertex[3].r, &pVertex[0].r, 4);
+
+			pVertex += 4;
+			p = p->next;
 		}
-
-		// 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) sizeLimit; // 0 <= s < sizes.size()
-		size_t i = (size_t) s;
-		size_t k = (i == sizeLimit) ? 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
-		float pSize = sizes[i] * (1.0f - s) + sizes[k] * s;
-
-		// Update color according to given intervals (as above)
-		s = t * (float) colorLimit;
-		i = (size_t) s;
-		k = (i == colorLimit) ? i : i + 1;
-		s -= (float) i; // 0 <= s <= 1
-		Colorf pColor = colors[i] * (1.0f - s) + colors[k] * s;
-
-		// Transforms the vectors.
-		for (int i = 0; i < 4; i++)
-			pVertex[i] = imgVertex[i];
-
-		love::Matrix pMat;
-		pMat.setTransformation(p->position.getX(), p->position.getY(),
-			p->rotation, pSize, pSize, offset.getX(), offset.getY(), 0.0f, 0.0f);
-		pMat.transform(pVertex, pVertex, 4);
-
-		// Copies the vertex colors.
-		pVertex[0].r = (uint8) pColor.r;
-		pVertex[0].g = (uint8) pColor.g;
-		pVertex[0].b = (uint8) pColor.b;
-		pVertex[0].a = (uint8) pColor.a;
-
-		memcpy(&pVertex[1].r, &pVertex[0].r, 4);
-		memcpy(&pVertex[2].r, &pVertex[0].r, 4);
-		memcpy(&pVertex[3].r, &pVertex[0].r, 4);
-
-		pVertex += 4;
-
-		// Decrease lifespan. Don't remove the particle if it just was spawned.
-		float curLifetime = p->lifetimeLeft;
-		p->lifetimeLeft -= dt;
-		if (p->lifetimeLeft < 0.0f && curLifetime != p->lifetime)
-			p = removeParticle(p);
-		else
-			p = p->next;
 	}
 }
 

File src/modules/graphics/opengl/ParticleSystem.h

 	 * Returns the current insert mode.
 	 * @param mode The current insert mode.
 	 */
-	InsertMode getInsertMode();
+	InsertMode getInsertMode() const;
 
 	/**
 	 * Sets the emission rate.
 	 * @param lifeMin The minimum life.
 	 * @param lifeMax The maximum life (if 0, then becomes the same as minimum life).
 	 */
-	void setParticleLife(float min, float max = 0);
+	void setParticleLifetime(float min, float max = 0);
 
 	/**
 	 * Gets the lifetime of a particle.
 	 * @param[out] min
 	 * @param[out] max
 	 **/
-	void getParticleLife(float *min, float *max) const;
+	void getParticleLifetime(float *min, float *max) const;
 
 	/**
 	 * Sets the position of the center of the emitter.
 	 * Sets the center for the radial and tangential acceleration of all particles.
 	 * @param point The new acceleration center.
 	 */
-	AccelerationCenterPoint getAccelerationCenter();
-
-	/**
-	 * Sets the speed of the particles.
-	 * @param speed The speed.
-	 */
-	void setSpeed(float speed);
+	AccelerationCenterPoint getAccelerationCenter() const;
 
 	/**
 	 * Sets the speed of the particles.
 
 	/**
 	 * Sets the linear acceleration (the acceleration along the x and y axes).
-	 * @param accel The acceleration vector.
-	 **/
-	void setLinearAcceleration(const love::Vector &accel);
-
-	/**
-	 * Sets the linear acceleration (the acceleration along the x and y axes).
 	 * @param min The minimum acceleration vector.
 	 * @param max The maximum acceleration vector.
 	 **/
 
 	/**
 	 * Sets the radial acceleration (the acceleration towards the emitter).
-	 * @param acceleration The amount of acceleration.
-	 */
-	void setRadialAcceleration(float acceleration);
-
-	/**
-	 * Sets the radial acceleration (the acceleration towards the emitter).
 	 * @param min The minimum acceleration.
 	 * @param max The maximum acceleration.
 	 */
 
 	/**
 	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
-	 * @param acceleration The amount of acceleration.
-	 */
-	void setTangentialAcceleration(float acceleration);
-
-	/**
-	 * Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
 	 * @param min The minimum acceleration.
 	 * @param max The maximum acceleration.
 	 */
 	void getTangentialAcceleration(float *min, float *max) const;
 
 	/**
-	 * Sets one size for all particles (1.0 being the default).
-	 * @param size The size of all particles.
-	 */
-	void setSize(float size);
-
-	/**
 	 * Sets a list of particle sizes. Over the particle's lifetime, it will
 	 * change its size according to it, interpolating from the first to the
 	 * last. The variation is a number in the interval [0,1]. If it is zero,
 	 * @param newSizes Array of sizes.
 	 * @param variation The possible variation of the interpolation interval.
 	 */
-	void setSize(const std::vector<float> &newSizes, float variation = 0.0f);
+	void setSize(const std::vector<float> &newSizes);
 
 	/**
 	 * Returns the list of particle sizes.
 	float getSizeVariation() const;
 
 	/**
-	 * Sets the initial rotation of the particles.
-	 * @param rotation The initial rotation of the particle.
-	 */
-	void setRotation(float rotation);
-
-	/**
 	 * Sets the minimum and maximum for the initial rotation of the particles.
 	 * @param min The minimum rotation.
 	 * @param max The maximum rotation.
 	void getRotation(float *min, float *max) const;
 
 	/**
-	 * Sets the angular velocity of the particles.
-	 * @param spin The angular velocity of the particles.
+	 * Sets the inital and final angular velocity of the particles and if
+	 * the velocities have a chance of getting randomly negated to make the
+	 * particle spin in the other direction.
+	 * @param start The angular velocity upon creation.
+	 * @param end The angular velocity upon death.
+	 * @param randomSign If true, the angular veolcity will get randomly negated.
 	 */
-	void setSpin(float spin);
+	void setSpin(float start, float end, bool randomSign);
 
 	/**
-	 * Sets the inital and final angular velocity of the particles.
-	 * @param start The angular velocity upon creation.
-	 * @param end The angular velocity upon death.
-	 */
-	void setSpin(float start, float end);
-
-	/**
-	 * Sets the inital and final angular velocity of the particles and the variation.
-	 * @param start The angular velocity upon creation.
-	 * @param end The angular velocity upon death.
-	 * @param variation The variation (?!).
-	 */
-	void setSpin(float start, float end, float variation);
-
-	/**
-	 * Gets the initial and final angular velocity of the particles.
+	 * Returns the initial and final angular velocity of the particles.
 	 * @param[out] start
 	 * @param[out] end
 	 **/
-	void getSpin(float *start, float *end) const;
+	void getSpin(float *start, float *end, bool *randomSign) const;
 
 	/**
-	 * Sets the angular velocity variation.
-	 * @param variation The variation.
+	 * Sets the variation for the angular velocities.
+	 * The random velocities calculated for new particles varies between
+	 * spin-variation and spin+variation.
+	 * @param startVariation The variation for the start spin.
+	 * @param endVariation The variation for the end spin.
 	 */
-	void setSpinVariation(float variation);
+	void setSpinVariation(float startVariation, float endVariation);
 
 	/**
-	 * Returns the angular velocity variation.
-	 **/
-	float getSpinVariation() const;
+	 * Returns the maximum variation for the angular velocities.
+	 * @param startVariation The float at the address receives the variation for the start spin.
+	 * @param endVariation The float at the address receives the variation for the end spin.
+	 */
+	void getSpinVariation(float *startVariation, float *endVariation) const;
 
 	/**
 	 * Sets the particles offset vector. Before the other transformations are
 	const love::Vector &getOffset() const;
 
 	/**
-	 * Sets a a ist of particle colors.
-	 * @param newColor The color.
-	 */
-	void setColor(const Color &newColor);
-
-	/**
 	 * Sets a list of particle colors. Over the particle's lifetime, it will
 	 * change its color according to it, interpolating from the first to the
 	 * last.
 	/**
 	 * Returns the color of the particles.
 	 **/
-	std::vector<Color> getColor() const;
+	const std::vector<Color> &getColor() const;
 
 	/**
 	 * Returns the amount of particles that are currently active in the system.
 	 */
-	uint32 getParticleCount() const;
+	uint32 getCount() const;
 
 	/**
 	 * Starts or resumes the emitter.
 	// The spin variation is something weird and unexplainable.
 	float spinStart;
 	float spinEnd;
-	float spinVariation;
+	float spinStartVariation;
+	float spinEndVariation;
+	bool spinRandomSign;
 
 	// The offset vector that is applied before any transformation. By default
 	// used to center the image on the origin point.
 
 	// The colors of the particles. Like the sizes, interpolation over time
 	// without the variation.
-	std::vector<Colorf> colors;
+	std::vector<Color> colors;
 
 	void createBuffers(size_t size);
 	void deleteBuffers();

File src/modules/graphics/opengl/wrap_ParticleSystem.cpp

 int w_ParticleSystem_setLifetime(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
-	float arg1 = luax_checkfloat(L, 2);
-	t->setLifetime(arg1);
+	try
+	{
+		if (lua_isnoneornil(L, 2))
+			t->setLifetime(-1);
+		else
+		{
+			float arg1 = luax_checkfloat(L, 2);
+			t->setLifetime(arg1);
+		}
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	return 1;
 }
 
-int w_ParticleSystem_setParticleLife(lua_State *L)
+int w_ParticleSystem_setParticleLifetime(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = (float) luaL_optnumber(L, 3, arg1);
-	t->setParticleLife(arg1, arg2);
+	try
+	{
+		t->setParticleLifetime(arg1, arg2);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
-int w_ParticleSystem_getParticleLife(lua_State *L)
+int w_ParticleSystem_getParticleLifetime(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float min, max;
-	t->getParticleLife(&min, &max);
+	t->getParticleLifetime(&min, &max);
 	lua_pushnumber(L, min);
 	lua_pushnumber(L, max);
 	return 2;
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = luax_checkfloat(L, 3);
-	t->setPosition(love::Vector(arg1, arg2));
+	try
+	{
+		t->setPosition(love::Vector(arg1, arg2));
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	const char *str = luaL_checkstring(L, 2);
 	if (!ParticleSystem::getConstant(str, distribution))
 		return luaL_error(L, "Invalid distribution: '%s'", str);
-
 	float x = luax_checkfloat(L, 3);
 	float y = luax_checkfloat(L, 4);
-	if (x < 0.0f || y < 0.0f)
-		return luaL_error(L, "Invalid area spread parameters (must be >= 0)");
-
-	t->setAreaSpread(distribution, love::Vector(x, y));
+	try
+	{
+		t->setAreaSpread(distribution, love::Vector(x, y));
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
-	t->setDirection(arg1);
+	try
+	{
+		t->setDirection(arg1);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
-	t->setSpread(arg1);
+	try
+	{
+		t->setSpread(arg1);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = (float) luaL_optnumber(L, 3, arg1);
-	t->setSpeed(arg1, arg2);
+	try
+	{
+		t->setSpeed(arg1, arg2);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	float ymin = (float) luaL_optnumber(L, 3, 0.0);
 	float xmax = (float) luaL_optnumber(L, 4, xmin);
 	float ymax = (float) luaL_optnumber(L, 5, ymin);
-	t->setLinearAcceleration(love::Vector(xmin, ymin), love::Vector(xmax, ymax));
+	try
+	{
+		t->setLinearAcceleration(love::Vector(xmin, ymin), love::Vector(xmax, ymax));
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = (float) luaL_optnumber(L, 3, arg1);
-	t->setRadialAcceleration(arg1, arg2);
+	try
+	{
+		t->setRadialAcceleration(arg1, arg2);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = (float) luaL_optnumber(L, 3, arg1);
-	t->setTangentialAcceleration(arg1, arg2);
+	try
+	{
+		t->setTangentialAcceleration(arg1, arg2);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	if (nSizes > 8)
 		return luaL_error(L, "At most eight (8) sizes may be used.");
 
-	if (nSizes <= 1)
+	std::vector<float> sizes(nSizes);
+	for (size_t i = 0; i < nSizes; ++i)
+		sizes[i] = luax_checkfloat(L, 1 + i + 1);
+
+	try
 	{
-		float size = luax_checkfloat(L, 2);
-		t->setSize(size);
+		t->setSize(sizes);
 	}
-	else
+	catch (love::Exception &e)
 	{
-		std::vector<float> sizes(nSizes);
-		for (size_t i = 0; i < nSizes; ++i)
-			sizes[i] = luax_checkfloat(L, 1 + i + 1);
-
-		t->setSize(sizes);
+		return luaL_error(L, "%s", e.what());
 	}
 	return 0;
 }
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
-	if (arg1 < 0.0f || arg1 > 1.0f)
-		return luaL_error(L, "Size variation has to be between 0 and 1, inclusive.");
-	t->setSizeVariation(arg1);
+	try
+	{
+		t->setSizeVariation(arg1);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = (float) luaL_optnumber(L, 3, arg1);
-	t->setRotation(arg1, arg2);
+	try
+	{
+		t->setRotation(arg1, arg2);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
 	float arg2 = (float) luaL_optnumber(L, 3, arg1);
-	float arg3 = (float) luaL_optnumber(L, 4, 0);
+	bool arg3 = luax_optboolean(L, 4, false);
+	try
+	{
 	t->setSpin(arg1, arg2, arg3);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float start, end;
-	t->getSpin(&start, &end);
+	bool randomSign;
+	t->getSpin(&start, &end, &randomSign);
 	lua_pushnumber(L, start);
 	lua_pushnumber(L, end);
+	lua_pushboolean(L, randomSign);
 	return 2;
 }
 
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float arg1 = luax_checkfloat(L, 2);
-	t->setSpinVariation(arg1);
+	float arg2 = luax_checkfloat(L, 3);
+	try
+	{
+		t->setSpinVariation(arg1, arg2);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 int w_ParticleSystem_getSpinVariation(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
-	lua_pushnumber(L, t->getSpinVariation());
-	return 1;
+	float start, end;
+	t->getSpinVariation(&start, &end);
+	lua_pushnumber(L, start);
+	lua_pushnumber(L, end);
+	return 2;
 }
 
 int w_ParticleSystem_setOffset(lua_State *L)
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	float x = luax_checkfloat(L, 2);
 	float y = luax_checkfloat(L, 3);
-	t->setOffset(love::Vector(x, y));
+	try
+	{
+		t->setOffset(love::Vector(x, y));
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 
-	if (lua_istable(L, 2)) // setColors({r,g,b,a}, {r,g,b,a}, ...)
+	try
 	{
-		size_t nColors = lua_gettop(L) - 1;
+		if (lua_istable(L, 2)) // setColors({r,g,b,a}, {r,g,b,a}, ...)
+		{
+			size_t nColors = lua_gettop(L) - 1;
 
-		if (nColors > 8)
-			return luaL_error(L, "At most eight (8) colors may be used.");
+			if (nColors > 8)
+				return luaL_error(L, "At most eight (8) colors may be used.");
 
-		std::vector<Color> colors(nColors);
+			std::vector<Color> colors(nColors);
 
-		for (size_t i = 0; i < nColors; i++)
+			for (size_t i = 0; i < nColors; i++)
+			{
+				luaL_checktype(L, i + 2, LUA_TTABLE);
+
+				if (lua_objlen(L, i + 2) < 3)
+					return luaL_argerror(L, i + 2, "expected 4 color components");
+
+				for (int j = 0; j < 4; j++)
+					// push args[i+2][j+1] onto the stack
+					lua_rawgeti(L, i + 2, j + 1);
+
+				int r = luaL_checkint(L, -4);
+				int g = luaL_checkint(L, -3);
+				int b = luaL_checkint(L, -2);
+				int a = luaL_optint(L, -1, 255);
+
+				// pop the color components from the stack
+				lua_pop(L, 4);
+
+				colors[i] = Color(r, g, b, a);
+			}
+
+			t->setColor(colors);
+		}
+		else // setColors(r,g,b,a, r,g,b,a, ...)
 		{
-			luaL_checktype(L, i + 2, LUA_TTABLE);
+			int cargs = lua_gettop(L) - 1;
+			size_t nColors = (cargs + 3) / 4; // nColors = ceil(color_args / 4)
 
-			if (lua_objlen(L, i + 2) < 3)
-				return luaL_argerror(L, i + 2, "expected 4 color components");
+			if (cargs % 4 != 0 || cargs == 0)
+				return luaL_error(L, "Expected red, green, blue, and alpha. Only got %d of 4 components.", cargs % 4);
 
-			for (int j = 0; j < 4; j++)
-				// push args[i+2][j+1] onto the stack
-				lua_rawgeti(L, i + 2, j + 1);
+			if (nColors > 8)
+				return luaL_error(L, "At most eight (8) colors may be used.");
 
-			int r = luaL_checkint(L, -4);
-			int g = luaL_checkint(L, -3);
-			int b = luaL_checkint(L, -2);
-			int a = luaL_optint(L, -1, 255);
-
-			// pop the color components from the stack
-			lua_pop(L, 4);
-
-			colors[i] = Color(r, g, b, a);
-		}
-
-		t->setColor(colors);
-	}
-	else // setColors(r,g,b,a, r,g,b,a, ...)
-	{
-		int cargs = lua_gettop(L) - 1;
-		size_t nColors = (cargs + 3) / 4; // nColors = ceil(color_args / 4)
-
-		if (cargs % 4 != 0 || cargs == 0)
-			return luaL_error(L, "Expected red, green, blue, and alpha. Only got %d of 4 components.", cargs % 4);
-
-		if (nColors > 8)
-			return luaL_error(L, "At most eight (8) colors may be used.");
-
-		if (nColors == 1)
-		{
-			int r = luaL_checkint(L, 2);
-			int g = luaL_checkint(L, 3);
-			int b = luaL_checkint(L, 4);
-			int a = luaL_checkint(L, 5);
-			t->setColor(Color(r,g,b,a));
-		}
-		else
-		{
 			std::vector<Color> colors(nColors);
 			for (size_t i = 0; i < nColors; ++i)
 			{
 			t->setColor(colors);
 		}
 	}
-
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
 	return colors.size();
 }
 
-int w_ParticleSystem_getParticleCount(lua_State *L)
+int w_ParticleSystem_getCount(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
-	lua_pushnumber(L, t->getParticleCount());
+	lua_pushnumber(L, t->getCount());
 	return 1;
 }
 
 	{ "getEmissionRate", w_ParticleSystem_getEmissionRate },
 	{ "setLifetime", w_ParticleSystem_setLifetime },
 	{ "getLifetime", w_ParticleSystem_getLifetime },
-	{ "setParticleLife", w_ParticleSystem_setParticleLife },
-	{ "getParticleLife", w_ParticleSystem_getParticleLife },
+	{ "setParticleLifetime", w_ParticleSystem_setParticleLifetime },
+	{ "getParticleLifetime", w_ParticleSystem_getParticleLifetime },
 	{ "setPosition", w_ParticleSystem_setPosition },
 	{ "getPosition", w_ParticleSystem_getPosition },
 	{ "getX", w_ParticleSystem_getX },
 	{ "getColors", w_ParticleSystem_getColors },
 	{ "setOffset", w_ParticleSystem_setOffset },
 	{ "getOffset", w_ParticleSystem_getOffset },
-	{ "getParticleCount", w_ParticleSystem_getParticleCount },
+	{ "getCount", w_ParticleSystem_getCount },
 	{ "start", w_ParticleSystem_start },
 	{ "stop", w_ParticleSystem_stop },
 	{ "pause", w_ParticleSystem_pause },