Commits

Anonymous committed 0d48721

Change particle system to accept an arbitrary number of colors and sizes to inerpolate.

API changes:
- ParticleSystem:setSize(min, max, var) -> ParticleSystem:setSizes(s0, ..., sN)
!! ParticleSystem:setSize() drops variation parameter !!
- ParticleSystem:setColor([color1], [color2]) -> ParticleSystem:setColor([color1], ..., [colorN])

Comments (0)

Files changed (4)

src/modules/graphics/opengl/ParticleSystem.cpp

 #include <SDL_opengl.h>
 #include <cmath>
 #include <cstdlib>
-#include <cstring>
 
 namespace love
 {
 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;
 															direction(0), spread(0), relative(false), speedMin(0), speedMax(0), gravityMin(0),
 															gravityMax(0), radialAccelerationMin(0), radialAccelerationMax(0),
 															tangentialAccelerationMin(0), tangentialAccelerationMax(0),
-															sizeStart(1), sizeEnd(1), sizeVariation(0), rotationMin(0), rotationMax(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();
-		memset(colorStart, 255, 4);
-		memset(colorEnd, 255, 4);
+		sizes.push_back(1.0f);
+		colors.push_back( Colorf(1.0f, 1.0f, 1.0f, 1.0f) );
 		setBufferSize(buffer);
 	}
 
 		max = tangentialAccelerationMax;
 		pLast->tangentialAcceleration = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
 
-		pLast->sizeStart = calculate_variation(sizeStart, sizeEnd, sizeVariation);
-		pLast->sizeEnd = calculate_variation(sizeEnd, sizeStart, sizeVariation);
-		pLast->size = pLast->sizeStart;
+		pLast->sizeOffset       = (rand() / (float(RAND_MAX)+1)) * sizeVariation; // time offset for size change
+		pLast->sizeIntervalSize = (1.0 - (rand() / (float(RAND_MAX)+1)) * sizeVariation) - pLast->sizeOffset;
+		pLast->size = sizes[(size_t)(pLast->sizeOffset - .5f) * (sizes.size() - 1)];
 
 		min = rotationMin;
 		max = rotationMax;
 		pLast->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
 		pLast->rotation = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;;
 
-		pLast->color[0] = (float)colorStart[0] / 255;
-		pLast->color[1] = (float)colorStart[1] / 255;
-		pLast->color[2] = (float)colorStart[2] / 255;
-		pLast->color[3] = (float)colorStart[3] / 255;
+		pLast->color = colors[0];
 
 		pLast++;
 	}
 
 	void ParticleSystem::setSize(float size)
 	{
-		sizeStart = size;
-		sizeEnd = size;
+		sizes.resize(1);
+		sizes[0] = size;
 	}
 
-	void ParticleSystem::setSize(float start, float end)
+	void ParticleSystem::setSize(const std::vector<float>& newSizes, float variation)
 	{
-		sizeStart = start;
-		sizeEnd = end;
-	}
-
-	void ParticleSystem::setSize(float start, float end, float variation)
-	{
-		sizeStart = start;
-		sizeEnd = end;
+		sizes = newSizes;
 		sizeVariation = variation;
 	}
 
 		spinVariation = variation;
 	}
 
-	void ParticleSystem::setColor(unsigned char * color)
+	void ParticleSystem::setColor(const Color& color)
 	{
-		memcpy(colorStart, color, 4);
-		memcpy(colorEnd, color, 4);
+		colors.resize(1);
+		colors[0] = colorToFloat(color);
 	}
 
-	void ParticleSystem::setColor(unsigned char * start, unsigned char * end)
+	void ParticleSystem::setColor(const std::vector<Color>& newColors)
 	{
-		memcpy(colorStart, start, 4);
-		memcpy(colorEnd, end, 4);
+		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)
 		{
 			glPushMatrix();
 
-			glColor4f(p->color[0],p->color[1],p->color[2],p->color[3]);
-			glTranslatef(p->position[0],p->position[1],0.0f);
-			glRotatef(LOVE_TODEG(p->rotation), 0.0f, 0.0f, 1.0f); // rad * (180 / pi)
-			glScalef(p->size,p->size,1.0f);
-			glTranslatef(-offsetX,-offsetY,0.0f);
-			sprite->draw(0,0, 0, 1, 1, 0, 0);
+			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);
 
 			glPopMatrix();
 			p++;
 				p->position[0] = ppos.getX();
 				p->position[1] = ppos.getY();
 
-				const float t = p->life / p->lifetime;
-
-				// Change size.
-				p->size = p->sizeEnd - ((p->sizeEnd - p->sizeStart) * t);
+				const float t = 1.0f - p->life / p->lifetime;
 
 				// Rotate.
-				p->rotation += (p->spinStart*(1-t) + p->spinEnd*t)*dt;
+				p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt;
 
-				// Update color.
-				p->color[0] = (float)(colorEnd[0]*(1.0f-t) + colorStart[0] * t)/255.0f;
-				p->color[1] = (float)(colorEnd[1]*(1.0f-t) + colorStart[1] * t)/255.0f;
-				p->color[2] = (float)(colorEnd[2]*(1.0f-t) + colorStart[2] * t)/255.0f;
-				p->color[3] = (float)(colorEnd[3]*(1.0f-t) + colorStart[3] * t)/255.0f;
+				// 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 - t) + sizes[k] * t;
+
+				// 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++;

src/modules/graphics/opengl/ParticleSystem.h

 #include <common/math.h>
 #include <common/Vector.h>
 #include <graphics/Drawable.h>
+#include <graphics/Color.h>
 #include "Image.h"
+#include <vector>
 
 namespace love
 {
 		float tangentialAcceleration;
 
 		float size;
-		float sizeStart;
-		float sizeEnd;
+		float sizeOffset;
+		float sizeIntervalSize;
 
 		float rotation;
 		float spinStart;
 		float spinEnd;
 
-		float color[4];
+		Colorf color;
 	};
 
 	/**
 		float tangentialAccelerationMax;
 
 		// Size.
-		float sizeStart;
-		float sizeEnd;
+		std::vector<float> sizes;
 		float sizeVariation;
 
 		// Rotation
 		float offsetY;
 
 		// Color.
-		unsigned char colorStart[4];
-		unsigned char colorEnd[4];
+		std::vector<Colorf> colors;
 
 		void add();
 		void remove(particle * p);
 		void setSize(float size);
 
 		/**
-		* Sets the size of the sprite upon creation and upon death (1.0 being the default size).
-		* @param start The size of the sprite upon creation
-		* @param end The size of the sprite upon death.
-		**/
-		void setSize(float start, float end);
-
-		/**
 		* Sets the size of the sprite upon creation and upon death (1.0 being the default size) and any variation.
-		* @param start The size of the sprite upon creation
-		* @param end The size of the sprite upon death.
+		* @param newSizes Array of sizes
 		* @param variation The amount of variation on the starting size (0 being no variation and 1.0 a random size between start and end).
 		**/
-		void setSize(float start, float end, float variation);
+		void setSize(const std::vector<float>& newSizes, float variation = 0.0f);
 
 		/**
 		* Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
 		* Sets the color of the particles.
 		* @param color The color.
 		**/
-		void setColor(unsigned char * color);
+		void setColor(const Color& color);
 		
 		/**
 		* Sets the particles' offsets for rotation.
 		
 		/**
 		* Sets the color of the particles.
-		* @param start The color of the particle when created.
-		* @param end The color of the particle upon death.
+		* @param newColors Array of colors
 		**/
-		void setColor(unsigned char * start, unsigned char * end);
+		void setColor(const std::vector<Color>& newColors);
 
 		/**
 		* Returns the x-coordinate of the emitter's position.

src/modules/graphics/opengl/wrap_ParticleSystem.cpp

 		return 0;
 	}
 
-	int w_ParticleSystem_setSize(lua_State * L)
+	int w_ParticleSystem_setSizes(lua_State * L)
 	{
 		ParticleSystem * t = luax_checkparticlesystem(L, 1);
-		float arg1 = (float)luaL_checknumber(L, 2);
-		float arg2 = (float)luaL_optnumber(L, 3, arg1);
-		float arg3 = (float)luaL_optnumber(L, 4, 0);
-		t->setSize(arg1, arg2, arg3);
+		size_t nSizes = lua_gettop(L) - 1;
+		if (nSizes == 1) {
+			t->setSize(luaL_checknumber(L, 2));
+		} else {
+			std::vector<float> sizes(nSizes);
+			for (size_t i = 0; i < nSizes; ++i)
+				sizes[i] = luaL_checknumber(L, 1 + i + 1);
+
+			t->setSize(sizes);
+		}
 		return 0;
 	}
 
 		return 0;
 	}
 
-	int w_ParticleSystem_setColor(lua_State * L)
+	int w_ParticleSystem_setColors(lua_State * L)
 	{
 		ParticleSystem * t = luax_checkparticlesystem(L, 1);
-		
-		unsigned char start[4];
+		size_t nColors = (lua_gettop(L) - 1) / 4;
 
-		start[0] = (unsigned char)luaL_checkint(L, 2);
-		start[1] = (unsigned char)luaL_checkint(L, 3);
-		start[2] = (unsigned char)luaL_checkint(L, 4);
-		start[3] = (unsigned char)luaL_checkint(L, 5);
-
-		if(lua_gettop(L) > 5)
-		{
-			unsigned char end[4];
-			end[0] = (unsigned char)luaL_checkint(L, 6);
-			end[1] = (unsigned char)luaL_checkint(L, 7);
-			end[2] = (unsigned char)luaL_checkint(L, 8);
-			end[3] = (unsigned char)luaL_checkint(L, 9);
-			t->setColor(start, end);
+		if (nColors == 1) {
+			t->setColor(Color(luaL_checkint(L,2),
+						luaL_checkint(L,3),
+						luaL_checkint(L,4),
+						luaL_checkint(L,5)));
+		} else {
+			std::vector<Color> colors(nColors);
+			for (size_t i = 0; i < nColors; ++i) {
+				colors[i] = Color(luaL_checkint(L, 1 + i*4 + 1),
+						luaL_checkint(L, 1 + i*4 + 2),
+						luaL_checkint(L, 1 + i*4 + 3),
+						luaL_checkint(L, 1 + i*4 + 4));
+			}
+			t->setColor(colors);
 		}
-		else
-			t->setColor(start);
 
 		return 0;
 	}
 		{ "setGravity", w_ParticleSystem_setGravity },
 		{ "setRadialAcceleration", w_ParticleSystem_setRadialAcceleration },
 		{ "setTangentialAcceleration", w_ParticleSystem_setTangentialAcceleration },
-		{ "setSize", w_ParticleSystem_setSize },
+		{ "setSizes", w_ParticleSystem_setSizes },
 		{ "setSizeVariation", w_ParticleSystem_setSizeVariation },
 		{ "setRotation", w_ParticleSystem_setRotation },
 		{ "setSpin", w_ParticleSystem_setSpin },
 		{ "setSpinVariation", w_ParticleSystem_setSpinVariation },
-		{ "setColor", w_ParticleSystem_setColor },
+		{ "setColors", w_ParticleSystem_setColors },
 		{ "setOffset", w_ParticleSystem_setOffset },
 		{ "getX", w_ParticleSystem_getX },
 		{ "getY", w_ParticleSystem_getY },

src/modules/graphics/opengl/wrap_ParticleSystem.h

 	int w_ParticleSystem_setGravity(lua_State * L);
 	int w_ParticleSystem_setRadialAcceleration(lua_State * L);
 	int w_ParticleSystem_setTangentialAcceleration(lua_State * L);
-	int w_ParticleSystem_setSize(lua_State * L);
+	int w_ParticleSystem_setSizes(lua_State * L);
 	int w_ParticleSystem_setSizeVariation(lua_State * L);
 	int w_ParticleSystem_setRotation(lua_State * L);
 	int w_ParticleSystem_setSpin(lua_State * L);
 	int w_ParticleSystem_setSpinVariation(lua_State * L);
-	int w_ParticleSystem_setColor(lua_State * L);
+	int w_ParticleSystem_setColors(lua_State * L);
 	int w_ParticleSystem_setOffset(lua_State * L);
 	int w_ParticleSystem_getX(lua_State * L);
 	int w_ParticleSystem_getY(lua_State * L);